0

I am currently trying to integrate my client and API applications with Duende IdentityServer but I am encountering issues when it comes to authorizing the API.

After I get the access token from the Identity Server and attach it to the HttpClient so that I can make a call to my API endpoint which is marked [Authorize] I am getting a 401 status code and I am not sure why.

-> This is how my Identity Server app configuration looks like:

    public static IEnumerable<IdentityResource> IdentityResources =>
        new IdentityResource[]
        { 
            new IdentityResources.OpenId(),
            new IdentityResources.Profile(),

            // -> adding the "roles" claim as part of the IdToken
            new IdentityResource(
                "roles",
                "Your role(s)",
                new [] { "role" })
        };

    public static IEnumerable<ApiResource> ApiResources =>
        new ApiResource[]
        {
            new ApiResource("hcmapi",
                            "Human Capital Management API")
            {
                Scopes = { "hcmapi.fullaccess" }
            }
        };

    public static IEnumerable<ApiScope> ApiScopes =>
        new ApiScope[]
        { 
            new ApiScope("hcmapi.fullaccess")
        };

    public static IEnumerable<Client> Clients =>
        new Client[] 
            {
                new Client()
                {
                    ClientName = "Human Capital Management Client",
                    ClientId = "hcmClient",
                    ClientSecrets =
                    {
                        new Secret("secret".Sha256())
                    },
                    AllowedGrantTypes = GrantTypes.Code,

                    // I did not want to reveal my URLs, hence the naming for the RedirectUris, but in my app they have the correct names
                    RedirectUris =
                    {
                        redirectUri
                    },
                    PostLogoutRedirectUris =
                    {
                        postLogoutRedirectUri 
                    },
                    AllowedScopes =
                    {
                        IdentityServerConstants.StandardScopes.OpenId,
                        IdentityServerConstants.StandardScopes.Profile,
                        "roles",
                        "hcmapi.fullaccess",
                        "offline_access"
                    },
                    AllowOfflineAccess = true,
                    RequireConsent = true
                }
            };

-> This is how my API's (Asp NET Core 6 web API) Program.cs class looks like:

builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
            .AddJwtBearer(options =>
            {
                options.Authority = builder.Configuration["IdentityServerAppUrl"];
                options.Audience = "hcmapi";
                options.TokenValidationParameters = new() 
                {
                    NameClaimType = "given_name",
                    RoleClaimType = "role",
                    ValidTypes= new[] { "aj+jwt" } 
                }; 
            });

-> This is how my Client (Blazor Server App) Program.cs class looks like:

builder.Services.AddAccessTokenManagement();

        builder.Services.AddHttpClient("HcmAPI", config =>
        {
            config.BaseAddress = new Uri(builder.Configuration["DevelopmentApiURL"]);
            config.DefaultRequestHeaders.Clear();
        })
            .AddUserAccessTokenHandler();

        JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();

        builder.Services
            .AddAuthentication(options =>
            {
                options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
                options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
            })
            .AddCookie(CookieAuthenticationDefaults.AuthenticationScheme)
            .AddOpenIdConnect(OpenIdConnectDefaults.AuthenticationScheme, options =>
            {
                options.SignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
                options.Authority = authorityUrl;
                options.ClientId = "hcmClient";
                options.ClientSecret = "secret";
                options.ResponseType = "code";
                options.SaveTokens = true;
                options.GetClaimsFromUserInfoEndpoint = true;

                options.ClaimActions.Remove("aud");
                options.ClaimActions.DeleteClaim("sid");
                options.ClaimActions.DeleteClaim("idp");

                options.Scope.Add("openid");
                options.Scope.Add("profile");
                options.Scope.Add("roles");
                options.Scope.Add("hcmapi.fullaccess");
                options.Scope.Add("offline_access");

                options.ClaimActions.MapJsonKey("role", "role");

                options.TokenValidationParameters = new()
                {
                    NameClaimType = "given_name",
                    RoleClaimType = "role"
                };
            });

-> These are the scopes that are present in the access_token: "scope": [ "openid", "profile", "roles", "hcmapi.fullaccess" ],

-> Inside one of my Client app's pages, I am doing the following:

protected async override Task OnInitializedAsync()
    {
        string apiAccessToken = await HttpContextAccessor.HttpContext!
            .GetTokenAsync(OpenIdConnectParameterNames.AccessToken);

        HttpEntitiesService.SetHttpClient(apiAccessToken!);

        await HandleRetrievingEmployees();
  
        StateHasChanged();
    }

Inside Blazor in the OnInitializedAsync method I retrieve the accessToken from Identity server and attach it as a Bearer token to the instance of the HttpClient I am creating.

However, if I try to use that instance of HttpClient to make a request to an endpoint which is marked with [Authorize], which is what await HandleRetrievingEmployees() does it returns a status code 401, and I do not understand why.

Could you look over and tell me what am I missing? If I have to provide more information/code let me know. I tried to be as specific and also concise as possible. Thank you in advance!

0 Answers0