What exactly is the call JwtBearerDefaults.AuthenticationScheme and JwtBearerDefaults for?
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidateAudience = true,
ValidateLifetime = true,
ValidIssuer = tokenOptions.Issuer,
ValidAudience = tokenOptions.Audience,
ValidateIssuerSigningKey = true,
IssuerSigningKey = SecurityKeyHelper.CreateSecurityKey(tokenOptions.SecurityKey)
};
});
JwtBearerDefaults.AuthenticationScheme is a constant with the value "Bearer". This is just text that is similar in intent to the authentication provider.
When you call AddAuthentication, you need to provide the default authentication provider. Providers are named. By default AddJwtBearer will create an authentication provider with the name, yep you guessed it, JwtBearerDefaults.AuthenticationScheme.
You could ignore the JwtBearerDefaults class altogether if you'd like, this is personal preference. Pass whatever string you want to AddAuthentication, then use the overload of AddJwtBearer that takes a string and an Action to customize the provider's name.
builder.Services.AddAuthentication("MyCoolAuthProvider")
.AddJwtBearer("MyCoolAuthProvider", options => {
//...
});
In the end, why do people use the JwtBearerDefaults class? Same reason you name your for loop variable i. It's what the documentation suggests, and everyone else does it. At least if it's a constant, you can't typo the value.
Working on a ASP.Net Core 3.1 API I am having trouble with JWT (bearer) token validation in production.
Given this code:
services.AddAuthentication(x =>
{
x.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
x.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(x =>
{
x.Authority = config["JwtOptions:Authority"];
x.Audience = config["JwtOptions:Audience"];
}
all works will in my development environment. However, moving to production, the token is found to be invalid. Kestrel returns a WWW-Authenticate header that says:
Bearer error="invalid_token", error_description="The signature is invalid"
I am at loss for the reason for this difference. The only way to get it working in production seems to alter the settings as below, however, this means sacreficing all security that the JWT should provide...
x.TokenValidationParameters = new TokenValidationParameters
{
RequireSignedTokens = false,
ValidateIssuerSigningKey = false,
ValidateIssuer = false,
ValidateAudience = false,
SignatureValidator = delegate(string token, TokenValidationParameters parameters)
{
var jwt = new JwtSecurityToken(token);
return jwt;
},
};
Any ideas are welcome.
EDIT
The problem lies with HTTP. When using HTTPS, things work fine. This would seem to be a bug in Microsoft.AspNetCore.Authentication.JwtBearer, since I'd expect this to work with HTTP as well. Microsoft actually has manual for using a reverse proxy to shield Kestrel from the worlds; using HTTP should be fine in that case.
I'm attempting to set up two different authorization policies called 'portal' and 'api' on our .NET Core 2.1 project hosted on a AWS Lambda environment. The first is the default policy that is used by the front-end site, and the latter is used by developers seeking to use our external API. I want each one to be signed with a different IssuerSigningKey so that users cannot use the bearer token they receive interchangeably with the two different services.
The problem I'm having is that the bearer token generated to access the 'api' policy works sometimes, but not others. I will get five 401s in a row, then followed by several 200s, then back to 401s. Seems very random. The errors I'm getting when the 401s show up are
Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerHandler: Failed to validate the token.
Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerHandler: portal was not authenticated. Failure message: IDX10503: Signature validation failed. Keys tried: 'Microsoft.IdentityModel.Tokens.SymmetricSecurityKey , KeyId:
'.
Exceptions caught:
''.
token: '
{
"alg": "HS512",
"typ": "JWT"
}
.
{
"nameid": "88",
"unique_name": "my#email.com",
"role": "apiAccess",
"nbf": 1596835699,
"exp": 1596839299,
"iat": 1596835699
}
'.
Which implies to me that sometimes it's not attempting to use the 'api' policy to validate the bearer token, but instead it's defaulting to 'portal'.
Here is a sample in Startup.cs where I set this all up.
...
serviceDependencies
.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer("portal", options =>
{
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(portalAccessKey),
ValidateIssuer = false,
ValidateAudience = false,
ValidateLifetime = true
};
})
.AddJwtBearer("api", options =>
{
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(apiAccessKey),
ValidateIssuer = false,
ValidateAudience = false,
ValidateLifetime = false
};
});
serviceDependencies.AddAuthorization(options =>
{
options.DefaultPolicy = new AuthorizationPolicyBuilder()
.RequireAuthenticatedUser()
.AddAuthenticationSchemes("portal")
.Build();
options.AddPolicy("MeteredAccess", new AuthorizationPolicyBuilder()
.RequireAuthenticatedUser()
.AddAuthenticationSchemes("api")
.RequireClaim(ClaimTypes.Role, "apiAccess")
.Build());
});
...
And the MVC endpoint I am applying the policy to is
[Authorize(Policy = "MeteredAccess")]
[HttpPost("api/objects/save")]
public async Task<IActionResult> ApiCreateObject([FromBody] CoreEngineObjectFormData data)
{
if(!(await _authService.UpdateUserApiAuthentications(UserId)))
return StatusCode(429);
var result = await _coreEngineService.CreateCoreEngineObject(data);
return Ok(result);
}
I never have this problem with the default policy, requests made from the front-end work every time.
Also, a big thing to mention is that I don't have this problem at all when I run the project locally under IIS Express, it's only after I deploy the project AWS Lambda that this starts to happen. I suspect that something about the lambda's lifetime may have something to do with this.
I'm working on an ASP.NET Core project that has a website as well as an API. I need the API to be secured by some means, so I implemented JWT authentication. This worked as expected, and the API is only accessible by calls made with a valid token.
The problem is, the built-in login functionality on the website no longer works.
This is the code I added to Startup.cs:
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(o =>
{
o.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidateAudience = true,
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
ValidIssuer = Configuration["Jwt:Issuer"],
ValidAudience = Configuration["Jwt:Issuer"],
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration["Jwt:Key"]))
};
});
If I remove this code, the built-in login feature works as usual, but the API no longer works.
Is there a way I can make the JWT authentication only apply to the API controllers? Or is there a way I can make the built-in authentication use JWT?
I had the same problem and resolved it (using Asp.Net Core 3.0).
You can add more than one Authentication schema to your project with code like this.
// Read the token generation data...
var token = Configuration.GetSection("JwtTokenData").Get<JwtTokenData>();
services.AddAuthentication()
.AddCookie(options =>
{
options.LoginPath = $"/Identity/Account/Login";
options.AccessDeniedPath = $"/Identity/Account/AccessDenied";
})
.AddJwtBearer(options => {
options.SaveToken = true;
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(token.Secret)),
ValidIssuer = token.Issuer,
ValidAudience = token.Audience,
ValidateIssuer = false,
ValidateAudience = false
};
});
Notice how I haven't specified the default authentication schema when I call AddAuthentication. This allows the built-in Authentication to be the default for your pages or controller where you use the [Authorize] attribute.
But when you need to use JWT Authentication then you use this option on the Authorize attribute for your Web API controller or methods
[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
I have an Angular 7 application interfacing with a .Net Core 2.2 API back-end. This is interfacing with Azure Active Directory.
On the Angular 7 side, it is authenticating properly with AAD and I am getting a valid JWT back as verified on jwt.io.
On the .Net Core API side I created a simple test API that has [Authorize] on it.
When I call this method from Angular, after adding the Bearer token, I am getting (as seen in Chrome Debug Tools, Network tab, "Headers"):
WWW-Authenticate: Bearer error="invalid_token", error_description="The
signature key was not found"
With a HTTP/1.1 401 Unauthorized.
The simplistic test API is:
[Route("Secure")]
[Authorize]
public IActionResult Secure() => Ok("Secure works");
The Angular calling code is also as simple as I can get it:
let params : any = {
responseType: 'text',
headers: new HttpHeaders({
"Authorization": "Bearer " + token,
"Content-Type": "application/json"
})
}
this.http
.get("https://localhost:5001/api/azureauth/secure", params)
.subscribe(
data => { },
error => { console.error(error); }
);
If I remove the [Authorize] attribute and just call this as a standard GET request from Angular it works fine.
My Startup.cs contains:
services
.AddAuthentication(AzureADDefaults.AuthenticationScheme)
.AddAzureADBearer(options => this.Configuration.Bind("AzureAd", options));
The options are all properly set (such as ClientId, TenantId, etc) in the appsettings.json and options here is populating as expected.
I was facing the same issue. i was missing the authority..make sure authority and api name is correct now this code in configure services in startup file works for me:
services.AddAuthentication(IdentityServerAuthenticationDefaults.AuthenticationScheme)
.AddIdentityServerAuthentication( x =>
{
x.Authority = "http://localhost:5000"; //idp address
x.RequireHttpsMetadata = false;
x.ApiName = "api2"; //api name
});
I had a unique scenario, hopefully this will help someone.
I was building an API which has Windows Negotiate authentication enabled (.NET Core 5.0, running from IIS) and unit testing the API using the CustomWebApplicationFactory (see documentation for CustomWebApplicationFactory) through XUnit which does not support Negotiate authentication.
For the purposes of unit testing, I told CustomWebApplicationFactory to use a "UnitTest" environment (ASPNETCORE_ENVIRONMENT variable) and specifically coded logic into my application Startup.cs file to only add JWT authentication for the "UnitTest" environment.
I came across this error because my Startup.cs configuration did not have the signing key I used to create the token (IssuerSigningKey below).
if (_env.IsEnvironment("UnitTest"))
{
// for unit testing, use a mocked up JWT auth, so claims can be overridden
// for testing specific authentication scenarios
services.AddAuthentication()
.AddJwtBearer("UnitTestAuth", opt =>
{
opt.Audience = "api://local-unit-test";
opt.RequireHttpsMetadata = false;
opt.TokenValidationParameters = new TokenValidationParameters()
{
ClockSkew = TokenValidationParameters.DefaultClockSkew,
ValidateAudience = true,
ValidateIssuer = true,
ValidateIssuerSigningKey = true,
ValidAudience = "api://local-unit-test",
ValidIssuer = "unit-test",
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("abcdefghijklmnopqrstuvwxyz123456"))
};
});
} else {
// Negotiate configuration here...
}
Regardless of the ValidateIssuerSigningKey being true or false, I still received the "invalid_token" 401 response, same as the OP. I even tried specifying a custom IssuerSigningKeyValidator delegate to always override the result, but did not have luck with that either.
WWW-Authenticate: Bearer error="invalid_token", error_description="The signature key was not found"
When I added IssuerSigningKey to the TokenValidationParameters object (of course matching the key I used when generating the token in my unit test), everything worked as expected.
There could be two reason:
You might have missed registering service :
services.AddAuthorization(auth =>
{
auth.AddPolicy("Bearer", new AuthorizationPolicyBuilder()
.AddAuthenticationSchemes(JwtBearerDefaults.AuthenticationScheme)
.RequireAuthenticatedUser().Build());
});
Or
2. You might have missed assigning value to key "IssuerSigningKey" as shown below
validate.TokenValidationParameters = new TokenValidationParameters()
{
ValidateAudience = true,
ValidAudience = "Audience",
ValidateIssuer = true,
ValidIssuer = "http://localhost:5000",
RequireExpirationTime = false,
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes("abcdefghi12345"))
});
This resolved my problem
My problem was that I needed to set the ValidIssuer option in the AddJwtBearer TokenValidationParameters, in addition to the authority
For example:
services.AddAuthentication("Bearer")
.AddJwtBearer(options =>
{
options.Audience = "My Audience";
options.Authority = "My issuer";
options.TokenValidationParameters = new TokenValidationParameters {
ValidateIssuerSigningKey = true,
ValidateLifetime = true,
ValidateIssuer = true,
ValidIssuer = "Also My Issuer", //Missing line here
ValidateAudience = true
};
});
Verify the values that you send for request the jwt token (eg: grant_type, client_secret, scope, client_id, etc)
Ensuere that you are using the appropiate token. That's all!
Here is my mistake:
I was using Postman, and request a token and set it to a varibale "Var_Token1":
pm.environment.set("Var_Token1", pm.response.json().access_token);
But when I need to use the token for my final request, I selected and use the wrong token (Var_Token2):
Authorization: Bearer {{Var_Token2}}
For me, this error was coming because the URL in appsettings.json was incorrect. I fixed it and it's working fine now.
This can also happen if you are using a different SignedOutCallbackPath/SignUpSignInPolicyId policy id than which is being passed in the token as tfp/acr.
My Core API uses different services configuration (and it works :)):
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
Configuration.Bind("JwtBearer", options);
}
Are you sure you are passing an access token and not an id_token? Is the aud claim present in the token exactly the same as the clientid your API is configured with? You may want to add some events to your options to see what you are receiving and where the validation fails.
I had this issue, and it was caused by jwtOptions.Authority not being set in config.
If you are using:
services.AddAuthentication(IdentityServerAuthenticationDefaults.AuthenticationScheme)
.AddIdentityServerAuthentication(IdentityServerAuthenticationDefaults.AuthenticationScheme,
And jwtOptions.Authority is set to null or "" you can get this error message.