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

LOST picture LOST · Apr 9, 2016 · Viewed 10k times · Source

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);

Answer

LOST picture LOST · Apr 11, 2016

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.