I keep getting the following error between postman and IdentityServer 4
Microsoft.IdentityModel.Tokens.SecurityTokenInvalidAudienceException: IDX10214: Audience validation failed. Audiences: 'empty'. Did not match: validationParameters.ValidAudience: 'MyNumberV2Api' or validationParameters.ValidAudiences: 'null'.
at Microsoft.IdentityModel.Tokens.Validators.ValidateAudience(IEnumerable`1 audiences, SecurityToken securityToken, TokenValidationParameters validationParameters)
at System.IdentityModel.Tokens.Jwt.JwtSecurityTokenHandler.ValidateAudience(IEnumerable`1 audiences, JwtSecurityToken jwtToken, TokenValidationParameters validationParameters)
at System.IdentityModel.Tokens.Jwt.JwtSecurityTokenHandler.ValidateTokenPayload(JwtSecurityToken jwtToken, TokenValidationParameters validationParameters)
at System.IdentityModel.Tokens.Jwt.JwtSecurityTokenHandler.ValidateToken(String token, TokenValidationParameters validationParameters, SecurityToken& validatedToken)
at Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerHandler.HandleAuthenticateAsync()
Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerHandler: Information: BearerIdentityServerAuthenticationJwt was not authenticated. Failure message: IDX10214: Audience validation failed. Audiences: 'empty'. Did not match: validationParameters.ValidAudience: 'MyNumberV2Api' or validationParameters.ValidAudiences: 'null'.
IdentityServer4.AccessTokenValidation.IdentityServerAuthenticationHandler: Information: Bearer was not authenticated. Failure message: IDX10214: Audience validation failed. Audiences: 'empty'. Did not match: validationParameters.ValidAudience: 'MyNumberV2Api' or validationParameters.ValidAudiences: 'null'.
Microsoft.AspNetCore.Authorization.DefaultAuthorizationService: Information: Authorization failed.
Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerHandler: Information: AuthenticationScheme: BearerIdentityServerAuthenticationJwt was challenged.
IdentityServer4.AccessTokenValidation.IdentityServerAuthenticationHandler: Information: AuthenticationScheme: Bearer was challenged.
Microsoft.AspNetCore.Hosting.Diagnostics: Information: Request finished in 1140.9671ms 401
The program '[12792] iisexpress.exe: Program Trace' has exited with code 0 (0x0).
The program '[12792] iisexpress.exe' has exited with code -1 (0xffffffff)..
public void ConfigureServices(IServiceCollection services)
{
services.AddIdentityServer(
options => {
options.Events.RaiseErrorEvents = true;
options.Events.RaiseInformationEvents = true;
options.Events.RaiseFailureEvents = true;
options.Events.RaiseSuccessEvents = true;
options.IssuerUri = "http://localhost:5000";
}
)
.AddDeveloperSigningCredential()
.AddInMemoryApiResources(Config.GetAllApiResources())
.AddInMemoryClients(Config.GetClients())
//.AddInMemoryIdentityResources(Config.GetIdentityResources())
.AddInMemoryApiScopes(Config.GetApiScopes());
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseIdentityServer();
app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.MapGet("/", async context =>
{
await context.Response.WriteAsync("Hello World!");
});
});
}
public void ConfigureServices(IServiceCollection services)
{
services.AddAuthentication("Bearer")
.AddIdentityServerAuthentication(options =>
{
options.Authority = "http://localhost:5000";
options.RequireHttpsMetadata = false;
options.ApiName = "MyNumberV2Api";
});
#region AddAuthentication
services.AddDbContext<MyNumberV2.Data.MyNumberV2Context>(options =>
options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
IdentityModelEventSource.ShowPII = true;
services.AddScoped<IAdminUserRepository, AdminUserRepository>();
services.AddCors(options =>
{
options.AddPolicy("Open", builder => builder.AllowAnyOrigin().AllowAnyHeader());
});
services.AddMvcCore();
services.AddControllers();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthorization();
app.UseAuthentication();
app.UseCors("Open");
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
Here is my public repo for the full solution code: https://github.com/zachion/blazor-auth
Here is how I generate an auth token in postman:
And here is the body section of my request to get the token where i add the Grant type and Scope.
I get the token from the response and add it to the follow up requests for trying to get to the actual api's controllers.
Here is how I add the auth token in postman. Issuing the token works fine
Here's the complete post man collection I use:
{
"info": {
"_postman_id": "089a85df-ae4b-41c3-8d1e-9d2e4ff8f7c8",
"name": "MYNumberV2.Api Copy",
"schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json"
},
"item": [
{
"name": "Generate Tokent API One",
"request": {
"auth": {
"type": "basic",
"basic": [
{
"key": "password",
"value": "secret",
"type": "string"
},
{
"key": "username",
"value": "client",
"type": "string"
}
]
},
"method": "POST",
"header": [],
"body": {
"mode": "urlencoded",
"urlencoded": [
{
"key": "grant_type",
"value": "client_credentials",
"type": "text"
},
{
"key": "scope",
"value": "MyNumberV2Api",
"type": "text"
}
],
"options": {
"urlencoded": {}
}
},
"url": {
"raw": "http://localhost:5000/connect/token",
"protocol": "http",
"host": [
"localhost"
],
"port": "5000",
"path": [
"connect",
"token"
]
}
},
"response": []
},
{
"name": "api/adminuser",
"protocolProfileBehavior": {
"disableBodyPruning": true
},
"request": {
"auth": {
"type": "noauth"
},
"method": "GET",
"header": [
{
"key": "Authorization",
"type": "text",
"value": "Bearer eyJhbGciOiJSUzI1NiIsImtpZCI6IjNENDZERDNFQ0NGNTNCNkMyNEZEMjlFOUEzQzE2RjVDIiwidHlwIjoiYXQrand0In0.eyJuYmYiOjE1OTM0NTQyNTQsImV4cCI6MTU5MzQ1Nzg1NCwiaXNzIjoiaHR0cDovL2xvY2FsaG9zdDo1MDAwIiwiY2xpZW50X2lkIjoiY2xpZW50IiwianRpIjoiQ0QxQzY5QzVGRkI0MTA0RDU5MTUwNERFQkI0MkI3NjgiLCJpYXQiOjE1OTM0NTQyNTQsInNjb3BlIjpbIk15TnVtYmVyVjJBcGkiXX0.xvAs-IYh_sh8RmpNOcy4Rl78Jv2L2-vPE7FYeEVqxES2HBoTEEgPT7uV5MiZrVeK1OaLOrkERzZ4druHrBtKgaeJ-BoC_IUt5Lp_otnJVbmCgGtrPXk8RMKcZguvxQsJdD5rqHLNZaN07kMNQEmmAprSAPpixtErzMK5DEmaAee2PNi430AyiZnObYbUBm_07Un5_6cjpOSFltjzsABBOzsbWfXIbXwvynCUVEiN5_mHhhjgocPcvlzrHdDtUi_PbdBk_hhtouTlveIaCTyNGdhfR4JCTJjO069hVVCXHScrekjNPeRSC4eOFEesmdG-4IbPKWBLsKldc1SrC1DE-w"
}
],
"body": {
"mode": "urlencoded",
"urlencoded": [
{
"key": "grant_type",
"value": "client_credentials",
"type": "text",
"disabled": true
},
{
"key": "scope",
"value": "MyNumberV2Api",
"type": "text",
"disabled": true
}
],
"options": {
"urlencoded": {}
}
},
"url": {
"raw": "http://localhost:44340/api/adminuser",
"protocol": "http",
"host": [
"localhost"
],
"port": "44340",
"path": [
"api",
"adminuser"
]
},
"description": "https://localhost:44340/api/adminuser"
},
"response": []
},
{
"name": "api/adminuserdetail/1",
"request": {
"method": "GET",
"header": [],
"url": {
"raw": "http://localhost:44340/api/adminuserdetail/1",
"protocol": "http",
"host": [
"localhost"
],
"port": "44340",
"path": [
"api",
"adminuserdetail",
"1"
]
},
"description": "https://localhost:44340/api/adminuser"
},
"response": []
}
],
"protocolProfileBehavior": {}
}
There is 2 issues in your code, I start from easy one to fix:
On your API's startup class, move app.UseAuthentication();
to be before app.UseAuthorization();
. Proper order is critical for security. Read more here. Similar issue here
Second issue is that on API you are asking for audience = MyNumberV2Api
but if you check your current token on https://jwt.ms/ there is no aud
as MyNumberV2Api
in the token. Read more here. To fix this we have two options:
2.1. Change API to remove audience validation. To do this on API startup class use AddJwtBearer
instead of AddIdentityServerAuthentication
and set ValidateAudience = false
. After change code would be like this:
services.AddAuthentication("Bearer").AddJwtBearer("Bearer",
options =>
{
options.Authority = "http://localhost:5000";
options.Audience = "MyNumberV2Api";
options.RequireHttpsMetadata = false;
options.TokenValidationParameters = new
TokenValidationParameters()
{
ValidateAudience = false
};
});
2.2. Add the audience to token. On IdentityServer - Config.cs
, add the scopes to API resource:
return new List<ApiResource>()
{
new ApiResource("MyNumberV2Api","Customer API for MyNumberV2")
{
Scopes = new []{ "MyNumberV2Api" }
},
new ApiResource("ApiOne","Customer API for MyNumberV2"),
new ApiResource("ApiTwo","Customer API for MyNumberV2")
};
After this change, if you regenerate the token there will be a property as aud
with value as MyNumberV2Api
. Check the token on https://jwt.ms/
I suggest you to test every thing on http first and then try on https. For http you may need to remove app.UseHttpsRedirection();
in your code and also clean up the launchSettings.json
to remove https URLS and make sure "sslPort": 0
.
Just to add to the accepted answer. Instead of changing AddIdentityServerAuthentication
, to use AddJwtBearer
, IdentityServer 4 has support for older Validation. What helped me solve my issue was adding options.LegacyAudienceValidation = true;
within my AddIdentityServerAuthentication
.
Example:
services.AddAuthentication()
.AddIdentityServerAuthentication("token", options =>
{
options.RequireHttpsMetadata = false;
options.Authority = "https://localhost:5001";
options.ApiName = "API name";
options.LegacyAudienceValidation = true;
});
The "token" as the first argument in my method allows me to use "Bearer" Authentication. It also specifies the Authentication Scheme that you are going to be using. However, you will need to update your [Authorize]
attribute to handle the "token" to [Authorize(AuthenticationSchemes = "token")]
. The setup on the client-side won't change at all and is useful when co-hosting your WebApi with IdentityServer 4. More info here: IdentityServer 4 Release Docs
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With