Blazor IdentityServer Authenthentication

Currently I have three separate servers. Client on :5001, API on :5002 and IdentityServer on :5003. I can athenticate my Blazor pages using @attribute [Authorize] but when I call the API I get a 401 error. If I past the token_id into postman and make a request to the API server it authenticates. If I make a request from my Blazor client it fails. I have whitelisted CORS to rule out that being the issue. If I remove the Audience check on the api with:

                    options.TokenValidationParameters = new TokenValidationParameters
                    {
                        ValidateAudience = false
                    };

It works

Client program.cs

            builder.Services.AddHttpClient("api")
                .AddHttpMessageHandler(sp => 
                {
                    var handler = sp.GetService<AuthorizationMessageHandler>()
                        .ConfigureHandler(
                            authorizedUrls: new[] { "https://localhost:5002" },
                            scopes: new[] { "coredesk" });
                    return handler;
                });
            
            builder.Services.AddScoped(
                sp => sp.GetService<IHttpClientFactory>().CreateClient("api"));
            
            builder.Services.AddOidcAuthentication(options =>
            {
                builder.Configuration.Bind("oidc", options.ProviderOptions);
            });

Client appsettings.json

{
  "oidc": {
    "Authority": "https://localhost:5003/",
    "ClientId": "coredesk",
    "DefaultScopes": [
      "openid",
      "profile",
      "coredesk"
    ],
    "PostLogoutRedirectUri": "/",
    "ResponseType": "code"
  }
}

API Startup.cs

            services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
                .AddJwtBearer(options =>
                {
                    options.Authority = "https://localhost:5003";
                    options.Audience = "coredesk";
                });

IdentityServer Config.cs

        public static IEnumerable<IdentityResource> IdentityResources =>
            new IdentityResource[]
            {
                new IdentityResources.OpenId(),
                new IdentityResources.Profile(),
            };
        
        public static IEnumerable<ApiResource> Apis =>
            new ApiResource[]
            {
                new ApiResource("coredesk", "CoreDesk API")
            };

        public static IEnumerable<ApiScope> ApiScopes =>
            new ApiScope[]
            {
                new ApiScope("coredesk"),
            };

        public static IEnumerable<Client> Clients =>
            new Client[]
            {
                new Client
                {
                    ClientId = "coredesk",
                    AllowedGrantTypes = GrantTypes.Code,
                    RequirePkce = true,
                    RequireClientSecret = false,
                    AllowedCorsOrigins = { "https://localhost:5001", "https://localhost:5002" },
                    AllowedScopes = { "openid", "profile", "coredesk" },
                    RedirectUris = { "https://localhost:5001/authentication/login-callback" },
                    PostLogoutRedirectUris = { "https://localhost:5001/" },
                    Enabled = true
                },
            };

2 answers

  • answered 2021-01-17 17:41 Tore Nestenius

    if you look at the source code for AddJwtBearer you find this part:

                    // If no authorization header found, nothing to process further
                    if (string.IsNullOrEmpty(authorization))
                    {
                        return AuthenticateResult.NoResult();
                    }
    
                    if (authorization.StartsWith("Bearer ", StringComparison.OrdinalIgnoreCase))
                    {
                        token = authorization.Substring("Bearer ".Length).Trim();
                    }
    
                    // If no token found, no further work possible
                    if (string.IsNullOrEmpty(token))
                    {
                        return AuthenticateResult.NoResult();
                    }
    

    this shows that by default (without customization) it requires the token to be provided in the Authorization header, like this:

    GET /api/payments HTTP/1.1
    Host: localhost:7001
    Authorization: Bearer eyJhbGciOiJSUzI1NiIsIYwA...
    

    to further debug, I would remove the authorize attribute and then put a breakpoint in one of the action method and examine what the ClaimsPrincipal user object contains.

  • answered 2021-01-17 18:22 enet

    In the Client appsettings.json

    Change: "ClientId": "coredesk" into "ClientId": "blazor"

    And in IdentityServer Config.cs

    Change: ClientId = "coredesk" into ClientId = "blazor",

    Try and report...