1
votes

I'm using the IdentityServer4 "AspNetCoreAndApis" sample application found here

It has a token server and an MVC client application.

The identity server project has an external OIDC authentication provider set up using their demo server - https://demo.identityserver.io/

After hitting a protected endpoint in MvcClient, being redirected to the local identity server, choosing and authenticating with the demo server, it reaches the ExternalController callback of the local identity server. At this point I would like to issue additional claims to the user, and have them be available in MvcClient.

There's code in the callback to addadditionalLocalClaims and issue a cookie. I tried adding another claim:

var additionalLocalClaims = new List<Claim>();
additionalLocalClaims.Add(new Claim("TestKey", "TestValue"));
await HttpContext.SignInAsync(user.SubjectId, user.Username, provider, localSignInProps, additionalLocalClaims.ToArray());

But by the time the user arrives in the HomeController of MvcClient this claim is not there.

I think I don't properly understand which authentication scheme is being used where, and the function of the relevant cookies.

EDIT:

In response to the first comment below, I tried attaching a claim to a requested scope, but still no luck - this is the in memory resource store:

public static IEnumerable<ApiResource> Apis
    {
        get
        {
            var apiResource = new ApiResource("api1", "My API");
            apiResource.UserClaims.Add("TestKey");
            var resources = new List<ApiResource>
            {
                apiResource
            };
            return resources;
        }
    }

The MvcClient is both allowed the api1 scope, and requests it.

1
That should work, but you also have to allow the user to receive the claim by attaching it to a scope that the user requests. E.g. if this is intended as an access token claim, you could add the claim type to the api1 ApiResource (assuming the user is requesting the api1 scope).sellotape
I was really thought that would work, but I'm afraid it didn't. I edited my question to include your suggestion.user888734
Couple of things to understand that might help you solve it: first is - as implied in my first comment - you can add any reasonable number of claims the way you have, but they should be viewed as available claims. The actual claims issued will depend on your configuration of IS4 (scopes, APIs, clients, Identity Resources, etc).sellotape
Second is the difference between identity-tokens and access-tokens. The first is received by your UI, usually in a clear-text JWT, and is available to your UI. The second is intended for the API you target, and is usually (or at least should be viewed as) opaque to your UI. You just pass it from UI to API to get access, and it's usually only the API that can see the actual claims. If you're looking for the new claim to appear in the first, you should add it to the matching IdentityResource, instead of ApiResource.sellotape
BTW you also called your claim "TestKey" when added, but added "CustomKey" to the ApiResource...sellotape

1 Answers

0
votes

Your client MVC could get the user's custom claims from ID token or UserInfo endpoint .

To add claims to ID token , you can set client's config :AlwaysIncludeUserClaimsInIdToken . But involve all user claims in ID token is not recommended concern about the size of ID Token .

A better solution is making your client app get user's claims from UserInfo endpoint :

public class MyProfileService : IProfileService
{
    public MyProfileService()
    { }

    public Task GetProfileDataAsync(ProfileDataRequestContext context)
    {

        var claims = new List<Claim>()
        {

            new Claim("TestKey", "TestValue")
        };
        context.IssuedClaims.AddRange(claims);
        return Task.CompletedTask;
    }

    public Task IsActiveAsync(IsActiveContext context)
    {
        // await base.IsActiveAsync(context);
        return Task.CompletedTask;
    }
}

Register in DI :

services.AddTransient<IProfileService, MyProfileService>();

The IProfileService service could be used to add claims to ID Token, Access token and UserInfo endpoint . By default the custom claims won't involve in ID Token event using IProfileService , the reason explained above - the ID token size . So you can make your client app get claims from UserInfo endpoint with OIDC middleware config :

options.Scope.Add("profile");
options.GetClaimsFromUserInfoEndpoint = true;
options.ClaimActions.MapJsonKey("TestKey", "TestKey");

Above codes will add OIDC profile permission to get claims from endpoint , and send a request to connect/userinfo endpoint with ID Token , and get claims and map claim whose name is TestKey to your client's claim principle and save to cookie . Now you can get the claims with User.Claims in MVC .