Denny Ferrassoli
Denny Ferrassoli

Reputation: 1725

Get name and email claim using IdentityServer from Google auth (SPA)

I'm new to using Identity Server for SPA auth but I started following this example: Authentication and authorization for SPAs and with some tinkering I've now also added Google auth. However, I'm having trouble getting the external Google claims merged into my application's claims (for example: given_name).

I've verified that Google does send back the appropriate claims but nothing seems to map those claims, e.g. options.ClaimActions.MapJsonKey(ClaimTypes.GivenName, "given_name");. When I access one of my protected endpoints my claims do not include any of the additional google claims.

I did find some additional documentation Persist additional claims... which tells me to add the claim in OnPostConfirmationAsync (Account/ExternalLogin.cshtml.cs) but since this is an SPA that page doesn't exist. Is there another approach to this? I haven't been able to find much that doesn't use the Page / OnPostConfirmationAsync.

Thanks

Including relevant details from my Startup.cs in case I'm doing something wrong here:

I've tried a few different variants from other examples I've found but

    services
        .AddDefaultIdentity<AppUser>(options => options.SignIn.RequireConfirmedAccount = true)
        .AddRoles<AppRole>()
        .AddEntityFrameworkStores<ApplicationDbContext>()
        .AddDefaultTokenProviders();

    services.AddIdentityServer()
        .AddApiAuthorization<AppUser, ApplicationDbContext>();

    services
        .AddAuthentication()
        .AddIdentityServerJwt()
        .AddGoogle(options =>
        {
            options.ClientId = Configuration["Auth:Google:ClientId"];
            options.ClientSecret = Configuration["Auth:Google:ClientSecret"];

            options.AuthorizationEndpoint += "?prompt=consent"; // Hack so we always get a refresh token, it only comes on the first authorization response
            options.AccessType = "offline";
            options.SaveTokens = true;

            options.Scope.Add("https://www.googleapis.com/auth/userinfo.email");
            options.Scope.Add("https://www.googleapis.com/auth/userinfo.profile");

            options.ClaimActions.MapJsonKey(ClaimTypes.GivenName, "given_name");
        })

And my api is simply:

[Authorize()]
[Route("test")]
public IActionResult Test()
{
    var all = User.Claims.Select(s => $"{s.Type}: {s.Value}");
    return Ok(all);
}

Upvotes: 2

Views: 1463

Answers (1)

Denny Ferrassoli
Denny Ferrassoli

Reputation: 1725

The only way I've been able to handle this is to scaffold the needed ExternalLogin page (like @d_f mentioned in the question's comments) and then continue following the Persist additional claims steps. I was hoping I could just set a collection of claims to keep in my Startup file and it would work but Identity Server just uses its internal ExternalLogin (Microsoft.AspNetCore.Identity.UI.V4.Pages.Account.Internal) and the code there doesn't handle adding external claims. This works for now but I would prefer a way of not needing to scaffold the ExternalLogin page.

I tried asking for clarification on the official docs but my question was closed (instead they suggested I ask on StackOverflow - lol). However there seems to be some work being done on improving the docs and flowing the claims through, if interested you can dig through this GitHub issue: https://github.com/dotnet/AspNetCore.Docs/issues/16488

Upvotes: 2

Related Questions