Reputation: 23078
I have trouble authenticating some integration tests requests when working with Identity Server 4 (ASP.NET Core 3.1).
My setup is as follows:
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>();
// 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();
}
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"
]
}
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
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