How to make IdentityServer to add user identity to the access token?

11,533

Hooray, I found an answer, when I stumbled upon this page: https://github.com/IdentityServer/IdentityServer3.Samples/issues/173

Apparently, user identity is passed in "sub" claim in the access token. Because I blindly copied API sample, its configuration included

JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();

which essentially prevented my API from mapping "sub" claim to nameidentifier. After removing this line, HttpContext.User.GetUserId() of authenticated controller returns user ID correctly.

Share:
11,533
LOST
Author by

LOST

Updated on June 05, 2022

Comments

  • LOST
    LOST almost 2 years

    Short: My client retrieves an access token from IdentityServer sample server, and then passes it to my WebApi. In my controller, this.HttpContext.User.GetUserId() returns null (User has other claims though). I suspect access token does not have nameidentity claim in it. How do I make IdentityServer include it?

    What I've tried so far:

    • switched from hybrid to implicit flow (random attempt)
    • in IdSvrHost scope definition I've added

      Claims = { new ScopeClaim(ClaimTypes.NameIdentifier, alwaysInclude: true) }

    • in IdSvrHost client definition I've added

      Claims = { new Claim(ClaimTypes.NameIdentifier, "42") }

    (also a random attempt)

    I've also tried other scopes in scope definition, and neither of them appeared. It seems, that nameidentity is usually included in identity token, but for most public APIs I am aware of, you don't provide identity token to the server.

    More details: IdSrvHost and Api are on different hosts. Controller has [Authorize]. In fact, I can see other claims coming. Api is configured with

    JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();
    
    app.UseIdentityServerAuthentication(options => {
        options.Authority = "http://localhost:22530/";
    
        // TODO: how to use multiple optional scopes?
        options.ScopeName = "borrow.slave";
        options.AdditionalScopes = new[] { "borrow.receiver", "borrow.manager" };
    
        options.AutomaticAuthenticate = true;
        options.AutomaticChallenge = true;
    });
    

    Scope:

    public static Scope Slave { get; } = new Scope {
        Name = "borrow.slave",
        DisplayName = "List assigned tasks",
        Type = ScopeType.Resource,
    
        Claims = {
            new ScopeClaim(ClaimTypes.NameIdentifier, alwaysInclude: true),
        },
    };
    

    And client:

    new Client {
        ClientId = "borrow_node",
        ClientName = "Borrow Node",
    
        Flow = Flows.Implicit,
    
        RedirectUris = new List<string>
        {
            "borrow_node:redirect-target",
        },
    
        Claims = { new Claim(ClaimTypes.NameIdentifier, "42") },
    
        AllowedScopes = {
            StandardScopes.OpenId.Name,
            //StandardScopes.OfflineAccess.Name,
            BorrowScopes.Slave.Name,
        },
    }
    

    Auth URI:

    request.CreateAuthorizeUrl(
                clientId: "borrow_node",
                responseType: "token",
                scope: "borrow.slave",
                redirectUri: "borrow_node:redirect-target",
                state: state,
                nonce: nonce);
    

    and I also tried

    request.CreateAuthorizeUrl(
                clientId: "borrow_node",
                responseType: "id_token token",
                scope: "openid borrow.slave",
                redirectUri: "borrow_node:redirect-target",
                state: state,
                nonce: nonce);