When using HttpClient with .AddClientAccessTokenHandler() extension, role claim is not included

9k Views Asked by At

I am using IdentityModel.AspNetCore and .AddClientAccessTokenHandler() extension to automatically supply HttpClient with access token (at least that is what I understand I can use it for) to an API. Some API endpoints are authorized based on a role. But for some reason, the access token that is added to the request does not contain the role claim. If I do not use the .AddClientAccessTokenHandler() and manually retrieve the token and set it using SetBearerToken(accessTone) then I can reach my role authorized endpoint.

My startup is:

services.AddAccessTokenManagement(options =>
{
    options.Client.Clients.Add("auth", new ClientCredentialsTokenRequest
    {
        Address = "https://localhost:44358/connect/token", 
        ClientId = "clientId",
        ClientSecret = "clientSecret",
    });
});

WebApi call:

var response = await _httpClient.GetAsync("api/WeatherForecast/SecretRole");

Identity server configuration:

public static IEnumerable<ApiResource> GetApis() =>
    new List<ApiResource>
    {
        new ApiResource("WebApi", new string[] { "role" })
            { Scopes = { "WebApi.All" }}
    };

public static IEnumerable<ApiScope> GetApiScopes() =>
    new List<ApiScope>
        { new ApiScope("WebApi.All") };

public static IEnumerable<IdentityResource> GetIdentityResources() =>
    new List<IdentityResource>
    {
        new IdentityResources.OpenId(),
        new IdentityResources.Profile(),
        new IdentityResource 
        {
            Name = "roles",
            UserClaims = { "role" }
        }
    };

public static IEnumerable<Client> GetClients() =>
    new List<Client>
    {
        new Client
        {
            ClientId = "clientId",
            ClientSecrets = { new Secret("clientSecret".ToSha256()) },
            AllowedGrantTypes = 
            { 
                GrantType.AuthorizationCode, 
                GrantType.ClientCredentials
            },
            AllowedScopes =
            {
                "WebApi.All",                        
                IdentityServerConstants.StandardScopes.OpenId,
                IdentityServerConstants.StandardScopes.Profile,
                "roles"
            },
            RedirectUris = { "https://localhost:44305/signin-oidc" },
            PostLogoutRedirectUris  = { "https://localhost:44305/Home/Index" },
            AlwaysIncludeUserClaimsInIdToken = false,
            AllowOfflineAccess = true,
        }
    };

For testing purposes I add users manually from Program.cs

public static void Main(string[] args)
{
    var host = CreateHostBuilder(args).Build();
    using (var scope = host.Services.CreateScope())
    {
        var userManager = scope.ServiceProvider.GetRequiredService<UserManager<IdentityUser>>();
        AddUsers(userManager).GetAwaiter().GetResult();
    }
    host.Run();
}

private static async Task AddUsers(UserManager<IdentityUser> userManager)
{
    var adminClaim = new Claim("role", "Admin");
    var visitorClaim = new Claim("role", "Visitor");

    var user = new IdentityUser("Admin");
    await userManager.CreateAsync(user, user.UserName);
    await userManager.AddClaimAsync(user, adminClaim);

    user = new IdentityUser("Visitor");
    await userManager.CreateAsync(user, user.UserName);
    await userManager.AddClaimAsync(user, visitorClaim);
}

So if I use manual access token retrieval and add it myself to the HttpClient headers, then my endpoint is reached and returns expected response. If I use .AddClientAccessTokenHandler(), I get 403 - Forbidden. What am I missing?

1

There are 1 best solutions below

0
On BEST ANSWER

Since you are registering the client under the name auth, you also should retrieve it as such.

This basically means I expect you to use something like this, or it's equivalent:

_httpClient = factory.CreateClient("auth");

Basically this mechanism ensures you're able to retrieve HttpClients for various API's and settings.

ps. I am on mobile; and currently not very good access to my resources.