Ok so it is time to enable ASP.NET Web API 2 external logins such as Facebook & Google then consume this in our AngularJS application. In this post we’ll add support to login using Facebook and Google+ external providers, then we’ll associate those authenticated social accounts with local accounts.
Once we complete the implementation in this post we’ll have an AngularJS application that uses OAuth bearer tokens to access any secured back-end API end point. This application will allow users to login using the resource owner password flow (Username/Password) along with refresh tokens, and support for using external login providers. So to follow along with this post I highly recommend to read previous parts:
- Token Based Authentication using ASP.NET Web API 2, Owin, and Identity – Part 1.
- AngularJS Token Authentication using ASP.NET Web API 2, Owin, and Identity – Part 2.
- Enable OAuth Refresh Tokens in AngularJS App using ASP .NET Web API 2, and Owin – Part 3.
- Decouple OWIN Authorization Server from Resource Server – Part 5.
You can check the demo application, play with the back-end API for learning purposes (http://ngauthenticationapi.azurewebsites.net), and check the source code on Github.
There is a great walkthrough by Rick Anderson which shows how you can enable end users to login using social providers, the walkthrough uses VS 2013 MVC 5 template with individual accounts, to be honest it is quite easy and straight forward to implement this if you want to use this template, but in my case and if you’r following along with previous parts, you will notice that I’ve built the Web API from scratch and I added the needed middle-wares manually.
The MVC 5 template with individual accounts is quite complex, there are middle-wares for cookies, external cookies, bearer tokens… so it needs time to digest how this template work and maybe you do not need all this features in your API. There are two good posts about describing how external logins implemented in this template by Brock Allen and Dominick Baier.
So what I tried to do at the beginning is to add the features and middle-wares needed to support external logins to my back-end API (I need to use the minimal possible code to implement this), everything went good but the final step where I should receive something called external bearer token is not happening, I ended up with the same scenario in this SO question. It will be great if someone will be able to fork my repo and try to find a solution for this issue without adding Nuget packages for ASP.NET MVC and end up with all the binaries used in VS 2013 MVC 5 template.
I didn’t want to stop there, so I decided to do custom implementation for external providers with little help from the MVC 5 template, this implementation follows different flow other than the one in MVC 5 template; so I’m open for suggestions, enhancements, best practices on what we can add to the implementation I’ll describe below.
Sequence of events which will take place during external login:
- AngularJS application sends HTTP GET request to anonymous end point (/ExternalLogin) defined in our back-end API by specifying client_id, redirect_uri, response_type, the GET request will look as this: http://ngauthenticationapi.azurewebsites.net/api/Account/ExternalLogin?provider=Google&response_type=token&client_id=ngAuthApp& redirect_uri=http://ngauthenticationweb.azurewebsites.net/authcomplete.html (more on redircet_uri value later on post)
- Once the end point receives the GET request, it will check if the user is authenticated, and let we assume he is not authenticated, so it will notify the middleware responsible for the requested external provider to take the responsibility for this call, in our case it is Google.
- The consent screen for Google will be shown, and the user will provide his Google credentials to authenticate.
- Google will callback our back-end API and Google will set an external cookie containing the outcome of the authentication from Google (contains all the claims from the external provider for the user).
- Google middleware will be listing for an event named “Authenticated” where we’ll have the chance to read all external claims set by Google. In our case we’ll be interested in reading the claim named “AccessToken” which represents a Google Access Token, where the issuer for this claim is not LOCAL AUTHORITY, so we can’t use this access token directly to authorize calls to our secure back-end API endpoints.
- Then we’ll set the external provider external access token as custom claim named “ExternalAccessToken” and Google middleware will redirect back the end point (/ExternalLogin).
- Now the user is authenticated using the external cookie so we need to check that the client_id and redirect_uri set in the initial request are valid and this client is configured to redirect for the specified URI (more on this later).
- Now the code checks if the external user_id along with the provider is already registered as local database account (with no password), in both cases the code will issue 302 redirect to the specified URI in the redirect_uri parameter, this URI will contain the following (“External Access Token”, “Has Local Account”, “Provider”, “External User Name”) as URL hash fragment not a query string.
- Once the AngularJS application receives the response, it will decide based on it if the user has local database account or not, based on this it will issue a request to one of the end points (/RegisterExternal or /ObtainLocalAccessToken). Both end points accept the external access token which will be used for verification and then using it to obtain local access token issued by LOCAL AUTHORITY. This local access token could be used to access our back-end API secured end points (more on external token verification and the two end points later in post).
Sounds complicated, right? Hopefully I’ll be able to simplify describing this in the steps below, so lets jump to the implementation:
Step 1: Add new methods to repository class
The methods I’ll add now will add support for creating the user without password as well finding and linking external social user account with local database account, they are self explanatory methods and there is nothing special about them, so open file “AuthRepository” and paste the code below:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
public async Task<IdentityUser> FindAsync(UserLoginInfo loginInfo) { IdentityUser user = await _userManager.FindAsync(loginInfo); return user; } public async Task<IdentityResult> CreateAsync(IdentityUser user) { var result = await _userManager.CreateAsync(user); return result; } public async Task<IdentityResult> AddLoginAsync(string userId, UserLoginInfo login) { var result = await _userManager.AddLoginAsync(userId, login); return result; } |
Step 2: Install the needed external social providers Nuget packages
In our case we need to add Google and Facebook external providers, so open Nuget package manger console and install the 2 packages below:
1 2 |
Install-Package Microsoft.Owin.Security.Facebook -Version 2.1.0 Install-Package Microsoft.Owin.Security.Google -Version 2.1.0 |
Step 3: Create Google and Facebook Application
I won’t go into details about this step, we need to create two applications one for Google and another for Facebook. I’ve followed exactly the steps used to create apps mentioned here so you can do this. We need to obtain AppId and Secret for both social providers and will use them later, below are the settings for my two apps:
Step 4: Add the challenge result
Now add new folder named “Results” then add new class named “ChallengeResult” which derives from “IHttpActionResult” then paste the code below:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
public class ChallengeResult : IHttpActionResult { public string LoginProvider { get; set; } public HttpRequestMessage Request { get; set; } public ChallengeResult(string loginProvider, ApiController controller) { LoginProvider = loginProvider; Request = controller.Request; } public Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken) { Request.GetOwinContext().Authentication.Challenge(LoginProvider); HttpResponseMessage response = new HttpResponseMessage(HttpStatusCode.Unauthorized); response.RequestMessage = Request; return Task.FromResult(response); } } |
The code we’ve just implemented above will responsible to call the Challenge passing the name of the external provider we’re using (Google, Facebook, etc..), this challenge won’t fire unless our API sets the HTTP status code to 401 (Unauthorized), once it is done the external provider middleware will communicate with the external provider system and the external provider system will display an authentication page for the end user (UI page by Facebook or Google where you’ll enter username/password for authentication).
Step 5: Add Google and Facebook authentication providers
Now we want to add two new authentication providers classes where we’ll be overriding the “Authenticated” method so we’ve the chance to read the external claims set by the external provider, those set of external claims will contain information about the authenticated user and we’re interested in the claim named “AccessToken”.
As I’mentioned earlier this token is for the external provider and issued by Google or Facebook, after we’ve received it we need to assign it to custom claim named “ExternalAccessToken” so it will be available in the request context for later use.
So add two new classes named “GoogleAuthProvider” and “FacebookAuthProvider” to folder “Providers” and paste the code below to the corresponding class:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
public class GoogleAuthProvider : IGoogleOAuth2AuthenticationProvider { public void ApplyRedirect(GoogleOAuth2ApplyRedirectContext context) { context.Response.Redirect(context.RedirectUri); } public Task Authenticated(GoogleOAuth2AuthenticatedContext context) { context.Identity.AddClaim(new Claim("ExternalAccessToken", context.AccessToken)); return Task.FromResult<object>(null); } public Task ReturnEndpoint(GoogleOAuth2ReturnEndpointContext context) { return Task.FromResult<object>(null); } } |
1 2 3 4 5 6 7 8 |
public class FacebookAuthProvider : FacebookAuthenticationProvider { public override Task Authenticated(FacebookAuthenticatedContext context) { context.Identity.AddClaim(new Claim("ExternalAccessToken", context.AccessToken)); return Task.FromResult<object>(null); } } |
Step 6: Add social providers middleware to Web API pipeline
Now we need to introduce some changes to “Startup” class, the changes as the below:
– Add three static properties as the code below:
1 2 3 4 5 6 7 8 |
public class Startup { public static OAuthBearerAuthenticationOptions OAuthBearerOptions { get; private set; } public static GoogleOAuth2AuthenticationOptions googleAuthOptions { get; private set; } public static FacebookAuthenticationOptions facebookAuthOptions { get; private set; } //More code here... { |
– Add support for using external cookies, this is important so external social providers will be able to set external claims, as well initialize the “OAuthBearerOptions” property as the code below:
1 2 3 4 5 6 7 8 |
public void ConfigureOAuth(IAppBuilder app) { //use a cookie to temporarily store information about a user logging in with a third party login provider app.UseExternalSignInCookie(Microsoft.AspNet.Identity.DefaultAuthenticationTypes.ExternalCookie); OAuthBearerOptions = new OAuthBearerAuthenticationOptions(); //More code here..... } |
– Lastly we need to wire up Google and Facebook social providers middleware to our Owin server pipeline and set the AppId/Secret for each provider, the values are removed so replace them with your app values obtained from step 3:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
//Configure Google External Login googleAuthOptions = new GoogleOAuth2AuthenticationOptions() { ClientId = "xxx", ClientSecret = "xxx", Provider = new GoogleAuthProvider() }; app.UseGoogleAuthentication(googleAuthOptions); //Configure Facebook External Login facebookAuthOptions = new FacebookAuthenticationOptions() { AppId = "xxx", AppSecret = "xxx", Provider = new FacebookAuthProvider() }; app.UseFacebookAuthentication(facebookAuthOptions); |
Step 7: Add “ExternalLogin” endpoint
As we stated earlier, this end point will accept the GET requests originated from our AngularJS app, so it will accept GET request on the form: http://ngauthenticationapi.azurewebsites.net/api/Account/ExternalLogin?provider=Google&response_type=token&client_id=ngAuthApp& redirect_uri=http://ngauthenticationweb.azurewebsites.net/authcomplete.html
So Open class “AccountController” and paste the code below:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 |
private IAuthenticationManager Authentication { get { return Request.GetOwinContext().Authentication; } } // GET api/Account/ExternalLogin [OverrideAuthentication] [HostAuthentication(DefaultAuthenticationTypes.ExternalCookie)] [AllowAnonymous] [Route("ExternalLogin", Name = "ExternalLogin")] public async Task<IHttpActionResult> GetExternalLogin(string provider, string error = null) { string redirectUri = string.Empty; if (error != null) { return BadRequest(Uri.EscapeDataString(error)); } if (!User.Identity.IsAuthenticated) { return new ChallengeResult(provider, this); } var redirectUriValidationResult = ValidateClientAndRedirectUri(this.Request, ref redirectUri); if (!string.IsNullOrWhiteSpace(redirectUriValidationResult)) { return BadRequest(redirectUriValidationResult); } ExternalLoginData externalLogin = ExternalLoginData.FromIdentity(User.Identity as ClaimsIdentity); if (externalLogin == null) { return InternalServerError(); } if (externalLogin.LoginProvider != provider) { Authentication.SignOut(DefaultAuthenticationTypes.ExternalCookie); return new ChallengeResult(provider, this); } IdentityUser user = await _repo.FindAsync(new UserLoginInfo(externalLogin.LoginProvider, externalLogin.ProviderKey)); bool hasRegistered = user != null; redirectUri = string.Format("{0}#external_access_token={1}&provider={2}&haslocalaccount={3}&external_user_name={4}", redirectUri, externalLogin.ExternalAccessToken, externalLogin.LoginProvider, hasRegistered.ToString(), externalLogin.UserName); return Redirect(redirectUri); } |
By looking at the code above there are lot of things happening so let’s describe what this end point is doing:
a) By looking at this method attributes you will notice that its configured to ignore bearer tokens, and it can be accessed if there is external cookie set by external authority (Facebook or Google) or can be accessed anonymously. it worth mentioning here that this end point will be called twice during the authentication, first call will be anonymously and the second time will be once the external cookie is set by the external provider.
b) Now the code flow will check if the user has been authenticated (External cookie has been set), if it is not the case then Challenge Result defined in step 4 will be called again.
c) Time to validate that client and redirect URI which is set by AngularJS application is valid and this client is configured to allow redirect for this URI, so we do not want to end allowing redirection to any URI set by a caller application which will open security threat (Visit this post to know more about clients and Allowed Origin). To do so we need to add a private helper function named “ValidateClientAndRedirectUri”, you can add this to the “AccountController” class as the code below:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 |
private string ValidateClientAndRedirectUri(HttpRequestMessage request, ref string redirectUriOutput) { Uri redirectUri; var redirectUriString = GetQueryString(Request, "redirect_uri"); if (string.IsNullOrWhiteSpace(redirectUriString)) { return "redirect_uri is required"; } bool validUri = Uri.TryCreate(redirectUriString, UriKind.Absolute, out redirectUri); if (!validUri) { return "redirect_uri is invalid"; } var clientId = GetQueryString(Request, "client_id"); if (string.IsNullOrWhiteSpace(clientId)) { return "client_Id is required"; } var client = _repo.FindClient(clientId); if (client == null) { return string.Format("Client_id '{0}' is not registered in the system.", clientId); } if (!string.Equals(client.AllowedOrigin, redirectUri.GetLeftPart(UriPartial.Authority), StringComparison.OrdinalIgnoreCase)) { return string.Format("The given URL is not allowed by Client_id '{0}' configuration.", clientId); } redirectUriOutput = redirectUri.AbsoluteUri; return string.Empty; } private string GetQueryString(HttpRequestMessage request, string key) { var queryStrings = request.GetQueryNameValuePairs(); if (queryStrings == null) return null; var match = queryStrings.FirstOrDefault(keyValue => string.Compare(keyValue.Key, key, true) == 0); if (string.IsNullOrEmpty(match.Value)) return null; return match.Value; } |
d) After we validate the client and redirect URI we need to get the external login data along with the “ExternalAccessToken” which has been set by the external provider, so we need to add private class named “ExternalLoginData”, to do so add this class to the same “AccountController” class as the code below:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
private class ExternalLoginData { public string LoginProvider { get; set; } public string ProviderKey { get; set; } public string UserName { get; set; } public string ExternalAccessToken { get; set; } public static ExternalLoginData FromIdentity(ClaimsIdentity identity) { if (identity == null) { return null; } Claim providerKeyClaim = identity.FindFirst(ClaimTypes.NameIdentifier); if (providerKeyClaim == null || String.IsNullOrEmpty(providerKeyClaim.Issuer) || String.IsNullOrEmpty(providerKeyClaim.Value)) { return null; } if (providerKeyClaim.Issuer == ClaimsIdentity.DefaultIssuer) { return null; } return new ExternalLoginData { LoginProvider = providerKeyClaim.Issuer, ProviderKey = providerKeyClaim.Value, UserName = identity.FindFirstValue(ClaimTypes.Name), ExternalAccessToken = identity.FindFirstValue("ExternalAccessToken"), }; } } |
e) Then we need to check if this social login (external user id with external provider) is already linked to local database account or this is first time to authenticate, based on this we are setting flag “hasRegistered” which will be returned to the AngularJS application.
f) Lastly we need to issue a 302 redirect to the “redirect_uri” set by the client application along with 4 values (“external_access_token”, “provider”, “hasLocalAccount”, “external_user_name”), those values will be added as URL hash fragment not as query string so they can be accessed by JS code only running on the return_uri page.
Now the AngularJS application has those values including external access token which can’t be used to access our secured back-end endpoints, to solve this issue we need to issue local access token with the help of this external access token. To do this we need to add two new end points where they will accept this external access token, validate it then generate local access token.
Why did we add two end points? Because if the external user is not linked to local database account; we need to issue HTTP POST request to the new endpoint “/RegisterExternal”, and if the external user already linked to local database account then we just need to obtain a local access token by issuing HTTP GET to endpoint “/ObtainLocalAccessToken”.
Before adding the two end points we need to add two helpers methods which they are used on these two endpoints.
Step 8: Verifying external access token
We’ve received the external access token from the external provider, and we returned it to the front-end AngularJS application, now we need to validate and make sure that this external access token which will be sent back from the front-end app to our back-end API is valid, and valid means (Correct access token, not expired, issued for the same client id configured in our back-end API). It is very important to check that external access token is issued to the same client id because you do not want our back-end API to end accepting valid external access tokens generated from different apps (client ids). You can read more about this here.
To do so we need to add class named “ExternalLoginModels” under folder “Models” and paste the code below:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
namespace AngularJSAuthentication.API.Models { public class ExternalLoginViewModel { public string Name { get; set; } public string Url { get; set; } public string State { get; set; } } public class RegisterExternalBindingModel { [Required] public string UserName { get; set; } [Required] public string Provider { get; set; } [Required] public string ExternalAccessToken { get; set; } } public class ParsedExternalAccessToken { public string user_id { get; set; } public string app_id { get; set; } } } |
Then we need to add private helper function named “VerifyExternalAccessToken” to class “AccountController”, the implantation for this function as the below:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 |
private async Task<ParsedExternalAccessToken> VerifyExternalAccessToken(string provider, string accessToken) { ParsedExternalAccessToken parsedToken = null; var verifyTokenEndPoint = ""; if (provider == "Facebook") { //You can get it from here: https://developers.facebook.com/tools/accesstoken/ //More about debug_tokn here: http://stackoverflow.com/questions/16641083/how-does-one-get-the-app-access-token-for-debug-token-inspection-on-facebook var appToken = "xxxxx"; verifyTokenEndPoint = string.Format("https://graph.facebook.com/debug_token?input_token={0}&access_token={1}", accessToken, appToken); } else if (provider == "Google") { verifyTokenEndPoint = string.Format("https://www.googleapis.com/oauth2/v1/tokeninfo?access_token={0}", accessToken); } else { return null; } var client = new HttpClient(); var uri = new Uri(verifyTokenEndPoint); var response = await client.GetAsync(uri); if (response.IsSuccessStatusCode) { var content = await response.Content.ReadAsStringAsync(); dynamic jObj = (JObject)Newtonsoft.Json.JsonConvert.DeserializeObject(content); parsedToken = new ParsedExternalAccessToken(); if (provider == "Facebook") { parsedToken.user_id = jObj["data"]["user_id"]; parsedToken.app_id = jObj["data"]["app_id"]; if (!string.Equals(Startup.facebookAuthOptions.AppId, parsedToken.app_id, StringComparison.OrdinalIgnoreCase)) { return null; } } else if (provider == "Google") { parsedToken.user_id = jObj["user_id"]; parsedToken.app_id = jObj["audience"]; if (!string.Equals(Startup.googleAuthOptions.ClientId, parsedToken.app_id, StringComparison.OrdinalIgnoreCase)) { return null; } } } return parsedToken; } |
The code above is not the prettiest code I’ve written, it could be written in better way, but the essence of this helper method is to validate the external access token so for example if we take a look on Google case you will notice that we are issuing HTTP GET request to a defined endpoint by Google which is responsible to validate this access token, so if the token is valid we’ll read the app_id (client_id) and the user_id from the response, then we need to make sure that app_id returned as result from this request is exactly the same app_id used to configure Google app in our back-end API. If there is any differences then we’ll consider this external access token as invalid.
Note about Facebook: To validate Facebook external access token you need to obtain another single token for your application named appToken, you can get it from here.
Step 9: Generate local access token
Now we need to add another helper function which will be responsible to issue local access token which can be used to access our secure back-end API end points, the response for this token need to match the response we obtain when we call the end point “/token”, there is nothing special in this method, we are only setting the claims for the local identity then calling “OAuthBearerOptions.AccessTokenFormat.Protect” which will generate the local access token, so add new private function named “” to “AccountController” class as the code below:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
private JObject GenerateLocalAccessTokenResponse(string userName) { var tokenExpiration = TimeSpan.FromDays(1); ClaimsIdentity identity = new ClaimsIdentity(OAuthDefaults.AuthenticationType); identity.AddClaim(new Claim(ClaimTypes.Name, userName)); identity.AddClaim(new Claim("role", "user")); var props = new AuthenticationProperties() { IssuedUtc = DateTime.UtcNow, ExpiresUtc = DateTime.UtcNow.Add(tokenExpiration), }; var ticket = new AuthenticationTicket(identity, props); var accessToken = Startup.OAuthBearerOptions.AccessTokenFormat.Protect(ticket); JObject tokenResponse = new JObject( new JProperty("userName", userName), new JProperty("access_token", accessToken), new JProperty("token_type", "bearer"), new JProperty("expires_in", tokenExpiration.TotalSeconds.ToString()), new JProperty(".issued", ticket.Properties.IssuedUtc.ToString()), new JProperty(".expires", ticket.Properties.ExpiresUtc.ToString()) ); return tokenResponse; } |
Step 10: Add “RegisterExternal” endpoint to link social user with local account
After we added the helper methods needed it is time to add new API end point where it will be used to add the external user as local account and then link it with the created local account, this method should be accessed anonymously because we do not have local access token yet; so add new method named “RegisterExternal” to class “AccountController” as the code below:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 |
// POST api/Account/RegisterExternal [AllowAnonymous] [Route("RegisterExternal")] public async Task<IHttpActionResult> RegisterExternal(RegisterExternalBindingModel model) { if (!ModelState.IsValid) { return BadRequest(ModelState); } var verifiedAccessToken = await VerifyExternalAccessToken(model.Provider, model.ExternalAccessToken); if (verifiedAccessToken == null) { return BadRequest("Invalid Provider or External Access Token"); } IdentityUser user = await _repo.FindAsync(new UserLoginInfo(model.Provider, verifiedAccessToken.user_id)); bool hasRegistered = user != null; if (hasRegistered) { return BadRequest("External user is already registered"); } user = new IdentityUser() { UserName = model.UserName }; IdentityResult result = await _repo.CreateAsync(user); if (!result.Succeeded) { return GetErrorResult(result); } var info = new ExternalLoginInfo() { DefaultUserName = model.UserName, Login = new UserLoginInfo(model.Provider, verifiedAccessToken.user_id) }; result = await _repo.AddLoginAsync(user.Id, info.Login); if (!result.Succeeded) { return GetErrorResult(result); } //generate access token response var accessTokenResponse = GenerateLocalAccessTokenResponse(model.UserName); return Ok(accessTokenResponse); } |
By looking at the code above you will notice that we need to issue HTTP POST request to the end point http://ngauthenticationapi.azurewebsites.net/api/account/RegisterExternal where the request body will contain JSON object of “userName”, “provider”, and “externalAccessToken” properties.
The code in this method is doing the following:
- once we receive the request we’ll call the helper method “VerifyExternalAccessToken” described earlier to ensure that this external access token is valid, and generated using our Facebook or Google application defined in our back-end API.
- We need to check if the user_id obtained from the external access token and the provider combination has already registered in our system, if this is not the case we’ll add new user using the username passed in request body without a password to the table “AspNetUsers”
- Then we need to link the local account created for this user to the provider and provider key (external user_id) and save this to table “AspNetUserLogins”
- Lastly we’ll call the helper method named “GenerateLocalAccessTokenResponse” described earlier which is responsible to generate the local access token and return this in the response body, so front-end application can use this access token to access our secured back-end API endpoints.
The request body will look as the image below:
Step 11: Add “ObtainLocalAccessToken” for linked external accounts
Now this endpoint will be used to generate local access tokens for external users who already registered with local account and linked their external identities to a local account, this method is accessed anonymously and will accept 2 query strings (Provider, ExternalAccessToken). The end point can be accessed by issuing HTTP GET request to URI: http://ngauthenticationapi.azurewebsites.net/api/account/ObtainLocalAccessToken?provider=Facebook&externalaccesstoken=CAAKEF……….
So add new method named “ObtainLocalAccessToken” to controller “AccountController” and paste the code below:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
[AllowAnonymous] [HttpGet] [Route("ObtainLocalAccessToken")] public async Task<IHttpActionResult> ObtainLocalAccessToken(string provider, string externalAccessToken) { if (string.IsNullOrWhiteSpace(provider) || string.IsNullOrWhiteSpace(externalAccessToken)) { return BadRequest("Provider or external access token is not sent"); } var verifiedAccessToken = await VerifyExternalAccessToken(provider, externalAccessToken); if (verifiedAccessToken == null) { return BadRequest("Invalid Provider or External Access Token"); } IdentityUser user = await _repo.FindAsync(new UserLoginInfo(provider, verifiedAccessToken.user_id)); bool hasRegistered = user != null; if (!hasRegistered) { return BadRequest("External user is not registered"); } //generate access token response var accessTokenResponse = GenerateLocalAccessTokenResponse(user.UserName); return Ok(accessTokenResponse); } |
By looking at the code above you will notice that we’re doing the following:
- Make sure that Provider and ExternalAccessToken query strings are sent with the HTTP GET request.
- Verifying the external access token as we did in the previous step.
- Check if the user_id and provider combination is already linked in our system.
- Generate a local access token and return this in the response body so front-end application can use this access token to access our secured back-end API endpoints.
Step 12:Updating the front-end AngularJS application
I’ve updated the live front-end application to support using social logins as the image below:
Now once the user clicks on one of the social login buttons, a popup window will open targeting the URI: http://ngauthenticationapi.azurewebsites.net/api/Account/ExternalLogin?provider=Google&response_type=token&client_id=ngAuthApp &redirect_uri=http://ngauthenticationweb.azurewebsites.net/authcomplete.html and the steps described earlier in this post will take place.
What worth mentioning here that the “authcomplete.html” page is an empty page which has JS function responsible to read the URL hash fragments and pass them to AngularJS controller in callback function, based on the value of the fragment (hasLocalAccount) the AngularJS controller will decide to call end point “/ObtainLocalAccessToken” or display a view named “associate” where it will give the end user the chance to set preferred username only and then call the endpoint “/RegisterExternal”.
The “associate” view will look as the image below:
That is it for now folks! Hopefully this implementation will be useful to integrate social logins with your ASP.NET Web API and keep the API isolated from any front end application, I believe by using this way we can integrate native iOS or Android Apps that using Facebook SDKs easily with our back-end API, usually the Facebook SDK will return Facebook access token and we can then obtain local access token as we described earlier.
If you have any feedback, comment, or better way to implement this; then please drop me comment, thanks for reading!
You can check the demo application, play with the back-end API for learning purposes (http://ngauthenticationapi.azurewebsites.net), and check the source code on Github.
Follow me on Twitter @tjoudeh
References
- Informative post by Brock Allen about External logins with OWIN middleware.
- Solid post by Dominick Baier on Web API Individual Accounts.
- Great post by Rick Anderson on VS 2013 MVC 5 template with individual accounts.
- Helpful post by Valerio Gheri on How to login with Facebook access token.
Hi, I did something similar to this, however – I used the MVC template form VS2015 and within it the Account/ExternalLogin had a call to the Challenge function.
I used the same function as :
[HttpPost]
public JsonResult Quote(QuoteDetails _model)
{
var response = Json(new AccountController.ChallengeResult(“Facebook”, Url.Action(“ExternalLoginCallback”, “Account”, “”)));
return response;
}
What I was hoping for was to be able to post to this action, call the Challenge function and I was hoping to get a URI to redirect the user to. I was going to return the payload back to the frontend to handle the redirection.
However, in the var response I noticed that the redirect URI does not contain a redirect uri… and I debugged the original template that was making a post to Account/ExternalLogin and at somepoint ExecuteResult(ControllerContext context) was being called. However, with the modification that I had made this call was not getting executed.
In your post you mentioned that in your sequence (#3) The consent screen for Google will be shown, and the user will provide his Google credentials to authenticate.
how is this happening (e.g how is the client getting redirected ) ?
Please help while logging with google i got error….
400. That’s an error.
Error: redirect_uri_mismatch
Application: AngularJSAuthentication
You can email the developer of this application at: nisar2048@gmail.com
The redirect URI in the request: http://localhost:26264/signin-google did not match a registered redirect URI.
Hi – how do I return a refresh token from ‘GenerateLocalAccessTokenResponse’ following this tutorial ? https://bitoftech.net/2014/07/16/enable-oauth-refresh-tokens-angularjs-app-using-asp-net-web-api-2-owin/
Hey there,
Having some trouble. After completing steps, I attempted to use the Facebook login and recieved this error:
No HTTP resource was found that matches the request URI ‘http://localhost:PORT/api/Account/ExternalLogin?provider=Facebook&response_type=token&client_id=ngAuthApp&redirect_uri=http://localhost:PORT/authcomplete.html’.
No action was found on the controller ‘Account’ that matches the request.
I went back through the steps and am not missing anything. I even tried copying the version from your mast solution.
Any assistance would be greatly appreciated. Thanks!
Hi Shevy, could you try to use the same Nuget packages version I have used in the project and check if this solves the issue, this is the only variable between your code and the GitHub project, if it worked then try again using the newer Nuget versions.
Hi, thanks for your excellent sample.
I have an issue. I create another sample which is not using AngujarJS. After authenticated by external provider and generated a local token, I put Authorization Bearer to the next WebAPI request but it never gets authorized. The IsAuthenticated is false, even there is a correct Authorization in headers.
Do you see something strange there? Any suggestion would be much appreciated. Thank you!
Ji Juan, it is hard for em to troubleshoot this issue, try to invoke tokens using PostMan not a client and watch the result, it should work the same weather it was AngularJS app or desktop app.
Hi Taiseer,
I would like to extend the ExternalLogin end point to return the external access token’s refresh token to the redirectUri, along with the main access token, so that the mobile app I am building is able to call the external provider and request new external access tokens (which are then used to request new local access token’s for my api) – from a security point of view is this something you would definitely recommend NOT doing, and if so is there anything I can do to the refresh token (hash it etc) to improve security, or do you think it is OK to do this?
Thanks in advance, and thanks again for the tutorials, this series has been a massive help.
Dave
Hi Taiseer,
Thank you for your excellent set of posts! I have trouble combining external logins with refresh tokens. I can see you also didn’t implement this. What is your advice on how to get it done?
Hi Kelu, Please check the comments as I already answered this question with deep details.
Hi Kelu, I am also looking to implement refresh tokens for my external logins, but I don’t see where you answered this. Can you provide a link to where you answered this?
Hi, nice work and thanks for sharing your knowledge with us. Please I’m also looking for the comment where you said you’ve explained how to incorporate the refresh token with the external login. Can you please point me in the right direction.
Regards
Tela
I too have not been able to find the comments that explain how to generate the refresh token on the ObjectLocalAccessToken function and nothing that I’ve tried as worked. Any guidance or a link to the comment would be greatly appreciated!
On fi Taiseer,
Even I couldn’t find your comment on implementing external logins with refresh token. So this is what I added in GenerateLocalAccessTokenResponse method
var context = new Microsoft.Owin.Security.Infrastructure.AuthenticationTokenCreateContext(
Request.GetOwinContext(),Startup.OAuthServerOptions.AccessTokenFormat, ticket);
context.Ticket.Properties.Dictionary.Add(new KeyValuePair(“client_id”, clientId));
context.OwinContext.Set(“clientAllowedOrigin”, client.AllowedOrigin);
context.OwinContext.Set(“clientRefreshTokenLifeTime”, client.RefreshTokenLifeTime.ToString());
await Startup.OAuthServerOptions.RefreshTokenProvider.CreateAsync((context));
And added refresh_token to JObject and it looks like below.
JObject tokenResponse = new JObject(
new JProperty(“userName”, userName),
new JProperty(“access_token”, accessToken),
new JProperty(“token_type”, “bearer”),
new JProperty(“expires_in”, tokenExpiration.TotalSeconds.ToString()),
new JProperty(“.issued”, ticket.Properties.IssuedUtc.ToString()),
new JProperty(“.expires”, ticket.Properties.ExpiresUtc.ToString()),
new JProperty(“refresh_token”, context.Token),
new JProperty(“client_Id”,clientId)
);
After doing this I was getting refresh token but I was not able to generate a new access token using this refresh token as it was failing in ReceiveAsync of RefreshTokenProvider i.e. Ticket was null even after context.DeserializeTicket(refreshToken.ProtectedTicket);
On further research I found this http://ambracode.com/index/show/8014 , which exactly explains the same problem I was facing and followed and also referenced your article. After following the steps mentioned in that article I am now able to generate a new access token using the refresh token.
Thanks
Ravindra
Thank you for sharing this, I will review it soon and let you know if there are any comments.
Dear Ravindra
I have same problem with you. Could you please send me the code to solve it? My email is hoanvtr@gmail.com.
Thanks & Best Regards.
In Step 9, Line 19 there is a NullReferenceEx.
This is fixed by using the existing Instance of OAuthBearerOptions in the Startup.cs as explained here: http://stackoverflow.com/questions/19938947/web-api-2-owin-bearer-token-authentication-accesstokenformat-null
I need to check this, thanks for the heads up.
I tried to add these lines and it worked
OAuthBearerOptions = new OAuthBearerAuthenticationOptions();
if (OAuthBearerOptions.AccessTokenFormat == null)
{
IDataProtector dataProtecter = app.CreateDataProtector(
typeof(OAuthAuthorizationServerMiddleware).Namespace,
“Access_Token”, “v1”);
OAuthBearerOptions.AccessTokenFormat = new TicketDataFormat(dataProtecter);
}
Within Startup.Auth.cs do this:
public partial class Startup {
….
public static OAuthBearerAuthenticationOptions OAuthBearerOptions { get; private set; }
….
public Startup() {
OAuthBearerOptions = new OAuthBearerAuthenticationOptions();
}
….
public void ConfigureAuth(IAppBuilder app) {
….
app.UseOAuthBearerAuthentication(OAuthBearerOptions);
….
}
}
Hi Taiseer,
Thank you for the awesome posts!
I followed your tutorial and encountered some problems.
I am stuck by the “ExternalLogin” endpoint, once I logged into Google then accepted the Google API, to shows me “access denied”. I tried reading the code and adding a breakpoints to the possible next steps, but unfortunately, I wasn’t able to move on. Do you have any advice on this?
Thanks.
Hi Bryan,
I guess this is due different NuGet packages version for the Microsoft.Owin.Security.Google, try to sue the exact package version I used in the post and let me know if this solved your issue.
Hi again Taiseer,
Thank you for your swift response.
I uninstalled the previous version and installed the one you currently stated in your posts.
Unfortunately, it didn’t solve my problem. I think I’m missing something. If you have any other ideas, please let me know, thank you very much. I will try again in my end.
Bryan
Hello again Taiseer,
After some research, I found out that the installed Google package was not linked to the right project after changing the version, that is why I’m getting the “access denied”. Thank you very much.
Although now, I am encountering a different problem.
Invalid URI: The format of the URI could not be determined.
at System.Uri.CreateThis(String uri, Boolean dontEscape, UriKind uriKind) at System.Uri..ctor(String uriString) at System.Web.Http.ApiController.Redirect(String location) at WebApplication11.Controllers.AccountController.d__5.MoveNext() in c:\~~\Controllers\AccountController.cs:line 146 —
Under line 146 of AccountController.cs
redirectUri = string.Format(“{0}#external_access_token={1}&provider={2}&haslocalaccount={3}&external_user_name={4}”,
redirectUri,
externalLogin.ExternalAccessToken,
externalLogin.LoginProvider,
hasRegistered.ToString(),
externalLogin.UserName);
return Redirect(redirectUri);
Thank you in advance..
Hi Bryan.
Could you be a little more specific about how you solved the “access denied” problem? What do you mean when you say that the “google package was not linked to the right project”? And where can I find the information about package linking? Any tip would be very helpful.
Roger
Hi Taiseer!
First of all I just wanted to say a massive thank you for these fantastic posts! I have managed to get the Facebook and Google logins working perfectly and they have really helped me to understand how to authenticate with external logins/OAuth with the Web API.
I’m now trying to extend this and get Twitter and Microsoft Account logins working also. However I am running into a few problems doing this. Do you have any suggestions or example code of how to do this? More specifically, how to verify the external tokens for these providers?
Thanks again and keep up the good work! 🙂
Best Regards,
Roger.
Hi Roger,
Thanks for you message, I didn’t try using Twitter or MS account, but most probably you will find the answers under the documentation part. Let me know if you did not find anything.
Hi Taiseer,
Thanks for this great work.
I’m trying to use Microsoft Live and Twitter login for my application. Blocked at validating token. Didn’t find any endpoint from MS or Twitter which we can use it to validate. Do you have anything on this?
Thanks,
Rajesh
Were you able to find a solution for Microsoft?
Hi, Taiseer Joudeh this is nice post and I thanks to you from my deep heart and I want to know one think is that how can I get more info of user like Picture, Email, Mobile, Address, etc from Google or Facebook that you implemented for only Username,
Thanks…..
Hi Taiseer, Thanks for this awesome post,
I stuck with one error and didn’t find any solution just only you can guide me, the error is
Message: Object reference not set to an instance of an object.
Stack trace: at Nullplex.Rest.Controllers.AccountController.GenerateLocalAccessTokenResponse(String userName) in d:\Paybackbazaar\Nullplex.Payback\Nullplex.Rest\Controllers\AccountController.cs:line 350
I’d check that the parameter username contains valid username so,
The line number 350 is:
var accessToken = Startup.OAuthBearerOptions.AccessTokenFormat.Protect(ticket);
so please help me upon the same error,
and sorry for disturbed you…
Hi again Taiseer,
For above error I’d solved it….
I want to know one think is that how can I get more info of user like Picture, Email, Mobile, Address, etc from Google or Facebook that you implemented for only Username,
Thanks….
You need to set the correct Facebook scopes to obtain access to Picture, email, etc… This will affect the consent screen so user can approve/reject what the application trying to have access ok. Check FB scopes here.
Thank you so much… You are really a genius guy…Thank again…
Hi Shoaib,
Can you please highlight how did you achieve this? Do we have to enable any more access permissions in facebook developer console or is it something that needs to be added in the code above, If its in code kindly share. I need the email especially along with username.
How did you solve the Message: Object reference not set to an instance of an object.
Stack trace: at Nullplex.Rest.Controllers.AccountController.GenerateLocalAccessTokenResponse(String userName) in d:\Paybackbazaar\Nullplex.Payback\Nullplex.Rest\Controllers\AccountController.cs:line 350
I’d check that the parameter username contains valid username so,
The line number 350 is:
var accessToken = Startup.OAuthBearerOptions.AccessTokenFormat.Protect(ticket);??
Best,
Alexander
Hi Shoaib,
You issue in the Startup class, make sure you are setting it like this LOC
How did you solve this?
Hi Taiseer,
I have downloaded this code filled the Google ClientId ClientSecret in the code and when I run the UI Login with Google it displays HTTP 400 Bad Request The webpage could not be found Can you please help me
Hmm, did you update the NuGet packages to latest version? Please try using them using the same version coming with the App. it should be working 100%
Thanks for nice post.
Can we get facebook email and password? Because, next time to login with our web by using facebook id and password.
Freelancer.com is using the same way? is that possible??
Nop, you can not ever get facebook password, you need to obtain an access token from FB to keep user logged in.
What is the point of the redirect URI? The API knows whether or not the user has been registered or not because it is setting the haslocalaccount variable in the response to the front end. Are there any downsides to having the API determine whether to log the user in or create an account? It seems like more work to have the client determine whether to log in or register.
Hi Taiseer.
Congratulations for this post.
There is something that bothers me: sending to the user agent the external access token obtained by code grant flow seems breaking the oauth spec: this access token (and optionnally its associated refresh token) must not be delivered to the user agent.
However, I am pretty new in Oauth/openidconnect and Owin worlds, so I may have missed something.
Is there some good reasons that let you think that sending this access token to user agent is not a security threat?
Thanks for your reply, and for your great job.
Jérôme
Hi Taiseer,
Thanks for this great post , but I haven’t been able to get it working. Everytime the request reaches GetExternalLogin method on Account Controller, the principal is null (System.NullReferenceException).
So doing if (!this.User.Identity.IsAuthenticated) crashes the app. I’ve configured the startup class and added the proper annotations before the “GetExternalLogin” as you’ve mentioned.
Have you any ideas of what can be going wrong.
Thanks in advance.
Hi Raouf,
To be honest I have to specific answer right now, but it seems you are didn’t configure the external auth cookie correctly which is created when you login with external auth provider. please check startup class again.
Hello Taiseer, gret posts, thank you a lot!
But i’m have same problem abowe mentioned, User property is null…
But… i am put our algoritm into self-hosted windows service..
update…
i am using a Microsoft.AspNet.WebApi.OwinSelfHost
i changed
if (!User.Identity.IsAuthenticated)
{
return new ChallengeResult(provider, this);
}
..to…
if (User == null || !User.Identity.IsAuthenticated)
{
return new ChallengeResult(provider, this);
}
and all OK.
Hello,
I tried the application using a mobile browser (iPhone iOS) for both Chrome and Safari.
When using any Social Login buttons, a new tab is opened where the Social web page is displayed. Upon logging in, the page is redirected to ngauthenticationweb.azurewebsites.net/authcomplete.html, but that tab never closes, nor goes back to the main application. This thereby results in the successful social login not propagating to the main application.
Any way around this?
Thanks
I didn’t test it with all browsers, so i do not have an answer for this. You need to teak the JS functions little bit to make it working maybe.
Thank you for this excellent work. I would like to use this approach to wire up Okta as an external provider. However, they use SAML2. I’ve had some success when using the MVC5 template and adding the Microsoft.Owin.Security.WsFederation nuget package.
Any thoughts on how to go about this or whether I should just stick with the MVC templates?
Regards,
Daudi
Starting from your ideas, I tried to get the same result starting from the template generated by VS for a Web API project with individual accounts. Next I removed all the MVC stuff, uninstalled related packages, and got a plain OWIN hosted Web API.
I finally got it working, the main problem was related to the call to GetExternalLoginInfoAsync(), which tries to authenticate the request via ExternalCookie, while the client application authenticates via bearer a token (external). I write my override version of GetExternalLoginInfoAsync, accepting the authenticationType as an input parameter, and now it’s working. I’ll upload my sample to github as soon as I have a little better UI
Happy to hear it is working, I prefer to start from scratch and add the packages needed, its a cleaner approach.
Hi Riccardo
it is about three months that I have same problem on a self-defined project and have not any success till now. I would be so happy to use your sample as a guide to solve my problem. Please leave the link to its github repository or share it with me in the way you prefer.
Hi Arman,
What is the problem you are facing please?
Hi Arman
I’ve moved to another company, so I forgot about this post. I’ll upload the sample and post the link to github repo as I get the source from my backups
Please Riccardo, share us your project
Thanks a lot for the post. I would like to find out if there was a way to check in the background if the user already has an account before displaying the pop-up because when the user logs in again after the initial registration, the window pops up for a while without anything to show in the display then closes before the user is signed in. Thanks.
This implementation details you can do it in the Angular App, so yes you can think of a way to do the DB call and check this, then display the popup.
Hi Taiseer.
First I would like to say thanks so much for a massive great post. I’m inspired by your post and learned from it so much. But my question is that in my website, I don’t use refresh token, so that which part of the code I should change to use the login external without using refresh token. Now, I’m stuck when I try to use ValidateClientAndRedirectUri method.
Thank you. Looking to hearing your feedback
I would just like to say there is another approach to how to do a facebook login in a SPA and that is to use the Facebook JavaScript SDK. I tried implementing the solution outlined above in an Aurelia SPA, but in the end gave up and just used the JS API which is very easy to use in a spa. There is almost no code required. Here is the sample from FB: https://developers.facebook.com/docs/facebook-login/web
Thanks for sharing this, I will check how you can use the FB JS SDK to implement this.
Hi Taiseer,
Thank you for this informative post. I am implementing External Login and stuck at step 10. When I use URL “https://graph.facebook.com/debug_token?input_token=abcd…&&access_token=1234…” I am able to get all the user details and showing me that user is valid. But when I run it through the postman , its breaking at VerifyExternalAccessToken (var response = await client.GetAsync(uri);) and giving error “Unable to connect to rmote server”. Could please guide me through this issue.
Thanks in advance!
That is strange, this happening locally and if you published the Api to a server?
SO how do you migrate it? Where is the dB we can seed?
I’m in analysis phase and I want to ask if this Approach will work if my client application is Mobile app ?, it will help me to make a decision.
Thanks for your posts.
You it will work, as well you can use the FB SDK to obtain FB access tokens.
Hi
If i use for example xamarine to develop the mobile app using google auth native api the workflow is the same on the server or i need to change something ? there is an example on mobile app ?
thanks alot
Paps
Hi Paps, it should be the same, as well you can benefit from the FB or Google SDKs to enable social logins.
I have read the above comments, but I couldn’t understand if I implement this for Mobile app, (not FB Sdk, but yours). What should be in that case the accessible URI address for authComplete.html page (because in that case it is mobile)?
This was a big help to me. I have been browsing for days for a decent description of this process and yours is the best by a country mile.
I almost have it working but I am getting a null reference exception at the point where I am generating a new access token because the value Startup.OAuthBearerOptions.AcessTokenFormat is null.
How can I set this value ?
It currently has the following values:-
Startup.OAuthBearerOptions
{Microsoft.Owin.Security.OAuth.OAuthBearerAuthenticationOptions}
base: {Microsoft.Owin.Security.OAuth.OAuthBearerAuthenticationOptions}
AccessTokenFormat: null
AccessTokenProvider: null
Challenge: null
Provider: null
Realm: null
SystemClock: {Microsoft.Owin.Infrastructure.SystemClock}
Many thanks.
You are most welcome, you need to make sure you are instantiating the object in class Startup.cs before calling it, please check this LOC
Actually, the missing piece appears to be several lines later: https://github.com/tjoudeh/AngularJSAuthentication/blob/master/AngularJSAuthentication.API/Startup.cs#l54
So, this might be a weird philosophical type question but… Why is this implementation in the Web API layer and not in the OWIN layer? I suspect that the answer is that it follows the model laid down by the ASP.Net template, which also has this strange “half in/half out” architecture where one of the endpoints (Token) is an OWIN endpoint and the other (ExternalLogin) is a Web API Action. Could this have been done entirely in the OWIN layer? Was there a good reason not to do it that way?
I have gone through this tutorial, and there is one thing that I don’t understand.
Why are you sending the external access token to method “ObtainLocalAccessToken” with GET and not POST?
As i understand it, sensitive information should usually be sent with POST, because with GET the parameters will be
stored in plain text in server logs, even if you use TLS (https). I had a look at the expiration of the external access token for facebook and it is valid for 60 days, so is that not a valid security concern?
Especially if you are deploying the project in the cloud you don’t have any control over how those server logs are protected?
Hi Taiseer,
Thank you very much for this article. It help me to implement social login in my project. But I also want to generate refresh token when user login with Social media. I read your another article in which you have generated the refresh token, But In this you are sending the refresh token along with access token. Please guide me how to send refresh token when login with social media.
Excellent tutorial.
How do I add the columns “Picture ” and ” FullName ” and also fill in the e- mail ?
You should require those in Facebook scopes so you obtain a FB token which allows you to get this data.
what it will be my redirect_uri if I call it from my android app ?
I am unable to get the demo project past Step 7. When I try to authenticate with Google I get “Client_Id MY_CLIENT_ID is not registered in the system. I simply downloaded your code, updated the needed references, added my Google Client and Secret Key, updated the Authorized redirect URIs on the Google + API, deployed the Web API, and ran the UI project. The popup window displays this error, where I am expecting to authenticate with Google. Please help.
var client = _repo.FindClient(clientId);
if (client == null)
{
return string.Format(“Client_id ‘{0}’ is not registered in the system.”, clientId);
}
Hi Robert,
Please refer to the previous post to see how the table OAuth clients are filled, it seems you missed filling the table with the correct OAuth clients thats why you are not able to find any clients
Hi Taiseer,
Thanks for the great set of tutorials,
I am trying to follow the same path and add WSFederation authentication
The issue I am stuck on is that the WsFederationAuthenticationOptions does not contain a Provider class, How do it get past this?
David
Hi Taiseer,
Thank you for the awesome posts!
Trying to add ADFS as a external login, stuck since the WsFederationAuthenticationOptions does not contain an AuthenticationProvider
Any input on how to continue?
David
Excellent tutorial.
I tried adding ADFS as an external provider however the WsFederationAuthenticationOptions does not include a Provider
any inputs on how to add similar logic with ADFS?
Thanks,
David
At Step 9 we create the GenerateLocalAccessTokenResponse ” the response for this token need to match the response we obtain when we call the end point “/token”.
Question: Can i have one function for both SignIn via user/pass and SignIn via external providers?
The solution is working as it is, however (no offence) it seems like a “hack” to me, since we try to match the output of another function insted of using one function for both.
At Step 9 we create the GenerateLocalAccessTokenResponse ” the response for this token need to match the response we obtain when we call the end point “/token”.
Question: Can i have one function for both SignIn via user/pass and SignIn via external providers?
The solution is working as it is, however (no offence) it seems like a “hack” to me, since we try to match the output of another function insted of using one function for both.
*Sorry if multiple reposts (I get: “Notice: Trying to get property of non-object in /home3/bitoftec/public_html/wp-content/plugins/crayon-syntax-highlighter/crayon_wp.class.php on line 717
Warning: Cannot modify header information – headers already sent by (output started at /home3/bitoftec/public_html/wp-content/plugins/crayon-syntax-highlighter/crayon_wp.class.php:717) in /home3/bitoftec/public_html/wp-includes/comment.php on line 517”
Hi ,Taiseer Joudeh
First thanks for this nice post.
I am getting error message while trying to login with google+/Facebook , I am getting error message “The given URL is not allowed by Client_id ‘ngAuthApp’ configuration.”,Can you please assist me on this.
Thanks,
Rajendra
Hello,
You need to register this client in the OauthClients table. Did you try to debug the code and made sure this client exists?
Thanks for this ! I am using WebAPI2 with Google auth. I found that all NuGet packages need to be updated to latest versions for this to work. (OWIN v3+) Also, I’m not sure if this is a change on Google’s side but in the VerifyExternalAccessToken() method of Account controller, the Google keys are different. In your code:
parsedToken.user_id = jObj[“user_id”];
parsedToken.app_id = jObj[“audience”];
But I dont see these keys. Instead, I had to use:
parsedToken.user_id = jObj[“email”];
parsedToken.app_id = jObj[“aud”];
Would you please let me know if this looks wrong?
John, thanks for your comment. Please share your solution when you have time 🙂
Hello there! thanks for this piece of magic, it works like a charm!
I have actually one question/possible improvement, which I will be working on: I don’t quite like the use of authComplete.html. It looks a little bit out of the application and messy. Actually the file is in the root level of the app which seems to be isolated from the rest.
I understand the reason, since login will come back there. But: Can’t we make that an angular route instead of something isolated? I believe this would be much better than using this isolated file to finish authentication.
Let me know your opinion, or why have you built it that way, I might be not seeing something.
Thanks!
Good eveening. I was passing this tutorial and it is really great. However, I have a question about social logIn against SignUp .
I have already configured google logIn in my project and found out that after the use of this feature I get a new user in DB. What seems strange is that this user does not have any password.
As I understand such entering to the website is almost similar to the sighnUp. However in the future somebody could enter account created with the help of social login with no password.
So, in that case, maybe, it is better to make this feature get called as social signUp and ask user to add password to the created user along with nickName?
Hello Andry,
For the first time when the user login with social login a user will be created in the database without a password, it looks like the signup process. For the second time when the user tries to access the website using the social login, the user will provide his/her FB/Google credentials and obtain an access token which verified by your application then s/he will access your web application. There is no need to create a local password as you will lose the benefit of using social identities.
No one will be able to access the web application unless he has access to the social login credentials if the social login us compromised then they can access your web application for sure.
Hope this help.
Thank you for your answer, it seems, like I almost got that. The only question is if user loses an opportunity to login into his account, created with the help of external sources without the help of external sources. I mean just entering his credentials in the login form?
Hi Taiseer,
I have two apps: Android app and Web app. Both using social logins. Web app is using social logins implemented as you discribed in this tutorial. This works great. In my Android app, I am using Facebook SDK to get user access token. Next, when I want to register new user using obtained user access token, I send POST request like http://myapi.azurewebsites.net/api/account/RegisterExternal with userName, provider, externalAccessToken (the one I recieved in my android app) but I am getting response code 400 and the message ,,Invalid Provider or External Token”. For information, our Android app has different app id in facebook than our Web app. Could you please help how can I solve this issue ?
Thank you so much in advance.
Ok,
I resolved this problem by migrating Android app under the Web app so now both apps have the same facebook app id.
Thank you.
Thanks for sharing the answer and apologise for not replying quickly.
“Authenticated” method is never being fired for Facebook provider. User idetity isauthenticated is True but “Authenticated” has never been fired so I cannot read claims from Facebook. Might I be missing something? Thanks.
Hi Taiseer,
Thank for your posts very much. I had problem when logging with Facebook but everything was OK when I did with Google
I added scopes to Facebook authentication options like: email, public_profile, user_friends
But I didn’t receive any more claim in response from server. I had checked in context parameter of method Authenticated in FacebookAuthProvider class (checked in context.User and context.Identity.Claims). I just had 2 claims: name and nameidentifier
Do you have any idea?
Thanks
Hello and thank you for this great tutorial.
I am trying to get a user from this line of code: User user = await _repo.FindAsync(new UserLoginInfo(externalLogin.LoginProvider, externalLogin.ProviderKey));
but it always returns null.
In my table “AspnetUserLogins” I can see LoginProvider, ProviderKey, UserId, then I also have this column: “IdentityUser_Id” which is always null.. Not sure if this is causing the problem but somehow I am not receiving data from that table.. any ideas?
Hello Alex,
Are the LoginProvider and ProviderKey properties filled? Did you debug the class UserLoginInfo? To be honest I’m not sure what is happening at your end. This has been for a while 🙁
Thank you SO much for this. I already had a mvc with identity and owin, i was trying to migrate to a web api version.
Once again. ty.
Is it Possible to do this without asp.net identity and EFM and with Ado.net
Yes you can use any ORM you like to get/post data.
Hi Taiseer,
Thanks again for this article. I just need a quick help that is related to client table.
Can we set multiple AllowedOrigin url in ngAuthApp Client. If yes then how we can update the code. if no then why we cannot do this. I have to same application with there urls are different other then url everything same.
Can you please help me.
Thanks in advance
Hi Taiseer Joudeh!
I’ve updated Microsoft.Owin.Security.Google to 3.0.1. And a error occured: “access_denied” after I allowed my web access google account.
So, could you help me to fix this error!?
Thanks you so much!
Hi Taiseer,
Thanks for the awesome article. I am trying to implement this on a mobile app supporting Google and Microsoft login. I cannot seem to find Microsoft API for verifying access token.
These are the authorization and token endpoints i used for microsoft:
Authorization end point: https://login.microsoftonline.com/common/oauth2/V2.0/authorize
Token end point: https://login.microsoftonline.com/common/oauth2/v2.0/token
The alternative i was thinking to implement was to use id_token instead of access token. Then i could validate the id_token and get required information from id_token, but unfortunately Microsoft does not provide ID on their end in the id_token jwt.
Have you tried this with Microsoft authentication??
were able to figure this out for Microsoft?
Hello,
When I trying to use external login with google the application redirects me to the google login, and I put my user and password, the application says that I logged succesfully but then(in associate page) when I write the name (without spaces) the browser console show me this error “Failed to load resource: the server responded with a status of 400 (Bad Request)”, because of this url “http://localhost:port/api/account/RegisterExternal”.
Any help would be appreciated, Thank you
From where I can get the Email address of the user using oAuth following your approach?
Hello Usman,
You can read it from the claim, and it depends on the external identity you are using, you need to ask for the email by using the external identity scope. Not all external identities expose the email claim.
HI Taiseer
Thanks for the article!
I have gone through the code and the post and it seems like the angular app is missing the google & facebook singup html (& possibly some other code) have you dropped them intentionally?
Thanks!
Ryan.
sorry! it seems to be working now, when I firstly launched the app I couldn’t see the social logins. A little weird. I am learning so much from your posts. I appreciate them very much!
I am getting null exception on this line of code,
var accessToken = Startup.OAuthBearerOptions.AccessTokenFormat.Protect(ticket);
Can you please, Help me out.
Hi Jhon,
Check how you are instantiating this object in this LOC
Hi Taisser
I was very useful the guide that you did, however I found an issue in the definition of “OAuthBearerOptions”. In an earlier post it was defined as:
app.UseOAuthAuthorizationServer (OAuthServerOptions);
app.UseOAuthBearerAuthentication (new OAuthBearerAuthenticationOptions ());
However when doing this integration of external logins is not applicable, since a static variable is required and not a new instance of “OAuthBearerAuthenticationOptions” in such a way that the implementation should be this way:
app.UseOAuthAuthorizationServer (OAuthServerOptions);
app.UseOAuthBearerAuthentication (OAuthBearerOptions);
Otherwise excellent work and again thank you very much 🙂
Thank you so much, Antonio, for your feedback, I will check your concerns as this has been written for a while, I will update the code is there is an issue based on your comment.
I confirm, this change has to be done. If not, there is an exception exception in the Controller in GenerateLocalAccessTokenResponse() when calling Startup.OAuthBearerOptions.AccessTokenFormat.Protect(ticket)
Thanks Taiseer, your articles are great.
Hello, In register page, How can we use facebook and google+ for register. Can you show an example ? Or update your codes.
Hello,
In the class
///
Public class GoogleAuthProvider: IGoogleOAuth2AuthenticationProvider
{
Public void ApplyRedirect (GoogleOAuth2ApplyRedirectContext context)
{
Context.Response.Redirect (context.RedirectUri)
}
///
When redirecting to https://accounts.google.com/o/oauth2/auth ?, the request header Origin is ‘null’ and I get a CORS policy error.
could you help me
Hi,
I have just finished implementing external logins into an API following your posts. I have actually used a mix of both of your blog series to implement my login flow (JWT Tokens + External providers). These posts have been very useful and informative for working through the OAuth process flow.
I was able to successfully get the Facebook external login working, but am getting an error on the Google login. I am successfully getting the Google prompts and authenticating, however, once I click the Allow button in the Google prompt, I always get back an “access_denied” error response. Have you seen this before? Any thoughts as to what might be causing the middleware failure?
Once again, great articles :).
Hello Aaron, I’m happy that posts were useful and still relevant after all this year.
Well I’m not sure why it is not working but double check your return URL and make sure it is the same registered URL in your Google Client Apps.
Hello,
Fantastic tutorial and it REALLY helped me out. Quick question: I am now having users input Username / Password on a front-end form, and these users have the option of logging in with Google. I have client applications on Android which are creating accounts without passwords or emails at the endpoint /api/Account/RegisterExternal. How can I associate or link these 2 accounts since they actually represent one user instead of creating 2 accounts?
Thank you again, fantastic guide,
Hi Rob,
Well this an implementation detail that should be handled by custom code, but I would check the email used in the external service and see if it was used in the local DB accounts, and create a mapping table between those 2 records to indicate that this 2 accounts belong to the same identity.
Hi, and thank you for your amazing work!
I have implemented a small variation, I would like to submit and get your feedback on the security perspective.
The idea is the following:
In your solution, when the user goes through the external login challenge, you read the external_token, send it back to the client (as querystring param), then the client submit it to obtain a local_token.
In my variation, when the user goes through the external login challenge, I generate a local_token and send it back to the client (as querystring param), so the client can use it without a second rountrip.
Of course I run all the validation steps (that you placed in the second round) before I generate the local token.
What do you think about it? Does this strategy add any security leaks?
Many thanks
Hi Carlo,
Well somehow you are building something close to the “Authroization code” flow, it is hard to confirm or deny if there are security flaws with this approach, but as long you are doing the validation steps and generating a random token each time then this looks acceptable.
hi ,
is refresh token supported in external providers ?
Hi Ayman,
Based on the tutorial I built I didn’t use refresh tokens with external identities, it doesn’t make sense to do this when you are logged in to your FB account. Assume that you have changed your FB credentials, then if you are using refresh tokens you will remain logged in while your FB account is not valid. That was my concern when writing this tutorial and thinking of refresh tokens with external accounts.
yes , i understand .
But i need refresh token cause i need to expire token after 30 minutes for example if user dose not obtain new access token (this for more security).
Please help me to obtain refresh token after login by external providers in accountApi
Awesome and very helpful tutorial. I have everything setup and working like it should, but have ran into another use case. When the user logs in or registers via an External Provider, I am not setting the refresh_token property. Therefore, when the access token expires, the ‘token’ (grant_type = refresh_token) endpoint is not able to create a new refresh token. Do you have any suggestions on how we could do this?
Hi William, thanks for your comment
Based on the tutorial I built I didn’t use refresh tokens with external identities, it doesn’t make sense to do this when you are logged in to your FB account. Assume that you have changed your FB credentials, then if you are using refresh tokens you will remain logged in while your FB account is not valid. That was my concern when writing this tutorial and thinking of refresh tokens with external accounts.
Great point. Providing users who login externally with a refresh token would allow them to be perpetually logged in to our site, even if they become unauthenticated with the external provider. So I guess from a usability standpoint, it is acceptable to require the user to periodically re-authenticate with the external provider. Thanks again for this post. I have referred several colleagues to it when setting up security for their websites.
Thanks William for the referral, please let me know if you need further help down the road 🙂
Thanks for the Tutorial. Have one Question, After the Local Access token expiry, how the client get new local access token?
Hi,
How can I make this work in Safari or in the browser used by facebook? When I am using Safari this is not working. Also, when I am in Facebook mobile app and I click a link to my app the facebook browser cannot handle this login.
Hi , Taiseer Joudeh
I am when i configured first time, it works fine.
After few days Facebook oauth returns an error , access_denied.
then i tried to add
context.Identity.AddClaim(new System.Security.Claims.Claim(“urn:facebook:email”, context.Email, ClaimValueTypes.Email, “Facebook”));
with reference to http://stackoverflow.com/questions/28503266/webapi-facebook-login-returning-access-denied-when-removing-permissions
still not working
i have changed url setting, still not working.
Hi there;
Thanks for the perfect step-by-step tutorial. It really is a good summary of several other complicated ones. I applied them to this step but encountered with an error at the end.
When I try sending an external login request, on first pass of step 7 it goes into unauthenticated and redirects to Facebook. But after Facebook login, it does not go back to GetExternalLogin action of Accounts controller. Instead, it returns an access_denied message. Do you have any suggestions for where to look at?
Regards;
y.
Hi Yusuf,
I’m not sure why is this happening but can you make sure using the same NuGet package I used so you can trace the issue, with the code in Github it should working perfectly.
Hi Taiseer;
The problem is related with recent upgrade of Facebook API from 2.2 to 2.3. .Net’s OWIN packages for Facebook will not work if they are older than v. 3.1.0-RC1. A bypass solution until a official release could be found here:
http://stackoverflow.com/questions/22364442/asp-net-mvc5-owin-facebook-authentication-suddenly-not-working
Applying that solution to your code works like a charm.
Again, thank you for the tutorial and your answer.
Regards;
y.
Also, updating all nuget packages to latest version including Microsoft.Owin.Security.Facebook (v3.1.0 as of April 10th, 2017) works fine. But solution above is not compatible with 3.1.0 version. I used new Nuget package on production.
y.
It is actually the contrary, since last FB update you need to upgrade to nuGet package 3.1
http://stackoverflow.com/questions/22364442/asp-net-mvc5-owin-facebook-authentication-suddenly-not-working/43063384#43063384
This same my case 🙂
Thanks mate, I update my nugget package and it becomes to work.
Hi Taiseer,
I am working on a similar implementation, but having client side mobile app (developed in AngularJS Cordova) and also webapp using AngularJS.
My concern is, if I call GetExternalLogin from my mobile app, it respond as a redirection to the external login provider’s login screen. Will it be a response to my mobile app and I have to handle that redirection?
I will try that out in next few days but wanted to know if you tried this. If GetExternalLogin redirects to my mobile’s browser, I will have no way to let my mobile app know that the external login is done.
I was thinking to let my mobile app call the external login provider’s login screen and receive the token back (using in-app browser) and then calling my API to validate that token, register user and obtain local token.
Do you have any suggestion on this?
Hi Hitesh, your assumption is correct, you need to use in-app browser for this to get the token back to the app and sent it for validation.
i’m developing login funcion via facebook but I get Access Denied error.It does not show any facebook page for me to interact.How can i find the problem and fix it?
Hi Taiser,
I was following this tutorial to integrate with linkedin. My observation is that once I get authentication code for my user from linkedin I will link him with my local Data base, Now what if the authenticated user changes his password in linked in ?The access token is granted every time with has local account =true when I call GetExternalLogin API. without asking user to login again with changed password. How to tackle such situation?
Thanks
Hi Taiser,
I was following this tutorial to integrate with linkedin. My observation is that once I get authentication code for my user from linkedin I will link him with my local Data base, Now what if the authenticated user changes his password in linked in ?The access token is granted every time with has local account =true when I call GetExternalLogin API. without asking user to login again with changed password. How to tackle such situation?
Thanks
Great article again, thanks for sharing.
I’ve noticed your comments on refreshtokens, but wondering on another use case. I’m logging in through another External Provider for the single sign-on experience and need the access tokens to do succeeding calls to that External system. The access token however expires within an hour, while the refreshtokens expires in a year.
Without storing any refresh tokens I need to re-authorize my External application everytime I try to ‘login’.
Any ideas on this one?
Is there any scenario where we would use refreshtokens in combination with facebook auth?
Is it even possible?
Thanks!
Good article, but as above,I am also faced a “access_denied” error when validate from google, and the code is downloaded from git-hub you referred in the article.
I am sure I have configured the client-Id and client-Secret right.
I hope you can try again.
Thank. You can also give me a suggestion for e-mail.
Thank again.
Hi Taiseer
It seems the source code doesn’t have external logins implemented.
Would it be possible for you to update it or point out source code that contains this part?
Thanks
Ryan.
Hi Taiseer,
Fantastic work!!!!
Helped me alot. I have implemented it in my work with few twists as per requirements but some how I am claiming for mobile number too but I get a null value. Can you help me out in this.
Thanks
Daman
Hi Daman,
Well, how did you set the claim for the mobile number? You need to inspect this area first.
Hi Taiseer,
Apologies for the late reply, below is code where I am trying to get claims:
private JObject GenerateLocalAccessTokenResponse(string userName)
{
string Mobile = “”;
string Email = “”;
var tokenExpiration = TimeSpan.FromDays(1);
ClaimsIdentity identity = new ClaimsIdentity(OAuthDefaults.AuthenticationType);
identity.AddClaim(new Claim(ClaimTypes.Name, userName));
identity.AddClaim(new Claim(ClaimTypes.Email, Email));
identity.AddClaim(new Claim(ClaimTypes.MobilePhone, Mobile));
identity.AddClaim(new Claim(“role”, “user”));
var props = new AuthenticationProperties()
{
IssuedUtc = DateTime.UtcNow,
ExpiresUtc = DateTime.UtcNow.Add(tokenExpiration),
};
…..
More over I am able to login to the facebook and getting also the username but somehow I need email address from the response but eventually its coming null, so please can you help me out in this.
Thanks
Daman
Hi Taiseer,
I got stuck with the claims please can you me out.
Hi Taiseer
Thanks a lot for this great article. Would you please also provide guides on how to write unit test with Moq and Effort library? I saw few articles on how to use Effort in memory DB with IdentityDbContext but there is extra parameter in constructor called DbCompiledModel which I am not sure how to pass or create for your app. I really appreciate your help on creating unit tests for the app.
Arash
this article is very help full for me to add external login for my site. thank you very much for providing clear, easy & accurate solution.
Very nice article. I want to ask one more thing. You have Registered User using only username.
user = new IdentityUser() { UserName = model.UserName };
IdentityResult result = await _repo.CreateAsync(user);
How can I register user with additional information. e.g. I am using Google Authentication and want to store user’s image, occupation, skills. I have customized my DB and ApplicationUser class with these additional properties. I am getting these properties in claims. But don’t know which function from your code to use to store these fields.
I tried the Demo Application link, both FB and Google login showing error.
I think You have hosted the demo without ClientId and ClientSecret.
Awesome
Facebook returns full name. Method RegisterExternal fails because first and last name are separated with space.
I added
private static readonly Regex rgx = new Regex(@”\s”, RegexOptions.Compiled);
and modified the procedure:
…
string noSpaces = rgx.Replace(model.UserName, “”);
user = new IdentityUser() { UserName = noSpaces };
AccountController.cs, line 139
Hey I am so grateful I found your webpage, I really found you by error, while I was searching on Yahoo for something else, Regardless
I am here now and would just like to say thank you for a remarkable post and a all round thrilling blog (I also love the theme/design),
I don’t have time to go through it all at the moment but I have bookmarked it and also added in your RSS feeds, so when I have time I will be back to read a lot more, Please do keep up the superb job.
Hi!
Quick question. What do I need to put on externalAccessToken? I don’t get it.
Hi,
thanks for your code, i try to implement it, i can make login but before i’m redirected to page
http://localhost:50785/signin-facebook?code=……
and not authcomplete.html
where is the problem?
Thanks in advance
Hi! First of all thank you for your posts, these are very clear and helpful.
I just want to ask you, what do you think about which layer should be responsible to GET/POST data from google/facebook APIs? For instance get profile image or post new comment.
Should be in the front end side (angularjs) or backend side (API)?
Thanks
Thanks for the perfect tutorial. I have implemented google Auth sucessfully and trying to add Miscrosoft Authentication. I din find the Access Token verification url for Microsoft. I can call the below url with access token which gives me the necessary details, But i dint find anyway to validate the token for the specific client.
https://graph.microsoft.com/v1.0/users/.
Please help me if you have any idea..
Thanks in advance
Subbiah K
Hi Taiseer,
I really find your tutorials on authentication useful, thank you! I have been struggling to do the same thing – Google login on a Web API for a few days now.
This has led me slightly I think in the right direction, the problem is I am using Core! Any plans for an updated post?
Thanks so much!!
Hi!
Thanks for the nice tutorial.
Could you maybe clarify this for me?
When you reload the angular app, how can you check if the user is still logged in into facebook in the browser?
Thanks.
Taiseer, thank you! This article helped me a lot.
Awesome
Hey. Thanks to the useful and practical article. It helped me a lot to understand token-based auth mechanism. I had implemented the solution exactly step by step based on article (with getting help of git source). But in GetExternalLogin I’m getting a 401 error page without any redirect information. It seems to me the line
Request.GetOwinContext().Authentication.Challenge(LoginProvider);
doesn’t do anything. Have you any idea please? Thanks in advance.Found the problem here:
Request.GetOwinContext().Authentication.Challenge(LoginProvider);
I was using LoginProvider = “google” (or “facebook”) as provider name, while the names must be capitalized (eg: “Google”)
LoginProvider is null
Hi Taiseer,3
I loved this article, i was searching a article like this from last three days, i have change my code some place after understanding your approach, I have done the same in Angular 5 with WEB API 2(Facebook and Google social login from Angular 5 App with WEB API). Its was a great integrated article. Its helped me aloooooot.
Thanks.
Finally for facebook login below article was so helpful, for debug purpose
https://developers.facebook.com/tools/accesstoken/
Hi Taiseer,
I loved this article, i was searching a article like this from last three days, i have change my code some place after understanding your approach, I have done the same in Angular 5 with WEB API 2(Facebook and Google social login from Angular 5 App with WEB API). Its was a great integrated article. Its helped me a lot.
Thanks.
Finally for facebook login below article was so helpful, for debug purpose
https://developers.facebook.com/tools/accesstoken/
Hi taiseer,
I got this article very much helpful but i have a doubt why i am not getting claims later on
How to get the claims by passing the bearer token after wards, because it generating the token up to that fine, but if i pass the bearer token to my web api func am getting Object reference not set to an instance of an object error. i know this error bcz something null, but why
angular call
var reqHeader = new HttpHeaders({‘Authorization’:’Bearer ‘+ localStorage.getItem(‘userToken’)});
return this._http.get(myGlobals.apiURL + ‘api/Account/GetUserClaims’, {headers : reqHeader});
my Api func
[Route(“GetUserClaims”)]
[HttpGet]
public ApplicationUser GetUserClaims()
{
var identityClaims = (ClaimsIdentity)User.Identity;
IEnumerable claims = identityClaims.Claims;
ApplicationUser model = new ApplicationUser()
{
Title = Convert.ToInt16(identityClaims.FindFirst(“Title”).Value),
UserName = identityClaims.FindFirst(“UserName”).Value,
FirstName = identityClaims.FindFirst(“FirstName”).Value,
LastName = identityClaims.FindFirst(“LastName”).Value,
PhoneNumber = identityClaims.FindFirst(“PhoneNumber”).Value,
PhoneNumberConfirmed = Convert.ToBoolean(identityClaims.FindFirst(“PhoneNumberConfirmed”).Value),
Email = identityClaims.FindFirst(“Email”).Value,
EmailConfirmed = Convert.ToBoolean(identityClaims.FindFirst(“EmailConfirmed”).Value),
CreatedDate = Convert.ToDateTime(identityClaims.FindFirst(“LoggedOn”).Value),
PasswordHash = “Test”,
};
return model;
}
even i have added all above details in the claims too
Normal calls and getting Claims details is fine
i think claims not added so here i am getting object ref error
var identityClaims = (ClaimsIdentity)User.Identity;
IEnumerable claims = identityClaims.Claims;
Please help me. Thanks in advance
Excellent article, very helpful…. It is the only article that explains this technology.
It is nice article. Explanation is very clear even beginners easy to understand
Hello Taiseer,
Thanks for the writeup and the source code, extremely helpful.
In our webapi project, we are already using Bearer Token based authentication enforced via AuthenticationFilter, and now need to add google/fb login. Do I still need to add the below in the StartUp Class, considering this is already working albeit a different way.
app.UseOAuthAuthorizationServer(OAuthServerOptions);
app.UseOAuthBearerAuthentication(OAuthBearerOptions);
But if i dont add above, will the pipeline for oauth still work correctly?
2. After the google oauth screen appears, the request on /Account/GetExtrenalLogin has an error of “access denied”. Any suggestion on what to check for that?
Hi,
I was able to make this work, however when users are using their mobile device it does not work. This seems to be incompatible with Safari and Facebook Browser. Do you have any idea how this can be fixed for mobile device users?