IdentityServer4 - missing claims from Google

Viewed 2863

TLDR; In the context of using IdentityServer4

  1. How do you get email address and hd claims from Google?
  2. How do you get User.Identity.Name to be populated?

I have worked through the IdentityServer quickstarts and have a working MVC client talking to a IdentityServer instance (apologies if using the wrong terminology). I am using External Authentication (Google) and do not have anything mildly complicated such as local logins / database etc. I am not using ASP.NET Identity. This is all working just fine.

I can successfully authenticate in my MVC app and the following code produces the claims in the screenshot below:

@foreach (var claim in User.Claims)
{
    <dt>@claim.Type</dt>
    <dd>@claim.Value</dd>
}
<dt>Identity.Name</dt>
<dd>&nbsp;@User.Identity.Name</dd>

<dt>IsAuthenticated</dt>
<dd>@User.Identity.IsAuthenticated</dd>

enter image description here

Questions:

  1. I cannot retrieve extra claims (right term?) from Google. Specifically 'hd' or even 'email' - note that they don't show up in the claims in the above screenshot. How do I get the email address and hd claims from Google? What am I missing or doing wrong?
  2. Note that the output of User.Identity.Name is empty. Why is this and how do I get this populated? This seems to be the only property of User.Identity that isn't set.

My setup is as follows - you can see the output of this as above:

Client (MVC)

In Startup.cs, ConfigureServices

JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();

services.AddAuthentication(options =>
{
    options.DefaultScheme = "Cookies";
    options.DefaultChallengeScheme = "oidc";
})
.AddCookie("Cookies")
.AddOpenIdConnect("oidc", options =>
{
    options.SignInScheme = "Cookies";
    options.Authority = Configuration["App:Urls:IdentityServer"];
    options.RequireHttpsMetadata = false;
    options.Resource = "openid profile email";
    options.Scope.Add("openid");
    options.Scope.Add("profile");
    options.Scope.Add("email");
    options.Scope.Add("domain");
    options.ClientId = "ctda-web";
    options.SaveTokens = true;
    options.GetClaimsFromUserInfoEndpoint = true;
});

Identity Server

Client definition

// OpenID Connect implicit flow client (MVC)
new Client
{
    ClientId = "ctda-web",
    ClientName = "Company To Do Web App",
    AllowedGrantTypes = GrantTypes.Implicit,
    EnableLocalLogin = false,

    // where to redirect to after login
    RedirectUris = { "http://localhost:53996/signin-oidc" },

    // where to redirect to after logout
    PostLogoutRedirectUris = { "http://localhost:53996/signout-callback-oidc" },

    AllowedScopes = new List<string>
    {
        IdentityServerConstants.StandardScopes.OpenId,
        IdentityServerConstants.StandardScopes.Profile,
        IdentityServerConstants.StandardScopes.Email,
        "domain"
    }
}

IdentityResource definition

return new List<IdentityResource>
{
    new IdentityResources.OpenId(),
    new IdentityResources.Profile(),
    new IdentityResources.Email(),
    new IdentityResource
    {
        Name = "domain",
        DisplayName = "Google Organisation",
        Description = "The hosted G Suite domain of the user, if part of one",
        UserClaims = new List<string> { "hd"}
    } 
};
2 Answers

I can tell you how to get email to be returned.

There are two ways to do this but they both require that you add the email scope to the initial request. Just sending openId isnt going to work.

Openid email

UserInfo request

Now when you get the access token back you can do

https://www.googleapis.com/oauth2/v1/userinfo?alt=json&access_token={access token}

Response

{
  "family_name": "Lawton", 
  "name": "Linda Lawton", 
  "picture": "https://lh5.googleusercontent.com/-a1CWlFnA5xE/AAAAAAAAAAI/AAAAAAAAl1I/UcwPajZOuN4/photo.jpg", 
  "gender": "female", 
  "email": "xxxx@gmail.com", 
  "link": "https://plus.google.com/+LindaLawton", 
  "given_name": "Linda", 
  "id": "117200475532672775346", 
  "verified_email": true
}

Token Info Request:

Using the id token

 https://www.googleapis.com/oauth2/v3/tokeninfo?id_token={token id}

response

{
 "azp": "07408718192.apps.googleusercontent.com",
 "aud": "07408718192.apps.googleusercontent.com",
 "sub": "00475532672775346",
 "email": "XX@gmail.com",
 "email_verified": "true",
 "at_hash": "8ON2HwraMXbPpP0Nwle8Kw",
 "iss": "https://accounts.google.com",
 "iat": "1509967160",
 "exp": "1509970760",
 "alg": "RS256",
 "kid": "d4ed62ee21d157e8a237b7db3cbd8f7aafab2e"
}

As to how to populate your controller i cant help with that.

Related