user1011627
user1011627

Reputation: 1811

AspNet User Claims Missing

Researching proof of concept for identity provider and need help understanding some nuances of aspnet identity, specifically related to user claims.

What I am trying to accomplish:

1) Expose an MVC app that provides secure access to 1 of 2 microservices that will expose sports and athlete data.

2) Allowing both local user accounts and external accounts (like google or facebook auth).

Current setup:

1) Asp.Net MVC app that leverages Identity Server 4 for authentication.

2) Two Web Api 2 web services (1 for sports and 1 for athletes). Sports API is not secure...ie...open API. The athlete API though should be secured via Policy based authorization.

3) Identity Server 4 web application, leveraging Entity Framework and ASP.NET Identity via IdentityServer4 nuget packages.

4) SQL Server database "fully" configured for api resources, identity resources, clients, a couple of test users with differing roles and claims to test with.

What works currently:

1) All Users can login (local and google)

2) Claims appear to be loading as I expect (still struggling to understand all the relationships of the claims tables in Identity Server data model).

3) MVC app shows id_token, access_token, refresh_token, and then loops through the claims for user and displays those.

What doesn't work:

1) Not all the claims that I believe should be shown in the mvc app are actually shown.

Example of what I mean:

1) In the first screenshot below, you can see given_name and family_name as claims for the user "bob". This is displayed using the following snippet of code in MVC razor:

enter image description here

@if (User.Identities.First().IsAuthenticated)
{
    <h1>Tokens:</h1>

    <dt>id token</dt>
    <dd>@await ViewContext.HttpContext.GetTokenAsync("id_token")</dd>

    <dt>access token</dt>
    <dd>@await ViewContext.HttpContext.GetTokenAsync("access_token")</dd>

    <dt>refresh token</dt>
    <dd>@await ViewContext.HttpContext.GetTokenAsync("refresh_token")</dd>

    <br />

    <h1>Claims:</h1>
    <dl>
        @foreach (var claim in User.Claims)
        {
            <dt>@claim.Type : @claim.Value</dt>
        }
    </dl>
}

2) In this second screenshot, you can see the other "claims" that are included in the access token that is generated by identity server. For example, "permission", "testing", "role", "testingroleclaim", and "oddball".

enter image description here

With the exception of a few pieces here and there, most of the code is straight from the quickstart tutorials from Identity Server 4 documentation. Based on everything I have read, in order to include custom claims, you need to implement your own instance of the IProfileService interface. I have done this and it appears to be working as expected. Here is the code and a screenshot showing the "other" claims being loaded in the debugger:

enter image description here

Here are the database tables and their entries:

1) Identity Resources/Identity Claims:

enter image description here

2) ApiResources/ApiScopes/ApiScopeClaims

enter image description here

3) AspNetUsers/AspNetRoles/AspNetUserClaims/AspNetRoleClaims/etc

enter image description here

Here is my question:

1) Why don't the claims that get loaded in the ProfileService::GetProfileDataAsync method get included in the User.Claims list that MVC is displaying?

UPDATE 1:

1) I updated the AlwaysIncludeUserClaimsInIdToken = True on the client table for the mvc app. The following things happened: a) The "oddball" claim is now displayed in the output in the MVC claims list. b) The claims displayed in the MVC app are now included in the id_token when they weren't before, but they still don't contain the "other" claims. enter image description here

UPDATE 2:

1) I figured out the answer to my original question. The reason the list is filtered is because the following line of code will internally filter based on the types of claims that exist in the RequestedClaimsType property. Now I need to understand why that list only has "name", "given_name", and "family_name" in it. Source for this info: https://github.com/IdentityServer/IdentityServer4/blob/master/src/IdentityServer4/src/Extensions/ProfileDataRequestContextExtensions.cs

context.AddRequestedClaims(claims);

Upvotes: 3

Views: 6844

Answers (1)

user1011627
user1011627

Reputation: 1811

Finally figured out what I was missing. The client application is responsible for setting the OpenIdConnect options when wiring up the openid middleware. One of the properties on the options object is called "ClaimActions". These claim actions allow a client to wire up custom mapping properties. Here is the documentation for that ClaimsActions.MapJsonKey property/method combination:

https://learn.microsoft.com/en-us/aspnet/core/security/authentication/social/additional-claims?view=aspnetcore-2.2#map-user-data-keys-and-create-claims

Here is the code I added to my client startup class to get this working for a set of claims that were serialized automatically.

services.AddAuthentication(options =>
{
   options.DefaultScheme = "Cookies";
   options.DefaultChallengeScheme = "oidc";
})
.AddCookie("Cookies")
.AddOpenIdConnect("oidc", options =>
{                    
    options.SignInScheme = "Cookies";

    options.Authority = "http://localhost:5000";
    options.RequireHttpsMetadata = false;

    options.ClientId = "mvc";
    options.ClientSecret = "secret";
    options.ResponseType = "code id_token";

    options.SaveTokens = true;
    options.GetClaimsFromUserInfoEndpoint = true;

    options.Scope.Add("athlete.full");
    options.Scope.Add("rights");
    options.Scope.Add("email");
    options.Scope.Add("address");
    options.Scope.Add("phone");

    options.Scope.Add("offline_access");

    // These are what allowed the claims to be serialized for front-end consumption.
    options.ClaimActions.MapJsonKey(JwtClaimTypes.WebSite, "website");
    options.ClaimActions.MapJsonKey(JwtClaimTypes.Gender, "gender");
    options.ClaimActions.MapJsonKey(JwtClaimTypes.BirthDate, "birthdate");
});

Upvotes: 5

Related Questions