OneDrive SDK UWA "AuthenticationFailure" - authentication

I'm building a W10 Universal app and I would like to know who is logged in to Windows so I can associate their data on my server with something that uniquely identifies the user w/o requiring a separate login.
OneDrive SDK is supposed to make this simple and easy.
So, I registered my app with OneDrive, used nuget to install the packages, downloaded the samples and wrote the following code.....
var scopes = new string[] { "wl.signin", "wl.offline_access", "onedrive.readonly" };
var client = OneDriveClientExtensions.GetUniversalClient(scopes);
try {
await client.AuthenticateAsync();
}
catch {
blahlblahblah;
}
This doesn't throw an exception, but, after AuthenticateAsync executes, the client's IsAuthenticated property is still false and the ServiceInfo's UserId is null.
So, I tried this next:
var client = OneDriveClient.GetMicrosoftAccountClient(
this.Resources["AppID"].ToString(),
this.Resources["ReturnUri"].ToString(),
scopes
);
where the AppID and ReturnUri match the Client ID and Redirect URL that are registered with the app.
This actually throws a OneDrive.Sdk.Error with a message of "Failed to retrieve a valid authentication token for the user."
So, I don't know what I'm doing wrong here. I'm at a total loss. I pulled up Fiddler to see what was being sent back & forth and nothing shows up. There's just not enough information for me to figure this out.
Anyone got any ideas?

So, ginach's workaround for the problem seems to be the solution until the bug is fixed. So, to sum it up....
Don't use the IsAuthenticated property of the UniversalClient. Instead, check the client's AuthenticationProvider's CurrentAccountSession to see if it has a value and an AccessToken.
var client = OneDriveClientExtensions.GetUniversalClient(scopes);
await client.AuthenticateAsync();
if (client.AuthenticationProvider.CurrentAccountSession != null && client.AuthenticationProvider.CurrentAccountSession.AccessToken != null) {
blahblahblahblahblah
}
This seems to do the trick.

Related

Windows authentication fail with "401 Unauthorized"

I have a MVC client accessing a Web API protected by IDS4. They all run on my local machine and hosted by IIS. The app works fine when using local identity for authentication. But when I try to use Windows authentication, I keep getting "401 Unauthorized" error from the dev tool and the login box keeps coming back to the browser.
Here is the Windows Authentication IIS setting
and enabled providers
It's almost like that the user ID or password was wrong, but that's nearly impossible because that's the domain user ID and password I use for logging into the system all the time. Besides, according to my reading, Windows Authentication is supposed to be "automatic", which means I will be authenticated silently without a login box in the first place.
Update
I enabled the IIS request tracing and here is the result from the log:
As you can see from the trace log item #29, the authentication (with the user ID I typed in, "DOM\Jack.Backer") was successful. However, some authorization item (#48) failed after that. And here is the detail of the failed item:
What's interesting is that the ErrorCode says that the operation (whatever it is) completed successfully, but still I received a warning with a HttpStatus=401 and a HttpReason=Unauthorized. Apparently, this is what failed my Windows Authentication. But what is this authorization about and how do I fix it?
In case anyone interested - I finally figured this one out. It is because the code that I downloaded from IndentityServer4's quickstart site in late 2020 doesn't have some of the important pieces needed for Windows authentication. Here is what I had to add to the Challenge function of the ExternalController class
and here is the ProcessWindowsLoginAsync function
private async Task<IActionResult> ProcessWindowsLoginAsync(string returnUrl)
{
var result = await HttpContext.AuthenticateAsync(AccountOptions.WindowsAuthenticationSchemeName);
if (result?.Principal is WindowsPrincipal wp)
{
var props = new AuthenticationProperties()
{
RedirectUri = Url.Action(nameof(Callback)),
Items =
{
{ "returnUrl", returnUrl },
{ "scheme", AccountOptions.WindowsAuthenticationSchemeName },
}
};
var id = new ClaimsIdentity(AccountOptions.WindowsAuthenticationSchemeName);
id.AddClaim(new Claim(JwtClaimTypes.Subject, wp.Identity.Name));
id.AddClaim(new Claim(JwtClaimTypes.Name, wp.Identity.Name));
if (AccountOptions.IncludeWindowsGroups)
{
var wi = wp.Identity as WindowsIdentity;
var groups = wi.Groups.Translate(typeof(NTAccount));
var roles = groups.Select(x => new Claim(JwtClaimTypes.Role, x.Value));
id.AddClaims(roles);
}
await HttpContext.SignInAsync(IdentityConstants.ExternalScheme, new ClaimsPrincipal(id), props);
return Redirect(props.RedirectUri);
}
else
{
return Challenge(AccountOptions.WindowsAuthenticationSchemeName);
}
}
Now my windows authentication works with no issues.

How to sign out previous login on new login in .net core?

How to sign out previous login when user log in through another browser in .net core?
I referred to this link but confused about how to use it.
enter link description here
You simply call UpdateSecurityStampAsync on your UserManager instance with the user in question. Then sign them in. This won't automatically log out other sessions, because there's a client-side component that must come into play. However, on the next request made from another browser, the cookie there will be invalidated because the security stamp won't match, and then the user will be effectively logged out.
It worked for me doing like:
After login done:
var loggedinUser = await _userManager.FindByEmailAsync(model.Email);
if (loggedinUser != null)
{
var Securitystamp = await _userManager.UpdateSecurityStampAsync(loggedinUser);
}
and in StartUp.cs
services.Configure<SecurityStampValidatorOptions>(options => options.ValidationInterval = TimeSpan.FromSeconds(0));

Cannot access UserInfo endpoint in IdentityServer4 doc example from Client

I'm testing out IdentityServer4, going through the documentation in order to learn more about OAuth2, OpenId Connect and Claim-based authentication, all of which I'm new at. However, some of the example code behaves weirdly and I can't figure out why...
So from my understanding, when given permission to access user data, the client can reach out to the UserInfo endpoint, which contains data such as claims, etc.
In IdentityServer4 there's even a setting
GetClaimsFromUserInfoEndpoint
that the documentation recommends we set to true.
So I'm following the IdentityServer4 startup guides and everything works perfectly until a point. This Quickstart contains the example code provided, although I'm assuming that I'm missing something obvious and seeing the code is not required.
Based on the openId Configuration page of the running server, the userinfo endpoint is located at
http://localhost:5000/connect/userinfo and when I try to access it via the browser I'm seeing a navbar which claims I'm logged in, but the body of the page is a signin prompt. Looks weird but I'm assuming that this is because I'm logged in at localhost:5000 (IdentityServer4), but I'm not sending the userId token which I got for the client on localhost:5002.
So I wrote the following code on my client app:
public async Task<IActionResult> GetData()
{
var accessToken = HttpContext.Authentication.GetTokenAsync("access_token").Result;
HttpClient client = new HttpClient();
client.SetBearerToken(accessToken);
var userInfo = await client.GetStringAsync("http://localhost:5000/connect/userinfo");
return Content(userInfo);
}
Here I know that GetTokenAsync("access_token") should work as it's used in other places in the example project by the client app that connect to an API. However, the responce I'm getting is again the layout page of IdentityServer and a log in prompt.
Any idea what my mistake is and how to access the UserInfo endpoint?
Edit: removed thread-blocking so that I don't show strangers shameful test code
Ok, so it turns out that this code should have a simplified version, namely:
UserInfoClient uic = new UserInfoClient("http://localhost:5000", idToken);
var result = await uic.GetAsync();
return Content(JsonConvert.SerializeObject(result.Claims));
Yet, the problem persists, even the encapsulated code inside UserInfoClient hits the brick wall of "no user endpoint data, just the layout for the example website".
It's probably little late to answer, but for anyone who is still stumbling upon this, try this ---
var accessToken = await HttpContext.Authentication.GetTokenAsync("access_token");
var client = new HttpClient();
client.SetBearerToken(accessToken);
var userInfoClient = new UserInfoClient("http://localhost:5000/connect/userinfo");
var response = await userInfoClient.GetAsync(accessToken);
var claims = response.Claims;
You can also get the list of claims on the client app like -
var claims = HttpContext.User.Claims.ToList();
without calling the endpoint.

Google analytics integration in mvc4

try
{
UserCredential credential;
credential = GoogleWebAuthorizationBroker.AuthorizeAsync(
new ClientSecrets { ClientId = ClientID, ClientSecret = ClientSecret },
new[] { AnalyticsService.Scope.AnalyticsReadonly, AnalyticsService.Scope.AnalyticsEdit },
"user",
CancellationToken.None,
new FileDataStore("Analytics.Auth.Store")).Result;
return credential;
}
catch { return null; }
I am using above code for google console web application(Google Analytic) but it gives redirect_uri mismatch error. How i can send redirect_uri.
redirect_uri is set up in the Google Developers console -> apis & auth -> credentials
Not sure if Sanaan C ever found an answer ... the reason that your code does not work in a web application is likely because the user that created the Analytics.Auth.Store entry in that user's %APPDATA% folder is NOT the one running your web application.
Does anyone have a solution to this - and please excuse that this question is appended to another ... I actually think this was the intended question in any event ...
===
One simple-minded solution could be to take the credentials created by a user who can respond to the redirect and put it in a folder, with appropriate access permissions, where the user under which the IIS service is being run can find it. Instantiate the FileDataStore with a full path to this folder ...

ArgumentException: Precondition failed.: !string.IsNullOrEmpty(authorization.RefreshToken) with Service Account for Google Admin SDK Directory access

I'm trying to access the Google Directory using a Service Account. I've fiddled with the DriveService example to get this code:
public static void Main(string[] args)
{
var service = BuildDirectoryService();
var results = service.Orgunits.List(customerID).Execute();
Console.WriteLine("OrgUnits");
foreach (var orgUnit in results.OrganizationUnits)
{
Console.WriteLine(orgUnit.Name);
}
Console.ReadKey();
}
static DirectoryService BuildDirectoryService()
{
X509Certificate2 certificate = new X509Certificate2(SERVICE_ACCOUNT_PKCS12_FILE_PATH, "notasecret",
X509KeyStorageFlags.Exportable);
var provider = new AssertionFlowClient(GoogleAuthenticationServer.Description, certificate)
{
ServiceAccountId = SERVICE_ACCOUNT_EMAIL,
Scope = DirectoryService.Scopes.AdminDirectoryOrgunit.GetStringValue()
};
var auth = new OAuth2Authenticator<AssertionFlowClient>(provider, AssertionFlowClient.GetState);
return new DirectoryService(new BaseClientService.Initializer()
{
Authenticator = auth,
ApplicationName = "TestProject1",
});
}
When I run it, I get
ArgumentException: Precondition failed.: !string.IsNullOrEmpty(authorization.RefreshToken)
I'm going round in circles in the Google documentation. The only stuff I can find about RefreshTokens seems to be for when an individual is authorizing the app and the app may need to work offline. Can anyone help out or point me in the direction of the documentation that will, please.
Service Account authorization actually do not return Refresh Token - so this error makes sense. Do you know where this is coming from?
I am not too familiar with the .NET client library but having the full error trace would help.
As a longshot - The error might be a bad error -
Can you confirm that you've enabled the Admin SDK in the APIs console for this project
Can you confirm that you whitelisted that Client ID for the service account in the domain you are testing with (along with the Admin SDK scopes)
The above code will work if you replace the provider block with:
var provider = new AssertionFlowClient(GoogleAuthenticationServer.Description, certificate)
{
ServiceAccountId = SERVICE_ACCOUNT_EMAIL,
Scope = DirectoryService.Scopes.AdminDirectoryOrgunit.GetStringValue(),
ServiceAccountUser = SERVICE_ACCOUNT_USER //"my.admin.account#my.domain.com"
};
I had seen this in another post and tried it with my standard user account and it didn't work. Then I read something that suggested everything had to be done with an admin account. So, I created a whole new project, using my admin account, including creating a new service account, and authorising it. When I tried it, it worked. So, then I put the old service account details back in but left the admin account in. That worked, too.