Search code examples
identityserver4

GetUserInfoAsync returns only sub without other claims


Why does GetUserInfoAsync return only sub without other claims?

var discoveryResponse = client.GetDiscoveryDocumentAsync("some Authorization Url").Result;
var userInfoResponse = client.GetUserInfoAsync(new UserInfoRequest
{
    Address = discoveryResponse.UserInfoEndpoint,
    Token = token // access_token
}).Result;

After signed in I have in the response 'email' but when I call GetUserInfoAsync I don't have it. I pass to GetUserInfoAsync access_token maybe that? Because claims are in id_token but how I can return claims from GetUserInfoAsync in that case?

My code:

I have on the list of the IdentityResource 'email':

public static IEnumerable<IdentityResource> GetIdentityResources()
{
    return new List<IdentityResource>
    {
        new IdentityResources.OpenId(),
        new IdentityResources.Email()
    };
}

In the client I have 'email' and 'AlwaysIncludeUserClaimsInIdToken':

return new Client
{
    ClientId = clientId,
    AllowedGrantTypes = GrantTypes.Implicit,
    AllowAccessTokensViaBrowser = true,
    RedirectUris = { "some url" },
    PostLogoutRedirectUris = { "some url" },
    AlwaysIncludeUserClaimsInIdToken = true,
    AllowedScopes = new List<string>
    {
        IdentityServerConstants.StandardScopes.OpenId,
        "email"
    }
};

I pass scopes in the SignInAsync method:

await _accessor.HttpContext.SignInAsync(subject, new Claim(ClaimNames.Email, email));

In the requested scopes I have:

scope: 'openid email'

Solution

  • UserInfo endpoint requires authorization with access_token having at least openid scope, which is transformed into the sub claim in response. All the rest is optional. That is by the spec.

    Now let's see how that's arranged in IdentityServer 4. Everything related to access_token (intended to be used by APIs) is grouped into ApiResource configuration. That's the only place where you can configure the API scopes and their claims. After introducing a scope, you may add it to the list of accessible for a particular client. Then, client side you may request it explicitly. ApiResource configuration might look a bit messy as it has additional fields such as API credentials for Introspection endpoint access, but the constructor we need to fetch some UseInfo data is extremely simple:

    new ApiResource("UserInfo", new []{JwtClaimTypes.Email, JwtClaimTypes.GivenName})
    

    With the code above we created the ApiResource "UserInfo" with the scope "UserInfo" and a couple associated user claims.

    All the same and more, from the first hand here