LoginAsync sometimes times out - c#

I have some authentication code for an Azure hosted mobile app that is being called from a WPF client. It attempts to authenticate the user as part of the initialisation of a Prism module that contains an authentication service. The async method to authenticate is written as follows:
public async Task AcquireTokenAndAuthenticateWebApiAsync()
{
try
{
//todo these are hardcoded :-( ... need to come from app settings!!
// settings for authentication
string resourceId = "https://windinspectordevmobileappservice.azurewebsites.net";
//string resourceId = "http://localhost:51293/";
string clientId = "5fe3b968-1d23-4667-9c31-86fac4ab4aec";
Uri redirectUri = new Uri("https://windinspectordevmobileappservice.azurewebsites.net/.auth/login/done"); // Page to say you have sucessfully signed in
const string appServiceUrl = "https://windinspectordevmobileappservice.azurewebsites.net";
string authorityUri = "https://login.windows.net/dnv.onmicrosoft.com";
this.authContext = new AuthenticationContext(authorityUri);
// authenticate against the AD
var result = await authContext.AcquireTokenAsync(
resourceId, clientId, redirectUri,
new PlatformParameters(PromptBehavior.Auto, false));
this.authResult = result;
// authenticate against the web api
Client = new MobileServiceClient(appServiceUrl);
JObject payload = new JObject();
payload["access_token"] = authResult.AccessToken;
var user = await Client.LoginAsync(
MobileServiceAuthenticationProvider.WindowsAzureActiveDirectory,
payload).ConfigureAwait(false);
this.authenticatedUserName = this.authResult.UserInfo.DisplayableId;
}
catch (InvalidOperationException e)
{
}
}
About 50% of the time the call to LoginAsync is successful. However, at other times the LoginAsync call simply times out (the task times out with an exception). What could cause this?
I've attempted to turn on the logs in the client, but see no reason for the timeout in the logs:
Application Log from Successful LoginAsync Call:
2017-01-27T16:24:29 PID[23292] Verbose Received request: POST https://windinspectordevmobileappservice.azurewebsites.net/.auth/login/aad
2017-01-27T16:24:29 PID[23292] Verbose JWT validation succeeded. Subject: '__HeuajpWfXmUxZBrDvwAqcV0UOirMVrs5iCwvpnrrY', Issuer: 'https://sts.windows.net/adf10e2b-b6e9-41d6-be2f-c12bb566019c/'.
2017-01-27T16:24:29 PID[23292] Information Login completed for 'max.palmer#dnvgl.com'. Provider: 'aad'.
2017-01-27T16:24:29 PID[23292] Information Sending response: 200.77 OK
2017-01-27 16:24:29 WINDINSPECTORDEVMOBILEAPPSERVICE POST /.auth/login/aad X-ARR-LOG-ID=4f08
Application Log from LoginAsync call that times out:
734a-75e2-4674-a9c8-bd74caa1aa3f 443 - 80.5.95.115 ZUMO/3.1+(lang=Managed;+os=Windows;+os_version=6.2.0.9200;+arch=Win32NT;+version=3.1.50105.0) - - windinspectordevmobileappservice.azurewebsites.net 200 77 0 1089 2739 109
2017-01-27T16:26:29 No new trace in the past 1 min(s).
2017-01-27T16:26:34 PID[23292] Verbose Received request: POST https://windinspectordevmobileappservice.azurewebsites.net/.auth/login/aad
2017-01-27T16:29:29 No new trace in the past 1 min(s).
NB looking at these logs, when the login call is successful, I see "JWT validation succeeded", when it is not, I never see this line and it times out. I've checked many samples online for using this authentication pattern and the code looks good to me. I've also tried shifting the code to be called via an async command Execute method so that I'm async all the way (up).
Any thoughts on what else I can do to try to understand what might be the cause of the timeout in some cases?
One question I had is that the source code for MobileServiceClient is hosted at:
https://github.com/Azure/azure-mobile-services
However, this repository is marked as deprecated (or at least Mobile Services is deprecated in favour of Mobile Apps). I presume this class is still the one to use with Mobile Apps?
Is there any way I can step into the code? Do I need to fetch the source from Git and debug that?

Related

MSAL Error message AADSTS65005 when trying to get token for accessing custom api

I downloaded the example below to get an access token from MS Graph and it worked fine. Now I changed the code to get a token from a custom web API. On apps.dev.microsoft.com I registered a client application and an the API.
Client and server registration in AD
private static async Task<AuthenticationResult> GetToken()
{
const string clientId = "185adc28-7e72-4f07-a052-651755513825";
var clientApp = new PublicClientApplication(clientId);
AuthenticationResult result = null;
string[] scopes = new string[] { "api://f69953b0-2d7f-4523-a8df-01f216b55200/Test" };
try
{
result = await clientApp.AcquireTokenAsync(scopes, "", UIBehavior.SelectAccount, string.Empty);
}
catch (Exception x)
{
if (x.Message == "User canceled authentication")
{
}
return null;
}
return result;
}
When I run the code I login to AD via the dialog en get the following exception in the debugger:
Error: Invalid client Message = "AADSTS65005: The application
'CoreWebAPIAzureADClient' asked for scope 'offline_access' that
doesn't exist on the resource. Contact the app vendor.\r\nTrace ID:
56a4b5ad-8ca1-4c41-b961-c74d84911300\r\nCorrelation ID:
a4350378-b802-4364-8464-c6fdf105cbf1\r...
Error message
Help appreciated trying for days...
For anyone still striking this problem, please read this:
https://www.andrew-best.com/posts/please-sir-can-i-have-some-auth/
You'll feel better after this guy reflects all of your frustrations, except that he works it out...
If using adal.js, for your scope you need to use
const tokenRequest = {
scopes: ["https://management.azure.com/user_impersonation"]
};
I spent a week using
const tokenRequest = {
scopes: ["user_impersonation"]
};
.. since that is the format that the graph API scopes took
As of today, the V2 Endpoint does not support API access other than the Microsoft Graph. See the limitations of the V2 app model here.
Standalone Web APIs
You can use the v2.0 endpoint to build a Web API that is secured with
OAuth 2.0. However, that Web API can receive tokens only from an
application that has the same Application ID. You cannot access a Web
API from a client that has a different Application ID. The client
won't be able to request or obtain permissions to your Web API.
For the specific scenario that you are trying to accomplish, you need to use the V1 App Model (register apps on https://portal.azure.com).
In the very near future, V2 apps will be enabled to call other APIs other than Microsoft Graph, so your scenario will be supported, but that is just not the case today. You should keep an eye out on our documentation for this update.
In your (server) application registration in AAD, you need to specify your scopes in the oauth2Permissions element.
You may already have a user_impersonation scope set. Copy that as a baseline, give it a unique GUID and value, and then AAD will let your client request an access token with your new scope.

Differences between AcquireTokenAsync and LoginAsync in Xamarin Native

TL;DR
What is the difference between authenticating users with AuthenticationContext.AcquireTokenAsync() and MobileServiceClient.LoginAsync() ?
Can I use the token from the first method to authenticate a user in the second?
Long Version
I've been trying to authenticate users via a mobile device (iOS) for a mobile service in Azure with Xamarin Native (not Forms).
There are enough tutorials online to get you started but in the process, I got lost and confused...
What's working at the moment is the following; which has the user enter his credentials in another page and returns a JWT token which (if decoded here1) has the claims listed here2.
Moreover, this token is authorized in controllers with the [Authorize] attribute in requests with an Authorization header and a Bearer token.
Note: the following constants are taken from the registered applications in Active Directory (Native and Web App / API).
public const string Authority = #"https://login.windows.net/******.com";
public const string GraphResource = #"https://*******.azurewebsites.net/********";
public const string ClientId = "046b****-****-****-****-********0290";
public const string Resource = #"https://******.azurewebsites.net/.auth/login/done";
var authContext = new AuthenticationContext(Authority);
if (authContext.TokenCache.ReadItems().Any(c => c.Authority == Authority))
{
authContext = new AuthenticationContext(authContext.TokenCache.ReadItems().First().Authority);
}
var uri = new Uri(Resource);
var platformParams = new PlatformParameters(UIApplication.SharedApplication.KeyWindow.RootViewController);
AuthenticationResult authResult = await authContext.AcquireTokenAsync(GraphResource, ClientId, uri, platformParams);
Another working authentication flow I tried is the following; which does the same with the difference that it informs the user that the app requires permissions to access some resources.
If allowed, a JWT token (with less characters than the previous one) is returned with less payload data. This token though, won't pass the authorization attribute just like the previous one.
public const string AadResource = #"https://******.azurewebsites.net/.auth/aad";
var client = new MobileServiceClient(AadResource);
var rootView = UIApplication.SharedApplication.KeyWindow.RootViewController;
MobileServiceUser user = await client.LoginAsync(rootView, "aad");
Obviously, the return type is different, but, what is the main difference between these two authentication methods?
Additionally, another headache comes from trying to achieve this3 at the very end of the article. I already have the token from the first aforementioned method but when I try to follow the client flow with the token in client.LoginAsync() the following error is returned:
The resource you are looking for has been removed, had its name changed, or is temporarily unavailable.
Link References:
https://jwt.io/
https://learn.microsoft.com/en-us/azure/active-directory/develop/active-directory-token-and-claims
https://adrianhall.github.io/develop-mobile-apps-with-csharp-and-azure/chapter2/enterprise/
https://www.reddit.com/r/xamarindevelopers/comments/6dw928/differences_between_acquiretokenasync/
Edit (30 May 2017)
The Why are they different? has been answered on this4 reddit post by the same person (pdx mobilist / saltydogdev) and the simple answer is claims.
Yes. You can insert a token into the MobileServicesClient and then use it had been authenticated directly. That's the beauty of bearer tokens.
Just set the MobileServiceClient CurrentUser:
MobileServiceclient Client;
...
Client.CurrentUser = new MobileServiceUser(username)
{ MobileServiceAuthenticationToken = authtoken};
Edit:
The reason they are different is because each library is requesting a different set of claims. The reason they still work is that the basic information for authenticating/validating the token is there. I'm not sure what are the specific required claims. At a minimum it would be the user id AND that the signature is valid. They are doing the same basic thing, MobileServiceClient just requests less claims.
I believe that the MobileServicesClient can authenticate against Azure AD, if you set up the mobile service correctly. So you should be able to just use the MobileServiceClient.
Here is the document that describes how this works: https://learn.microsoft.com/en-us/azure/app-service-mobile/app-service-mobile-how-to-configure-active-directory-authentication

AuthenticationManager.GetExternalLoginInfoAsync() return null on Facebook [duplicate]

Update 2017!
The issue I had when I posted the original question has got nothing to do with the recent changes Facebook made when they forced everyone to version 2.3 of their API. For a solution to that specific problem, see sammy34's answer below. Version 2.3 of the /oauth/access_token endpoint now returns JSON instead of form-encoded values
For historical reasons, here's my original question/issue:
I've got an MVC5 Web application which is using the built-in support for authentication via Facebook and Google. When we built this app a few months ago, we followed this tutorial: http://www.asp.net/mvc/tutorials/mvc-5/create-an-aspnet-mvc-5-app-with-facebook-and-google-oauth2-and-openid-sign-on and everything worked great.
Now, all of a sudden, the Facebook authentication has just stopped working alltogether. The Google authentication still works great.
Description of the problem: We click the link to connect using Facebook, we are redirected to Facebook where we are prompted if we wan't to allow our Facebook app access to our profile. When we click "OK" we are redirected back to our site, but instead of being logged in we simply end up at the login screen.
I've gone through this process in debug mode and I've got this ActionResult in my account controller as per the tutorial mentioned above:
// GET: /Account/ExternalLoginCallback
[AllowAnonymous]
public async Task<ActionResult> ExternalLoginCallback(string returnUrl)
{
var loginInfo = await AuthenticationManager.GetExternalLoginInfoAsync();
if (loginInfo == null)
{
return RedirectToAction("Login");
}
............
When stepping through the code and upon returning from Facebook, the loginInfo object is always NULL, which causes the user to be redirected back to the login.
In order to understand what is actually happening behind the scenes, I installed Fiddler and monitored the HTTP traffic. What I disovered is that upon clicking "OK" at the Facebook permission dialog, Facebook redirects back to our application with this URL:
https://localhost/signin-facebook?code=<access-token>
This URL is not an actual file and probably handled by some controller/handler built into this OWIN framework I'm guessing. Most likely, it is connecting back to Facebook using the given code to query information about the user which is trying to login. Now, the problem is that instead of doing that, we are redirected to:
/Account/ExternalLoginCallback?error=access_denied
Which I'm sure is something Facebook is doing, that is, instead of giving us the user data, it's redirecting us back with this error message.
This causes the AuthenticationManager.GetExternalLoginInfoAsync(); to fail and always return NULL.
I'm completely out of ideas. As far as we know, we did not change anything on our end.
I've tried creating a new Facebook app, I've tried following the tutorial again but I always have the same problem.
Any ideas welcome!
Update!
OK, this is driving me insane! I've now manually gone through the steps required to perform the authentication and everything works great when I do that. Why on earth is this not working when using the MVC5 Owin stuff?
This is what I did:
// Step 1 - Pasted this into a browser, this returns a code
https://www.facebook.com/dialog/oauth?response_type=code&client_id=619359858118523&redirect_uri=https%3A%2F%2Flocalhost%2Fsignin-facebook&scope=&state=u9R1m4iRI6Td4yACEgO99ETQw9NAos06bZWilJxJrXRn1rh4KEQhfuEVAq52UPnUif-lEHgayyWrsrdlW6t3ghLD8iFGX5S2iUBHotyTqCCQ9lx2Nl091pHPIw1N0JV23sc4wYfOs2YU5smyw9MGhcEuinvTAEql2QhBowR62FfU6PY4lA6m8pD3odI5MwBYOMor3eMLu2qnpEk0GekbtTVWgQnKnH6t1UcC6KcNXYY
I was redirected back to localhost (which I had shut down at this point to avoid being redirected immediately away). The URL I was redirected to is this:
https://localhost/signin-facebook?code=<code-received-removed-for-obvious-reasons>
Now, I grabbed the code I got and used it in the URL below:
// Step 2 - opened this URL in a browser, and successfully retrieved an access token
https://graph.facebook.com/oauth/access_token?client_id=619359858118523&redirect_uri=https://localhost/signin-facebook&client_secret=<client-secret>&code=<code-from-step-1>
// Step 3 - Now I'm able to query the facebook graph using the access token from step 2!
https://graph.facebook.com/me?access_token=<access-token-from-step-2>
No errors, everything works great! Then why the hell is this not working when using the MVC5 Owin stuff? There's obviously something wrong with the OWin implementation.
Update 22nd April 2017: Version 3.1.0 of the Microsoft.Owin.* packages are now available. If you're having problems after Facebook's API changes from the 27th March 2017, try the updated NuGet packages first. In my case they solved the problem (working fine on our production systems).
Original answer:
In my case, I woke up on the 28th March 2017 to discover that our app's Facebook authentication had suddenly stopped working. We hadn't changed anything in the app code.
It turns out that Facebook did a "force upgrade" of their graph API from version 2.2 to 2.3 on 27th March 2017. One of the differences in these versions of the API seems to be that the Facebook endpoint /oauth/access_token responds no longer with a form-encoded content body, but with JSON instead.
Now, in the Owin middleware, we find the method protected override FacebookAuthenticationHandler.AuthenticateCoreAsync(), which parses the body of the response as a form and subsequently uses the access_token from the parsed form. Needless to say, the parsed form is empty, so the access_token is also empty, causing an access_denied error further down the chain.
To fix this quickly, we created a wrapper class for the Facebook Oauth response
public class FacebookOauthResponse
{
public string access_token { get; set; }
public string token_type { get; set; }
public int expires_in { get; set; }
}
Then, in OwinStart, we added a custom back-channel handler...
app.UseFacebookAuthentication(new FacebookAuthenticationOptions
{
AppId = "hidden",
AppSecret = "hidden",
BackchannelHttpHandler = new FacebookBackChannelHandler()
});
...where the handler is defined as:
public class FacebookBackChannelHandler : HttpClientHandler
{
protected override async System.Threading.Tasks.Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, System.Threading.CancellationToken cancellationToken)
{
var result = await base.SendAsync(request, cancellationToken);
if (!request.RequestUri.AbsolutePath.Contains("access_token"))
return result;
// For the access token we need to now deal with the fact that the response is now in JSON format, not form values. Owin looks for form values.
var content = await result.Content.ReadAsStringAsync();
var facebookOauthResponse = JsonConvert.DeserializeObject<FacebookOauthResponse>(content);
var outgoingQueryString = HttpUtility.ParseQueryString(string.Empty);
outgoingQueryString.Add(nameof(facebookOauthResponse.access_token), facebookOauthResponse.access_token);
outgoingQueryString.Add(nameof(facebookOauthResponse.expires_in), facebookOauthResponse.expires_in + string.Empty);
outgoingQueryString.Add(nameof(facebookOauthResponse.token_type), facebookOauthResponse.token_type);
var postdata = outgoingQueryString.ToString();
var modifiedResult = new HttpResponseMessage(HttpStatusCode.OK)
{
Content = new StringContent(postdata)
};
return modifiedResult;
}
}
Basically, the handler simply creates a new HttpResponseMessage containing the equivalent form-encoded information from the Facebook JSON response. Note that this code uses the popular Json.Net package.
With this custom handler, the problems seem to be resolved (although we're yet to deploy to prod :)).
Hope that saves somebody else waking up today with similar problems!
Also, if anybody has a cleaner solution to this, I'd love to know!
Noticed this problem yesterday. Facebook does not support Microsoft.Owin.Security.Facebook version 3.0.1 anymore. For me it worked to install version 3.1.0. To update to 3.1.0, run the command Install-Package Microsoft.Owin.Security.Facebook in Package Manager Console: https://www.nuget.org/packages/Microsoft.Owin.Security.Facebook
Ok I've got a solution to the problem.
This is the code I had previously in my Startup.Auth.cs file:
var x = new FacebookAuthenticationOptions();
//x.Scope.Add("email");
x.AppId = "1442725269277224";
x.AppSecret = "<secret>";
x.Provider = new FacebookAuthenticationProvider()
{
OnAuthenticated = async context =>
{
//Get the access token from FB and store it in the database and
//use FacebookC# SDK to get more information about the user
context.Identity.AddClaim(new System.Security.Claims.Claim("FacebookAccessToken",context.AccessToken));
context.Identity.AddClaim(new System.Security.Claims.Claim("urn:facebook:name", context.Name));
context.Identity.AddClaim(new System.Security.Claims.Claim("urn:facebook:email", context.Email));
}
};
x.SignInAsAuthenticationType = DefaultAuthenticationTypes.ExternalCookie;
app.UseFacebookAuthentication(x);
Notice how the
x.Scope.Add("email")
line has been commented out, but still I'm query-ing for the e-mail later in the OnAuthenticated handler? Yup, that's right. For some reason this worked flawlessly for a few weeks.
My solution was to simply uncomment the x.Scope.Add("email"); line to make sure that the scope=email variable was present in the initial request to Facebook.
Now everything works like it did!
I cannot understand why this worked before like it was. The only explanation I can come up with is that Facebook changed something on their end.
I had this same issue with the Google Authentication. The following worked for me: Changes to Google OAuth 2.0 and updates in Google middleware for 3.0.0 RC release
The last Facebook upgrade was on 2015-02-09 (https://www.nuget.org/packages/Microsoft.AspNet.WebPages.OAuth/)
The latest version of the API at that point was version 2.2. Version 2.2 expired on the 25th of March 2017, which is coincidentally when the problem started. (https://developers.facebook.com/docs/apps/changelog)
I'm guessing Facebook probably automatically upgraded the API and now the MS OAUTH library is unable to parse the new response.
tldr: The Microsoft WebPages OAuth library is outdated (for FB at least) and you'll probably have to find another solution
The above solutions didn't work for me. In the end, it seemed to be related to the Session. By "waking up" the session in the previous call, it would no longer return null from the GetExternalLoginInfoAsync()
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public ActionResult ExternalLogin(string provider, string returnUrl)
{
Session["WAKEUP"] = "NOW!";
// Request a redirect to the external login provider
return new ChallengeResult(provider, Url.Action("ExternalLoginCallback", "Account", new { ReturnUrl = returnUrl }));
}
Like the OP, I had the 3rd party auth working fine for a long time then suddenly it stopped. I beleive it was due to the changes made in my code when I set up the Session to use Redis Cache on Azure.
I had this problem as well, but it wasn't caused by the scope setting. Took me a long time to figure that out, but what finally clued me in was by setting a custom logger by setting the following in OwinStartup.Configuration(IAppBuilder app).
app.SetLoggerFactory(new LoggerFactory());
// Note: LoggerFactory is my own custom ILoggerFactory
This outputted the following:
2014-05-31 21:14:48,508 [8] ERROR
Microsoft.Owin.Security.Cookies.CookieAuthenticationMiddleware
[(null)] - 0x00000000 - Authentication failed
System.Net.Http.HttpRequestException: An error occurred while sending
the request. ---> System.Net.WebException: The remote name could not
be resolved: 'graph.facebook.com' at
System.Net.HttpWebRequest.EndGetResponse(IAsyncResult asyncResult)
at System.Net.Http.HttpClientHandler.GetResponseCallback(IAsyncResult
ar) --- End of inner exception stack trace --- at
System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task
task) at
System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task
task) at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()
at
Microsoft.Owin.Security.Facebook.FacebookAuthenticationHandler.d__0.MoveNext()
Based on the above call stack I found that my Azure VM was unable to resolve graph.facebook.com. All I had to do to fix that was to run "ipconfig /registerdns" and I was all fixed...
I have been working on solution for three days. And I've just found it on github(https://github.com/aspnet/AspNetKatana/issues/38#issuecomment-290400987)
var facebookOptions = new FacebookAuthenticationOptions()
{
AppId = "xxxxx",
AppSecret = "xxxxx",
};
// Set requested scope
facebookOptions.Scope.Add("email");
facebookOptions.Scope.Add("public_profile");
// Set requested fields
facebookOptions.Fields.Add("email");
facebookOptions.Fields.Add("first_name");
facebookOptions.Fields.Add("last_name");
facebookOptions.Provider = new FacebookAuthenticationProvider()
{
OnAuthenticated = (context) =>
{
// Attach the access token if you need it later on for calls on behalf of the user
context.Identity.AddClaim(new System.Security.Claims.Claim("FacebookAccessToken", context.AccessToken));
foreach (var claim in context.User)
{
//var claimType = string.Format("urn:facebook:{0}", claim.Key);
var claimType = string.Format("{0}", claim.Key);
string claimValue = claim.Value.ToString();
if (!context.Identity.HasClaim(claimType, claimValue))
context.Identity.AddClaim(new System.Security.Claims.Claim(claimType, claimValue, "XmlSchemaString", "Facebook"));
}
return Task.FromResult(0);
}
};
app.UseFacebookAuthentication(facebookOptions);
And to get values
var info = await AuthenticationManager.GetExternalLoginInfoAsync();
if (info != null)
{
var firstName = info.ExternalIdentity.Claims.First(c => c.Type == "first_name").Value;
var lastName = info.ExternalIdentity.Claims.First(c => c.Type == "last_name").Value;
}
Check you get an outside internet connection from your application. If not, fix your outside internet connection. My problem was I was using an EC2 AWS instance that suddenly stopped connecting to the internet. It took me a while to realize that was the problem.
This drove me insane. All was working until I deployed to my staging environment. I was using Microsoft.Owin.Security.Facebook version 3.0.1 from Nuget. Updated it to the prelease version 3.1.0 from Nuget and I no longer got the access denied error...
Even though i did everything what sammy34 said, it did not work for me. I was at the same point with HaukurHaf: When i make apirequest manually on browser it works perfect, but if i use my mvc app, GetExternalLoginInfoAsync() always returns null.
So i changed some rows on sammy34's codes like on this comment: https://stackoverflow.com/a/43148543/7776015
Replaced:
if (!request.RequestUri.AbsolutePath.Contains("/oauth"))
{
request.RequestUri = new Uri(request.RequestUri.AbsoluteUri.Replace("?access_token", "&access_token"));
}
var result = await base.SendAsync(request, cancellationToken);
if (!request.RequestUri.AbsolutePath.Contains("/oauth"))
{
return result;
}
Instead of:
var result = await base.SendAsync(request, cancellationToken);
if (!request.RequestUri.AbsolutePath.Contains("access_token"))
return result;
And added this row into my FacebookAuthenticationOptions:
UserInformationEndpoint = "https://graph.facebook.com/v2.8/me?fields=id,name,email,first_name,last_name,picture"
and now it works.(fields and that parameters optional)
Note: I did not update Microsoft.Owin.Security.Facebook

Persistent authentication across UWP app and Azure Mobile Service

Building on the example here I'm attempting to authenticate an MSA login on the client, and have it authenticate service-side as well. The difference with mine is I'm using the new WebAccount-related API's in Windows 10 instead of the now deprecated Live SDK.
So far I've got:
var provider = await WebAuthenticationCoreManager.FindAccountProviderAsync("https://login.microsoft.com", "consumers");
var request = new WebTokenRequest(provider, "service::wl.basic wl.emails::DELEGATION", "none");
var result = await WebAuthenticationCoreManager.RequestTokenAsync(request);
if (result.ResponseStatus == WebTokenRequestStatus.Success)
{
string token = result.ResponseData[0].Token;
//This calls my custom wrappers around the Live REST API v5 and runs successfully with this token
var acc = await LiveApi.GetLiveAccount(token);
var jtoken = new JObject
{
{"authenticationToken", token}
};
try
{
//Shouldn't this work? but raises a 401
await App.MobileService.LoginAsync(MobileServiceAuthenticationProvider.MicrosoftAccount, jtoken);
//Alternate method? Also raises a 401
//await App.MobileService.LoginWithMicrosoftAccountAsync(token);
}
}
As I mentioned in the comments, all I get are 401s.
As far as I can tell the application is configured correctly in Microsoft Account dev center:
I'm using the client ID and secret from the same app in the Azure portal.
JWT issuing is not restricted.
Redirect URL is of the format https://{appname}.azurewebsites.net/.auth/login/microsoftaccount/callback
Authentication works fine when I switch to use purely server-side authentication. i.e.
await App.MobileService.LoginAsync(MobileServiceAuthenticationProvider.MicrosoftAccount);
Any ideas? Am I missing something? Any help would be appreciated.
UPDATED:
The token I get back in the WebTokenRequestResult is 877 characters long and does not appear to be in the JWT format, with the dot (.) separators and I'm quite certain that this is the issue. The following error gets logged in service when the client calls the code above:
JWT validation failed: IDX10708: 'System.IdentityModel.Tokens.JwtSecurityTokenHandler' cannot read this string: 'EwCQAq1DBAAUGCCXc8wU/zFu9QnLdZXy+...Zz9TbuxCowNxsEPPOvXwE='.
Application: The string needs to be in compact JSON format, which is of the form: '<Base64UrlEncodedHeader>.<Base64UrlEndcodedPayload>.<OPTIONAL, Base64UrlEncodedSignature>'..
Application: 2015-12-07T17:47:09 PID[5740] Information Sending response: 401.71 Unauthorized
What format is the token currently in? Can it be transformed to a JWT?
Still no closer to a solution, so any help is appreciated.
Anyone feel free to correct me, but it looks like RequestTokenAsync gets you an access token which you can't use to login the backend. You need an authentication token for that, and as far as I can see RequestTokenAsync doesn't get you that.
There's some info here about the tokens.
If people end up here searching for a solution for App Service Mobile, the update to MobileService. Then there is now a solution
The code replicated here is:
async Task<string> GetDataAsync()
{
try
{
return await App.MobileService.InvokeApiAsync<string>("values");
}
catch (MobileServiceInvalidOperationException e)
{
if (e.Response.StatusCode != HttpStatusCode.Unauthorized)
{
throw;
}
}
// Calling /.auth/refresh will update the tokens in the token store
// and will also return a new mobile authentication token.
JObject refreshJson = (JObject)await App.MobileService.InvokeApiAsync(
"/.auth/refresh",
HttpMethod.Get,
null);
string newToken = refreshJson["authenticationToken"].Value<string>();
App.MobileService.CurrentUser.MobileServiceAuthenticationToken
= newToken;
return await App.MobileService.InvokeApiAsync<string>("values");
}
Hope it saves somebody time !

Facebook C# SDK OAuth Exception "ClientID required"

This question is, I think, similar to my previous one.
Using the latest C# Facebook SDK on .NET 4 I get an Exception with the message "ClientID required" with the following code on the last line:
var app = new DefaultFacebookApplication();
app.AppId = "appId";
app.AppSecret = "secret";
var fb = new FacebookWebContext(app);
fb.IsAuthenticated();
App ID and secret are properly set. The stack trace of the exception is the following:
System.Exception occurred
Message=ClientID required. Source=Facebook StackTrace:
at Facebook.FacebookOAuthClient.BuildExchangeCodeForAccessTokenParameters(IDictionary`2 parameters, String& name, String& path)
at Facebook.FacebookOAuthClient.ExchangeCodeForAccessToken(String code, IDictionary`2 parameters)
at Facebook.FacebookSession.get_AccessToken()
at Facebook.FacebookSession.get_Expires()
at Facebook.Web.FacebookWebContext.IsAuthenticated()
at Piedone.FacebookTest.Authorize() InnerException:
On the client side I'm using the JS SDK, initialized as following:
FB.init({
appId: appId,
status: true, // check login status
cookie: true, // enable cookies to allow the server to access the session
xfbml: true, // parse XFBML
oauth: true // enable OAuth 2.0
});
The users gets properly logged in with the JS login() method, as the alert in the following piece of code runs:
FB.login(function (response) {
if (response.authResponse) {
alert("logged in");
} else {
alert('User cancelled login or did not fully authorize.');
}
}, { scope: scope });
In the app settings on Facebook both the "Forces use of login secret for OAuth call and for auth.login" and "Encrypted Access Token" are turned on. As far as I know all this should enable the use of the OAuth 2 authentication.
Anybody has an idea what am I doing wrong? There really can't be any error in these few lines of code...
Thanks in advance for any help!
Edit:
The AccessToken property of FacebookWebContext throws the same error and HttpContext.CurrentNotification does:
CurrentNotification '(_facebookWebContextCache.HttpContext).CurrentNotification' threw an exception of type 'System.PlatformNotSupportedException' System.Web.RequestNotification {System.PlatformNotSupportedException}
This operation requires IIS integrated pipeline mode.
Since I must run the program from Visual Studio with its Development Server (as I'm currently developing the application) there is no way anything can be done about the latter exception, I suppose. Actually I also tried with Webmatrix's IIS express, but the problem persists.
It's also interesting, that in the FacebookWebContext the settings (app id, secret) are correctly set as well, the user Id and the signed request is also there...
Edit 2:
I also get the same error when using the SDK source. It looks that AccessToken and in the Session the Expires property throw the exception. I don't know if this is connected to the httpcontext issue above.
One more solution is add facebook settings to you web or app congfig
<facebookSettings appId="appid" appSecret="secret" />
after that create Auth class
var oauth = new FacebookOAuthClient(FacebookApplication.Current);
And it wil work as well
Finally I managed to solve the problem, but most likely this is a bug in the SDK.
The cause
The problem is that the FacebookApplication.Current is empty, as it does not get populated with data set in the FacebookWebContext ctor. This leads to the problem of the access token: in FacebookSession.AccessToken on line 119 FacebookOAuthClient is instantiated with FacebookApplication.Current, that of course is practically empty. So FacebookOAuthClient is throwing the exception as it doesn't get the application settings.
The solution
The workaround is to simply explicitly set the current FacebookApplication together with the instantiation of FacebookWebContext:
var app = new DefaultFacebookApplication();
app.AppId = "appId";
app.AppSecret = "secret";
var fb = new FacebookWebContext(app);
FacebookApplication.SetApplication(app); // Note this is the new line

Categories