Alexei - check Codidact
Alexei - check Codidact

Reputation: 23078

Identity server issues AuthenticationScheme: Bearer was challenged for a token obtained by client credentials. How to find out the underlying error?

I have trouble authenticating some integration tests requests when working with Identity Server 4 (ASP.NET Core 3.1).

My setup is as follows:

Identity server configuration

public static IEnumerable<IdentityResource> GetIdentityResources()
{
    return new List<IdentityResource>
    {
        new IdentityResources.OpenId(),
        new IdentityResources.Email(),
        new IdentityResources.Profile(),
    };
}

public static IEnumerable<ApiResource> GetApiResources()
{
    return new List<ApiResource>
    {
        new ApiResource("resourceapi", "Resource API")
        {
            Scopes = {new Scope("api.read")}
        }
    };
}

public static IEnumerable<Client> GetClients()
{
    return new[]
    {
        new Client
        {
            RequireConsent = false,
            ClientId = "MY_CLIENT_ID",
            ClientName = "My Client Name",
            // code is required for SPA, client credentials for test runner
            AllowedGrantTypes = GrantTypes.CodeAndClientCredentials,
            AllowedScopes = {"openid", "profile", "email", "api.read"},
            RedirectUris = {"http://localhost:4201/auth-callback"},
            PostLogoutRedirectUris = {"http://localhost:4201/"},
            AllowedCorsOrigins = {"http://localhost:4201"},
            AllowAccessTokensViaBrowser = true,
            AccessTokenLifetime = 3600,
            RequireClientSecret = false
        }
    };
}


services.AddIdentity<AppUser, IdentityRole>()
    .AddEntityFrameworkStores<AppIdentityDbContext>()
    .AddDefaultTokenProviders();

services.AddIdentityServer()
    .AddDeveloperSigningCredential()
    // this adds the operational data from DB (codes, tokens, consents)
    .AddOperationalStore(options =>
    {
        options.ConfigureDbContext = builder => builder.UseSqlServer(Configuration.GetConnectionString("Default"));
        // this enables automatic token cleanup. this is optional.
        options.EnableTokenCleanup = true;
        options.TokenCleanupInterval = 30; // interval in seconds
    })
    //.AddInMemoryPersistedGrants()
    .AddInMemoryIdentityResources(Config.GetIdentityResources())
    .AddInMemoryApiResources(Config.GetApiResources())
    .AddInMemoryClients(Config.GetClients())
    .AddAspNetIdentity<AppUser>();

ASP.NET Core client

// this is called from Startup.ConfigureServices
public static void ConfigureSecurity(this IServiceCollection services, IConfiguration configuration)
{
    services.AddAuthentication(options =>
    {
        options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
        options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
    }).AddJwtBearer(o =>
    {
        o.Authority = configuration.GetSection("Idam").GetValue<string>("BaseUrl");  // http://localhost:54916
        o.Audience = configuration.GetSection("Idam").GetValue<string>("Audience");  // "resourceapi"
        o.RequireHttpsMetadata = false;
    });

    services.AddAuthorization();
}

Integration tests code

var client = new HttpClient();
var tokenResponse = await client.RequestClientCredentialsTokenAsync(new ClientCredentialsTokenRequest
{
    Address = $"{IdentityServerUrl}/connect/token",
    ClientId = "MY_CLIENT_ID",
    ClientSecret = IdentityServerPass,
    Scope = "api.read"

}).ConfigureAwait(false);
tokenResponse.HttpResponse.EnsureSuccessStatusCode();

Here I receive a bearer token, but does not seem to be accepted (Identity Server issues the error below). It looks like the following:

{
  "nbf": 1587392198,
  "exp": 1587395798,
  "iss": "http://localhost:54916",
  "aud": "resourceapi",
  "client_id": "STACKOVERFLOW_METRO_MIRROR",
  "scope": [
    "api.read"
  ]
}


> IdentityServer4.Hosting.IdentityServerMiddleware: Information:
> Invoking IdentityServer endpoint:
> IdentityServer4.Endpoints.TokenEndpoint for /connect/token
> IdentityServer4.Validation.TokenRequestValidator: Information: Token
> request validation success, {   "ClientId":
> "STACKOVERFLOW_METRO_MIRROR",   "ClientName": "My Client Name",
> "GrantType": "client_credentials",   "Scopes": "api.read",   "Raw": {
>     "grant_type": "client_credentials",
>     "scope": "api.read",
>     "client_id": "MY_CLIENT_ID",
>     "client_secret": "***REDACTED***"   } } Microsoft.AspNetCore.Hosting.Diagnostics: Information: Request
> starting HTTP/1.1 GET
> http://localhost:44324/api/GeneralData/GetAllTags  
> Microsoft.AspNetCore.ResponseCaching.ResponseCachingMiddleware:
> Information: No cached response available for this request.
> Microsoft.AspNetCore.Authorization.DefaultAuthorizationService:
> Information: Authorization failed.
> Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerHandler:
> Information: AuthenticationScheme: Bearer was challenged.

Since my setup also includes a SPA that is able to successfully authenticate (login via Identity Server login form -> get token -> API uses token with success), I decrypted such a token to see if I see any relevant difference that might reveal what I am missing for the testing authentication flow:

{
  "nbf": 1587393059,
  "exp": 1587396659,
  "iss": "http://localhost:54916",
  "aud": "resourceapi",
  "client_id": "MY_CLIENT_ID",
  "sub": "fd351b3b-dfb2-4f2f-8987-af9d23c9dc6e",
  "auth_time": 1587393055,
  "idp": "local",
  "given_name": "test",
  "email": "[email protected]",
  "scope": [
    "openid",
    "email",
    "profile",
    "api.read"
  ],
  "amr": [
    "pwd"
  ]
}

ASP.NET Core Web API Startup.cs

public void ConfigureServices(IServiceCollection services)
{
    services.ConfigureCustomServices();

    services.ConfigureSettings(Configuration);
    services.ConfigureSecurity(Configuration);
    services.ConfigureMvc();
    services.BindLogging();

    services.ConfigureRedisCache(Configuration);
    services.ConfigureApiExplorer();
    services.AddHttpContextAccessor();

    services.AddDbContext(Configuration);
    ConfigureAuditNet();

    services.AddCorsAndPolicy();

    services.ConfigureHangfire(Configuration);

    services.AddSignalR();
    services.AddAutoMapper(typeof(QuestionProfile).Assembly);
    services.AddHealthChecks();

    services
        .AddControllers();
}

public void Configure(IApplicationBuilder app, IWebHostEnvironment env,
    ILoggingService logger, IHostApplicationLifetime lifetime, IServiceProvider serviceProvider,
    ISoApiDailyRequestInfoService soApiDailyRequestInfoService)
{
    app.UseResponseCaching();

    app.UseMiddleware<ResponseTimeMiddleware>();

    app.ProtectHangfireDashboard();

    app.ConfigureExceptionPage(env);

    app.StartHangFireJobs(serviceProvider, Configuration);

    ConfigureApplicationLifetime(logger, lifetime, soApiDailyRequestInfoService);

    app.UseHttpsRedirection();

    app.UseRouting();
    app.UseCors("CorsPolicy");

    app.UseAuthentication();
    app.UseAuthorization();
    app.EnsureAppUserMiddleware();

    app.UseEndpoints(endpoints =>
    {
        endpoints.MapControllers();

        endpoints.MapHub<PostHub>("/post");
        endpoints.MapHealthChecks("/health");
    });

    app.ConfigureAuditMiddleware();
    app.UseSwagger();
}

Unfortunately, the Identity server provides a very generic error and I do see what I missing here.

Question: Identity server issues AuthenticationScheme: Bearer was challenged for a token obtained by client credentials. How to find out the underlying error?

Upvotes: 2

Views: 6134

Answers (1)

Anupam Maiti
Anupam Maiti

Reputation: 245

In stratup.cs Configure method , Please make sure that sequence of app.Use... are correct.

E.g. app.UseAuthentication(); before app.UseMvc()

Upvotes: 2

Related Questions