Bit of Technology

  • Archive
  • About Me
    • Advertise
    • Disclaimer
  • Speaking
  • Contact

ASP.NET Web API 2 external logins with Facebook and Google in AngularJS app

August 11, 2014 By Taiseer Joudeh 460 Comments

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.

AngularJs External LoginsThere 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:

  1. 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)
  2. 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.
  3. The consent screen for Google will be shown, and the user will provide his Google credentials to authenticate.
  4. 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).
  5. 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.
  6. 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).
  7. 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).
  8. 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.
  9. 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:

C#
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:

C#
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:

AngularJS Facebook App AngularJS Google App

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:

C#
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:

C#
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);
        }
    }

C#
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:

C#
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:

C#
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:

C#
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:

C#
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:

C#
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:

C#
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:

C#
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:

C#
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:

C#
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:

C#
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:

External Login Request

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:

C#
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:

AngularJS Social Login

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:

AngularJS Social Login Associate

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

  1. Informative post by Brock Allen about External logins with OWIN middleware.
  2. Solid post by Dominick Baier on Web API Individual Accounts.
  3. Great post by Rick Anderson on VS 2013 MVC 5 template with individual accounts.
  4. Helpful post by Valerio Gheri on How to login with Facebook access token.

Related Posts

  • Integrate Azure AD B2C with ASP.NET MVC Web App – Part 3
  • Secure ASP.NET Web API 2 using Azure AD B2C – Part 2
  • Azure Active Directory B2C Overview and Policies Management – Part 1
  • ASP.NET Web API Claims Authorization with ASP.NET Identity 2.1 – Part 5
  • ASP.NET Identity 2.1 Roles Based Authorization with ASP.NET Web API – Part 4

Filed Under: AngularJS, ASP.NET, ASP.Net Web API, RESTful API, Single Page Applications, Uncategorized, Web API Tutorial Tagged With: AngularJS, ASP.NET, External Login, Facebook, Google, OAuth, Social Login, SPA, Token Authentication, Web API

Comments

  1. Nisar Mohammed says

    September 3, 2015 at 4:58 pm

    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.

    Reply
  2. Ryne YC Cheow says

    September 4, 2015 at 4:34 pm

    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/

    Reply
  3. Shevy says

    September 20, 2015 at 10:42 pm

    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!

    Reply
    • Taiseer Joudeh says

      September 21, 2015 at 2:45 pm

      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.

      Reply
  4. Juan H says

    October 15, 2015 at 2:44 pm

    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!

    Reply
    • Taiseer Joudeh says

      October 21, 2015 at 11:25 am

      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.

      Reply
  5. Dave says

    October 15, 2015 at 3:13 pm

    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

    Reply
  6. kelu says

    November 10, 2015 at 12:48 pm

    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?

    Reply
    • Taiseer Joudeh says

      November 11, 2015 at 6:46 pm

      Hi Kelu, Please check the comments as I already answered this question with deep details.

      Reply
      • Andrew Petersen says

        January 3, 2016 at 9:43 pm

        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?

        Reply
      • Tela says

        February 19, 2016 at 11:28 pm

        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

        Reply
      • James Hancock says

        March 2, 2016 at 7:06 pm

        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!

        Reply
        • Ravindra says

          March 6, 2016 at 6:36 am

          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

          Reply
          • Taiseer Joudeh says

            March 6, 2016 at 11:40 am

            Thank you for sharing this, I will review it soon and let you know if there are any comments.

          • Hoa.Ngo says

            October 23, 2016 at 10:12 pm

            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.

  7. nano says

    November 12, 2015 at 2:07 pm

    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

    Reply
    • Taiseer Joudeh says

      November 16, 2015 at 7:27 pm

      I need to check this, thanks for the heads up.

      Reply
      • Hoang says

        May 10, 2016 at 12:57 pm

        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);
        }

        Reply
    • Hines Vaughan says

      August 15, 2016 at 6:19 am

      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);
      ….
      }
      }

      Reply
  8. bryan says

    November 26, 2015 at 7:45 am

    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.

    Reply
    • Taiseer Joudeh says

      November 26, 2015 at 10:47 am

      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.

      Reply
      • bryan says

        November 27, 2015 at 3:31 am

        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

        Reply
        • bryan says

          December 2, 2015 at 8:10 am

          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..

          Reply
          • Roger says

            April 7, 2016 at 10:06 pm

            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

  9. Roger says

    December 2, 2015 at 3:00 pm

    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.

    Reply
    • Taiseer Joudeh says

      December 7, 2015 at 12:15 pm

      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.

      Reply
    • Graham Saunders says

      August 31, 2019 at 1:18 am

      Were you able to find a solution for Microsoft?

      Reply
  10. Shoaib says

    December 4, 2015 at 10:24 am

    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…..

    Reply
  11. Shoaib says

    December 5, 2015 at 8:35 am

    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…

    Reply
    • Shoaib says

      December 5, 2015 at 8:57 am

      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….

      Reply
      • Taiseer Joudeh says

        December 7, 2015 at 11:47 am

        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.

        Reply
        • Shoaib says

          December 10, 2015 at 1:10 pm

          Thank you so much… You are really a genius guy…Thank again…

          Reply
          • Netaji says

            September 25, 2016 at 10:43 pm

            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.

      • Alexander Helsinghof says

        November 6, 2016 at 2:54 am

        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

        Reply
    • Taiseer Joudeh says

      December 7, 2015 at 11:49 am

      Hi Shoaib,

      You issue in the Startup class, make sure you are setting it like this LOC

      Reply
    • Alexander Helsinghof says

      November 6, 2016 at 2:52 am

      How did you solve this?

      Reply
  12. Ramya Jambunath says

    December 17, 2015 at 10:49 am

    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

    Reply
    • Taiseer Joudeh says

      December 24, 2015 at 3:14 am

      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%

      Reply
  13. ghazanfarseyal says

    December 25, 2015 at 1:29 pm

    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??

    Reply
    • Taiseer Joudeh says

      January 10, 2016 at 1:35 am

      Nop, you can not ever get facebook password, you need to obtain an access token from FB to keep user logged in.

      Reply
  14. Andrew Petersen says

    January 3, 2016 at 9:49 pm

    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.

    Reply
  15. Jérôme says

    January 5, 2016 at 11:35 pm

    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

    Reply
  16. Raouf Hammami says

    January 7, 2016 at 2:27 am

    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.

    Reply
    • Taiseer Joudeh says

      January 10, 2016 at 1:25 am

      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.

      Reply
      • Sergey says

        August 7, 2016 at 9:34 pm

        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..

        Reply
      • Sergey says

        August 7, 2016 at 9:48 pm

        update…

        i am using a Microsoft.AspNet.WebApi.OwinSelfHost

        Reply
        • Sergey says

          August 7, 2016 at 10:22 pm

          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.

          Reply
  17. Bunsing says

    January 9, 2016 at 12:47 am

    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

    Reply
    • Taiseer Joudeh says

      January 10, 2016 at 1:21 am

      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.

      Reply
  18. Daudi Husbands says

    January 11, 2016 at 2:09 am

    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

    Reply
  19. riccardo says

    February 4, 2016 at 7:59 pm

    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

    Reply
    • Taiseer Joudeh says

      February 7, 2016 at 11:41 pm

      Happy to hear it is working, I prefer to start from scratch and add the packages needed, its a cleaner approach.

      Reply
    • Arman says

      June 30, 2016 at 3:59 am

      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.

      Reply
      • Taiseer Joudeh says

        July 10, 2016 at 11:08 am

        Hi Arman,
        What is the problem you are facing please?

        Reply
      • riccardo says

        July 10, 2016 at 12:45 pm

        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

        Reply
        • D says

          September 8, 2016 at 4:05 am

          Please Riccardo, share us your project

          Reply
  20. Ebuka says

    February 16, 2016 at 3:41 pm

    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.

    Reply
    • Taiseer Joudeh says

      February 17, 2016 at 3:38 pm

      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.

      Reply
  21. Thanh says

    February 19, 2016 at 1:48 pm

    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

    Reply
  22. greggum says

    February 20, 2016 at 7:59 pm

    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

    Reply
    • Taiseer Joudeh says

      February 22, 2016 at 11:54 am

      Thanks for sharing this, I will check how you can use the FB JS SDK to implement this.

      Reply
  23. Siva says

    February 23, 2016 at 8:20 pm

    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!

    Reply
    • Taiseer Joudeh says

      March 4, 2016 at 12:09 am

      That is strange, this happening locally and if you published the Api to a server?

      Reply
  24. Joe Hoeller says

    February 23, 2016 at 10:10 pm

    SO how do you migrate it? Where is the dB we can seed?

    Reply
  25. ragia says

    February 24, 2016 at 4:38 pm

    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.

    Reply
    • Taiseer Joudeh says

      March 4, 2016 at 12:03 am

      You it will work, as well you can use the FB SDK to obtain FB access tokens.

      Reply
      • paps says

        April 26, 2016 at 6:56 pm

        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

        Reply
        • Taiseer Joudeh says

          April 27, 2016 at 1:42 am

          Hi Paps, it should be the same, as well you can benefit from the FB or Google SDKs to enable social logins.

          Reply
          • Mohsen says

            October 27, 2016 at 12:14 am

            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)?

  26. Tony says

    February 24, 2016 at 10:14 pm

    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.

    Reply
    • Taiseer Joudeh says

      March 4, 2016 at 12:02 am

      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

      Reply
      • Jeff Shelby says

        March 23, 2016 at 11:38 pm

        Actually, the missing piece appears to be several lines later: https://github.com/tjoudeh/AngularJSAuthentication/blob/master/AngularJSAuthentication.API/Startup.cs#l54

        Reply
  27. Jeff Shelby says

    March 25, 2016 at 4:35 pm

    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?

    Reply
  28. Ole H says

    March 29, 2016 at 12:57 pm

    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?

    Reply
  29. Amit says

    April 6, 2016 at 7:10 am

    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.

    Reply
  30. Jacson Brandão says

    April 7, 2016 at 11:16 pm

    Excellent tutorial.

    How do I add the columns “Picture ” and ” FullName ” and also fill in the e- mail ?

    Reply
    • Taiseer Joudeh says

      April 19, 2016 at 9:02 pm

      You should require those in Facebook scopes so you obtain a FB token which allows you to get this data.

      Reply
  31. André says

    April 12, 2016 at 9:04 pm

    what it will be my redirect_uri if I call it from my android app ?

    Reply
  32. Robert Green says

    May 31, 2016 at 6:37 pm

    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);
    }

    Reply
    • Taiseer Joudeh says

      May 31, 2016 at 11:07 pm

      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

      Reply
  33. David says

    June 23, 2016 at 10:18 am

    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

    Reply
  34. David says

    June 23, 2016 at 11:56 am

    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

    Reply
  35. David says

    June 27, 2016 at 10:05 am

    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

    Reply
  36. Stelios Kornelakis says

    June 28, 2016 at 12:43 am

    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.

    Reply
  37. Stelios Kornelakis says

    June 28, 2016 at 12:47 am

    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”

    Reply
  38. Rajendra K says

    June 30, 2016 at 10:58 am

    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

    Reply
    • Taiseer Joudeh says

      July 10, 2016 at 11:08 am

      Hello,
      You need to register this client in the OauthClients table. Did you try to debug the code and made sure this client exists?

      Reply
  39. John says

    July 18, 2016 at 10:39 am

    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?

    Reply
    • Taiseer Joudeh says

      July 18, 2016 at 5:00 pm

      John, thanks for your comment. Please share your solution when you have time 🙂

      Reply
  40. Diego says

    August 21, 2016 at 3:16 pm

    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!

    Reply
  41. Andrey says

    September 20, 2016 at 5:30 pm

    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?

    Reply
    • Taiseer Joudeh says

      September 22, 2016 at 9:09 am

      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.

      Reply
      • Andrey says

        October 7, 2016 at 1:41 pm

        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?

        Reply
  42. Martin says

    September 27, 2016 at 11:34 am

    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.

    Reply
    • Martin says

      September 27, 2016 at 12:50 pm

      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.

      Reply
      • Taiseer Joudeh says

        October 2, 2016 at 1:52 pm

        Thanks for sharing the answer and apologise for not replying quickly.

        Reply
  43. Karlo says

    October 25, 2016 at 2:43 am

    “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.

    Reply
  44. Alexander Helsinghof says

    November 9, 2016 at 12:41 am

    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?

    Reply
    • Taiseer Joudeh says

      November 12, 2016 at 12:29 am

      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 🙁

      Reply
  45. Dil says

    December 8, 2016 at 8:58 am

    Is it Possible to do this without asp.net identity and EFM and with Ado.net

    Reply
    • Taiseer Joudeh says

      December 15, 2016 at 5:05 pm

      Yes you can use any ORM you like to get/post data.

      Reply
  46. Satish Somani says

    December 9, 2016 at 4:15 pm

    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

    Reply
  47. Dung Nguyen says

    December 14, 2016 at 7:24 pm

    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!

    Reply
  48. Sanjog Sharma says

    December 14, 2016 at 8:38 pm

    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??

    Reply
    • Graham Saunders says

      August 31, 2019 at 1:15 am

      were able to figure this out for Microsoft?

      Reply
  49. Usman Khalid says

    January 4, 2017 at 11:30 am

    From where I can get the Email address of the user using oAuth following your approach?

    Reply
    • Taiseer Joudeh says

      January 8, 2017 at 9:36 pm

      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.

      Reply
  50. John says

    January 20, 2017 at 3:02 pm

    I am getting null exception on this line of code,

    var accessToken = Startup.OAuthBearerOptions.AccessTokenFormat.Protect(ticket);

    Can you please, Help me out.

    Reply
    • Taiseer Joudeh says

      January 23, 2017 at 12:34 am

      Hi Jhon,
      Check how you are instantiating this object in this LOC

      Reply
  51. Antonio N says

    January 20, 2017 at 7:55 pm

    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 🙂

    Reply
    • Taiseer Joudeh says

      January 23, 2017 at 12:36 am

      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.

      Reply
  52. Asraf says

    January 23, 2017 at 2:39 pm

    Hello, In register page, How can we use facebook and google+ for register. Can you show an example ? Or update your codes.

    Reply
  53. Juan Manuel says

    January 23, 2017 at 6:29 pm

    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

    Reply
  54. Aaron B says

    February 7, 2017 at 7:29 am

    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 :).

    Reply
    • Taiseer Joudeh says

      February 14, 2017 at 1:18 am

      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.

      Reply
  55. Rob says

    February 10, 2017 at 1:04 am

    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,

    Reply
    • Taiseer Joudeh says

      February 14, 2017 at 1:36 am

      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.

      Reply
  56. Carlo Saccone says

    February 14, 2017 at 11:59 am

    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

    Reply
    • Taiseer Joudeh says

      March 5, 2017 at 2:53 am

      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.

      Reply
  57. Ayman says

    February 14, 2017 at 2:42 pm

    hi ,
    is refresh token supported in external providers ?

    Reply
    • Taiseer Joudeh says

      March 5, 2017 at 2:59 am

      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.

      Reply
      • ayman says

        April 23, 2017 at 7:08 pm

        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

        Reply
  58. William says

    February 15, 2017 at 9:34 pm

    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?

    Reply
    • Taiseer Joudeh says

      March 5, 2017 at 2:58 am

      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.

      Reply
      • William says

        March 8, 2017 at 8:22 pm

        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.

        Reply
        • Taiseer Joudeh says

          March 11, 2017 at 12:50 am

          Thanks William for the referral, please let me know if you need further help down the road 🙂

          Reply
          • Subbiah says

            October 30, 2017 at 1:57 am

            Thanks for the Tutorial. Have one Question, After the Local Access token expiry, how the client get new local access token?

  59. Jester says

    February 27, 2017 at 3:59 pm

    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.

    Reply
  60. Ganesh Loke says

    March 29, 2017 at 1:46 pm

    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.

    Reply
  61. Yusuf says

    April 1, 2017 at 7:35 pm

    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.

    Reply
    • Taiseer Joudeh says

      April 8, 2017 at 6:19 pm

      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.

      Reply
    • Nico says

      April 28, 2017 at 9:52 am

      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 🙂

      Reply
      • Ed says

        January 24, 2018 at 7:48 am

        Thanks mate, I update my nugget package and it becomes to work.

        Reply
  62. Hitesh Sharma says

    April 5, 2017 at 4:56 am

    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?

    Reply
    • Taiseer Joudeh says

      April 8, 2017 at 6:09 pm

      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.

      Reply
  63. Raksha says

    April 9, 2017 at 8:42 pm

    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

    Reply
  64. Ryan says

    April 28, 2017 at 6:12 am

    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.

    Reply
  65. Daman says

    June 12, 2017 at 11:17 am

    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

    Reply
    • Taiseer Joudeh says

      June 13, 2017 at 12:47 am

      Hi Daman,
      Well, how did you set the claim for the mobile number? You need to inspect this area first.

      Reply
      • Daman says

        June 23, 2017 at 8:48 pm

        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

        Reply
      • Daman says

        June 26, 2017 at 8:23 pm

        Hi Taiseer,

        I got stuck with the claims please can you me out.

        Reply
  66. Himalaya Garg says

    July 2, 2017 at 12:03 pm

    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.

    Reply
    • Himalaya Garg says

      July 2, 2017 at 4:33 pm

      I tried the Demo Application link, both FB and Google login showing error.

      Reply
      • Himalaya Garg says

        July 2, 2017 at 5:28 pm

        I think You have hosted the demo without ClientId and ClientSecret.

        Reply
  67. Harun says

    July 6, 2017 at 7:04 pm

    Awesome

    Reply
  68. may tinh de ban cau hinh cao gia re says

    August 9, 2017 at 2:16 pm

    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.

    Reply
  69. kevin says

    August 16, 2017 at 12:21 am

    Hi!
    Quick question. What do I need to put on externalAccessToken? I don’t get it.

    Reply
  70. Alessio says

    August 26, 2017 at 10:39 am

    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

    Reply
  71. Guillermo Subirán says

    August 31, 2017 at 1:35 pm

    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

    Reply
  72. Subbiah says

    November 7, 2017 at 6:01 pm

    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

    Reply
  73. Nicole says

    November 20, 2017 at 9:58 pm

    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?

    Reply
  74. Fermin says

    December 3, 2017 at 9:33 pm

    Thanks so much!!

    Reply
  75. Rick says

    December 29, 2017 at 9:36 pm

    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.

    Reply
  76. Sergei Averkiev says

    January 16, 2018 at 10:07 pm

    Taiseer, thank you! This article helped me a lot.

    Reply
  77. jon says

    March 7, 2018 at 12:11 pm

    Awesome

    Reply
  78. Amiry says

    March 20, 2018 at 2:15 am

    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.

    Reply
    • Tran Thanh Son says

      February 21, 2020 at 1:26 pm

      LoginProvider is null

      Reply
  79. Hazrat Ali says

    March 23, 2018 at 3:46 pm

    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/

    Reply
  80. Hazrat Ali says

    March 23, 2018 at 3:51 pm

    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/

    Reply
  81. Hazrat ali says

    March 25, 2018 at 1:44 pm

    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

    Reply
  82. Thiruchelvam Karmegam says

    December 1, 2021 at 4:29 pm

    It is nice article. Explanation is very clear even beginners easy to understand

    Reply
  83. Vandana says

    May 11, 2022 at 6:32 am

    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?

    Reply
  84. Vandana says

    May 11, 2022 at 6:34 am

    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/GetExternalLogin has an error of “access denied”. Any suggestion on what to check f

    Reply
  85. Jester Han Tolosa says

    February 28, 2017 at 4:11 am

    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?

    Reply
« Older Comments

Trackbacks

  1. How to make a Asp.Net WebAPI 2 SPA (AngularJS) and Google Calendar API work together? - Quorai says:
    December 16, 2015 at 3:43 am

    […] have an ASP.NET WebAPI 2 SPA with AngularJS, based on this sample, with social logins working already. The Google API’s Authentication docs just states […]

    Reply
  2. Web API authentication using Facebook and Google – Thirul says:
    April 18, 2016 at 11:12 am

    […]   webapi google and facebook with angular:  https://bitoftech.net/2014/08/11/asp-net-web-api-2-external-logins-social-logins-facebook-google-angu&#8230;   angular and web api facebook authenication […]

    Reply
  3. Social authentication with Google account on auto generated Web API 2 code - How to Code .NET says:
    June 1, 2017 at 8:39 pm

    […] demo from this article does not allow registering from google account, and does not work: ASP.NET Web API 2 external logins with Facebook and Google in AngularJS app. Please don’t cite this […]

    Reply

Leave a Reply Cancel reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.

About Taiseer

Husband, Father, Consultant @ MSFT, Life Time Learner... Read More…

Buy me a coffeeBuy me a coffee

Recent Posts

  • Integrate Azure AD B2C with ASP.NET MVC Web App – Part 3
  • Secure ASP.NET Web API 2 using Azure AD B2C – Part 2
  • Azure Active Directory B2C Overview and Policies Management – Part 1
  • ASP.NET Web API Claims Authorization with ASP.NET Identity 2.1 – Part 5
  • ASP.NET Identity 2.1 Roles Based Authorization with ASP.NET Web API – Part 4

Blog Archives

Recent Posts

  • Integrate Azure AD B2C with ASP.NET MVC Web App – Part 3
  • Secure ASP.NET Web API 2 using Azure AD B2C – Part 2
  • Azure Active Directory B2C Overview and Policies Management – Part 1
  • ASP.NET Web API Claims Authorization with ASP.NET Identity 2.1 – Part 5
  • ASP.NET Identity 2.1 Roles Based Authorization with ASP.NET Web API – Part 4

Tags

AJAX AngularJS API API Versioning ASP.NET ASP.Net 5 Authentication Autherization Server Azure Active Directory Azure Active Directory B2C Azure AD B2C basic authentication Code First Dependency Injection Documentation Entity Framework Entity Framework 7 Facebook Foursquare API Google Authenticator Identity jQuery JSON JSON Web Tokens JWT MVC 6 Ninject OAuth OData Resource Server REST RESTful RESTful. Web API Single Page Applications SPA Swagger-ui Swashbuckle TFA Token Authentication Tutorial Two Factor Authentication Web API Web API 2 Web API Security Web Service

Search

Copyright © 2022 · eleven40 Pro Theme on Genesis Framework · WordPress · Log in