Search code examples
c#oauthasp.net-core-mvc.net-core.net-core-2.0

AddOAuth linkedin dotnet core 2.0


I'm using dotnet core I want to setup a LinkedIn authentication on the site since there is no default authentication builder for LinkedIn as facebook, google and twitter I decided to use the generic implementation as follows:

services.AddAuthentication().AddOAuth("LinkedIn", 
            c =>
            {
                c.ClientId = Configuration["linkedin-app-id"];
                c.ClientSecret = Configuration["linkedin-app-secret"];
                c.Scope.Add("r_basicprofile");
                c.Scope.Add("r_emailaddress");
                c.CallbackPath = "/signin-linkedin";
                c.AuthorizationEndpoint = "https://www.linkedin.com/oauth/v2/authorization";
                c.TokenEndpoint = "https://www.linkedin.com/oauth/v2/accessToken";
                c.UserInformationEndpoint = "https://api.linkedin.com/v1/people/~:(id,formatted-name,email-address,picture-url)";
})

I'm having an issue because GetExternalLoginInfoAsync() is null, looking the Identity ASP.net core source, is because the providerkey is null.

Taken from asp.net core code:

var providerKey = auth.Principal.FindFirstValue(ClaimTypes.NameIdentifier);
var provider = items["LoginProvider"] as string;
if (providerKey == null || provider == null)
{
   return null;
}

the question is where can I add the ClaimTypes.NameIdentifier to the LinkedIn claim?


Solution

  • In this case, you have to pre populate each Claim manually using an OauthEvent like this:

    .AddOAuth("LinkedIn", 
                c =>
                {
                    c.ClientId = Configuration["linkedin-app-id"];
                    c.ClientSecret = Configuration["linkedin-app-secret"];
                    c.Scope.Add("r_basicprofile");
                    c.Scope.Add("r_emailaddress");
                    c.CallbackPath = "/signin-linkedin";
                    c.AuthorizationEndpoint = "https://www.linkedin.com/oauth/v2/authorization";
                    c.TokenEndpoint = "https://www.linkedin.com/oauth/v2/accessToken";
                    c.UserInformationEndpoint = "https://api.linkedin.com/v1/people/~:(id,formatted-name,email-address,picture-url)";
                    c.Events = new OAuthEvents
                    {
                        OnCreatingTicket = async context =>
                        {
                            var request = new HttpRequestMessage(HttpMethod.Get, context.Options.UserInformationEndpoint);
                            request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", context.AccessToken);
                            request.Headers.Add("x-li-format", "json");
    
                            var response = await context.Backchannel.SendAsync(request, context.HttpContext.RequestAborted);
                            response.EnsureSuccessStatusCode();
                            var user = JObject.Parse(await response.Content.ReadAsStringAsync());
    
                            var userId = user.Value<string>("id");
                            if (!string.IsNullOrEmpty(userId))
                            {
                                context.Identity.AddClaim(new Claim(ClaimTypes.NameIdentifier, userId, ClaimValueTypes.String, context.Options.ClaimsIssuer));
                            }
    
                            var formattedName = user.Value<string>("formattedName");
                            if (!string.IsNullOrEmpty(formattedName))
                            {
                                context.Identity.AddClaim(new Claim(ClaimTypes.Name, formattedName, ClaimValueTypes.String, context.Options.ClaimsIssuer));
                            }
    
                            var email = user.Value<string>("emailAddress");
                            if (!string.IsNullOrEmpty(email))
                            {
                                context.Identity.AddClaim(new Claim(ClaimTypes.Email, email, ClaimValueTypes.String,
                                    context.Options.ClaimsIssuer));
                            }
                            var pictureUrl = user.Value<string>("pictureUrl");
                            if (!string.IsNullOrEmpty(pictureUrl))
                            {
                                context.Identity.AddClaim(new Claim("profile-picture", pictureUrl, ClaimValueTypes.String,
                                    context.Options.ClaimsIssuer));
                            }
                        }
                    };
    
                })