Search code examples
c#asp.net-coreblazorblazor-webassemblyduende-identity-server

Blazor WebAssembly App with Individual Accounts and ASP.NET Core Hosted - Resource Owner Password Credentials grant - Does not get role claim


I have set up Resource Owner Password Credentials grant using the answer below:

https://stackoverflow.com/a/67324222/3850405

I have then followed this guide to set up name and role claim:

https://learn.microsoft.com/en-us/aspnet/core/blazor/security/webassembly/hosted-with-identity-server?view=aspnetcore-6.0&tabs=visual-studio#name-and-role-claim-with-api-authorization

Everything works well but with normal Authorization Code Grant but with ROPC I do not get neither name or role claim.

The reason we are not using Client Credentials Grant can be seen here:

https://github.com/dotnet/aspnetcore/issues/44122

ProfileService.cs:

using IdentityModel;
using Duende.IdentityServer.Models;
using Duende.IdentityServer.Services;
using System.Threading.Tasks;

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

    public async Task GetProfileDataAsync(ProfileDataRequestContext context)
    {
        var nameClaim = context.Subject.FindAll(JwtClaimTypes.Name);
        context.IssuedClaims.AddRange(nameClaim);

        var roleClaims = context.Subject.FindAll(JwtClaimTypes.Role);
        context.IssuedClaims.AddRange(roleClaims);

        await Task.CompletedTask;
    }

    public async Task IsActiveAsync(IsActiveContext context)
    {
        await Task.CompletedTask;
    }
}

Startup.cs (Program.cs in .NET6):

services.AddIdentityServer()
    .AddApiAuthorization<ApplicationUser, ApplicationDbContext>(options =>
    {
        options.Clients.AddIdentityServerSPA("WebApplication4.Client", builder =>
        {
            builder.WithRedirectUri("/authentication/login-callback");
            builder.WithLogoutRedirectUri("/authentication/logout-callback");
        });
        //Or Duende.IdentityServer.Models.Client
        options.Clients.Add(new IdentityServer4.Models.Client
        {
            ClientId = "WebApplication4.Integration",
            AllowedGrantTypes = { GrantType.ResourceOwnerPassword },
            //Use Configuration.GetSection("MySecretValue").Value; to get a value from appsettings.json
            ClientSecrets = { new Secret("MySecretValue".Sha256()) },
            AllowedScopes = { "WebApplication4.ServerAPI", "openid", "profile" }
        });
    });

services.AddTransient<IProfileService, ProfileService>();

JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Remove("role");

Solution

  • Turned out to be a configuration issue.

    https://learn.microsoft.com/en-us/aspnet/core/blazor/security/webassembly/hosted-with-identity-server?view=aspnetcore-6.0&tabs=visual-studio#configure-identity-server

    Microsoft says Use one of the following approaches: but using a Profile Service like public class ProfileService : IProfileServic only works with Authorization Code Grant and not Resource Owner Password Credentials. Removing the ProfileService and instead using this code everything worked:

    services.AddIdentityServer()
        .AddApiAuthorization<ApplicationUser, ApplicationDbContext>(options => {
            options.IdentityResources["openid"].UserClaims.Add("name");
            options.ApiResources.Single().UserClaims.Add("name");
            options.IdentityResources["openid"].UserClaims.Add("role");
            options.ApiResources.Single().UserClaims.Add("role");
        });
    
    JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Remove("role");