Jwt token in dotnet with no Exp and default props - c#

I'am using .Net core 3.1 to make a indentity api that i register and make my user login , and with that i need to pass a JWT token for the frontend use.
I already generate my token with the claims i need, but i need the token to never expires (i know it's not a good practice but i'm just following orders), and also i need to remove token's default props such as nbf and iat.
I'm using the lib Microsoft.AspNetCore.Authentication.JwtBearer
I did not found much in the documentation, so i don't even know if it's possible

Have you tried setting ValidateLifetime to false in TokenValidationParameters? That would allow any expiration dates.
Another thing you can do is to set RequireExpirationTime to false, which means exp doesn't need to be present in the token.
So, if you want to configure it to allow tokens not to contain an expiration time, but still validate the expiration time if the exp property is present, you can do this:
.AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateLifetime = true,
RequireExpirationTime = false,
};
});
Also, if you want, you can set a custom lifetime validator at the same place:
.AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters
{
// ... Other settings
LifetimeValidator = (notBefore, expiration, token, parameters) =>
{
// Decide if expiration is valid. Don't forget about clock skew.
}
};
});
Although it's unclear if you have control over the token issuer, or the token consumer, or both. All the above solutions assume you have control over the token consumer.
If you have control only over the issuer, it's a bit more difficult. What comes to my mind is setting an exp date that is extremely far into the future. And for invalidating the tokens if it's later required, one thing you could do I suppose is to change the encryption key that the issuer and consumer uses to create/validate the signature.
Removing exp, iat and nbf at the issuer side
As for removing the properties from the token, you can just leave them our from the token generation. Assuming you're doing manual token generation, you'd want to configure it e.g. like this:
var tokenOptions = new JwtSecurityToken(
issuer: jwtIssuer,
audience: jwtAudience,
claims: new List<Claim>() {
new Claim(JwtRegisteredClaimNames.Sub, userName),
},
// 'expires' not set
signingCredentials: new SigningCredentials(new SymmetricSecurityKey(Convert.FromBase64String(someKey)), SecurityAlgorithms.HmacSha256)
);
Generating it this way it won't contain exp, iat or nbf (confirmed locally).
Of course if you happen to generate it via e.g. Identity Server, it's a different story, but then you forgot to mention that (you mentioned Identity, which is a membership system that doesn't have built-in JWT token generation capabilities).

Related

Load roles from database based in JWT's userId

I'm trying to implement some database fetching in my JWT authentication, so that I can dinamically fetch the roles and other info from the database, I would ideally only want to store the userId in the JWT payload.
Doing that, I can do stuff like store the date where the password was last changed, and refuse the token in case it was issued before that.
The problem is that I have no idea where on the chain I would do such thing, I'm just getting started with .NET/ASP.NET
I have this on my ConfigureServices currently:
var jwtKey = Encoding.ASCII.GetBytes(_configuration["Jwt:Secret"]);
services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(options =>
{
options.RequireHttpsMetadata = false;
options.SaveToken = true;
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(jwtKey),
ValidateIssuer = false,
ValidateAudience = false,
};
});
And this on my Configure:
app.UseAuthentication();
app.UseAuthorization();
With that, I can access some user info inside a controller's action with User.Identity.Name for example, how would I do that after loading the model from the DB?
REQUIREMENTS
These are usually the main things you will care about:
APIs must have access to domain specific claims so that they can authorize correctly
Tokens returned to internet clients are kept confidential
OPTION 1
The optimal way to meet the requirements is to issue opaque tokens to internet clients, then introspect them before they reach the API, as described in the Phantom Token Approach.
An Authorization Server can then reach out to your domain specific data at the time of token issuance, to include custom claims in access tokens.
OPTION 2
In the API code there are two stages involved in integrating custom claims, and the second of these can be customized:
Verify the JWT access token
Form a ClaimsPrincipal
If your Authorization Server doesn't support opaque access tokens, another option that will work is a Custom Authentication Handler. It adds some complexity to your API though. See this blog post of mine and this code.

.Net Core API JWT Token Validation

Implemented the JWT Bearer Token validation in .Net Core WEB API as mentioned below:
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(opt =>
{
opt.Audience = Configuration["AAD:ResourceId"];
opt.Authority = $"{Configuration["AAD:Instance"]}{Configuration["AAD:TenantId"]}";
});
Doubt here is the above mentioned code will validate only the audience and authority ? or it will validate all the parameters like expiration and signature etc. ?
Do we need to validate the signature explicitly to check the payload has been tampered ?
I think you're looking for this:
https://zhiliaxu.github.io/how-do-aspnet-core-services-validate-jwt-signature-signed-by-aad.html
Here zhiliaxu explains in details how and what is actually validated when using .AddJwtBearer() and their conclusions are:
Now it is clear that
JWT signature is validated without providing any key or certification
in our service’s source code.
JWT signing key is retrieved from the well-known URL https://login.microsoftonline.com/common/discovery/keys, based on
JwtBearerOptions.Authority property.
The signing key is cached in the JwtBearerHandler singleton instance, and so our ASP.NET Core service only needs to retrieve it
once throughout its lifecycle.
Also based on this article we can take a look at the ValidateToken() documentation on MSDN: https://learn.microsoft.com/en-us/dotnet/api/system.identitymodel.tokens.jwt.jwtsecuritytokenhandler.validatetoken?view=azure-dotnet Where you can find the different exceptions the method throws:
SecurityTokenDecryptionFailedException: token was a JWE was not able to be decrypted.
SecurityTokenEncryptionKeyNotFoundException: token 'kid' header claim is not null AND decryption fails.
SecurityTokenException: token 'enc' header claim is null or empty.
SecurityTokenExpiredException: token 'exp' claim is < DateTime.UtcNow.
SecurityTokenInvalidAudienceException: token 'aud' claim did not match either ValidAudience or one of ValidAudiences.
SecurityTokenInvalidLifetimeException: token 'nbf' claim is > 'exp' claim.
SecurityTokenInvalidSignatureException: token.signature is not properly formatted.
SecurityTokenNoExpirationException: TokenReplayCache is not null and expirationTime.HasValue is false. When a TokenReplayCache is set, tokens require an expiration time.
SecurityTokenNotYetValidException: token 'nbf' claim is > DateTime.UtcNow.
SecurityTokenReplayAddFailedException: token could not be added to the TokenReplayCache.
SecurityTokenReplayDetectedException: token is found in the cache.
It will validate issuer, audience and lifetime by default. There's a bunch of properties in TokenValidationParameters. If you create a new instance of that class, you'll see which fields are set to true/false. Or, you could add the following to your code, breakpoint and investigate yourself.
.AddJwtBearer(JwtBearerDefaults.AuthenticationScheme, options =>
{ ...
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = false,
ValidateAudience = false, ...
}; ..
} ...
NB authority and issuer are pretty much the same concept. Also, mind the difference between ValidIssuer and ValidateIssuer.

Why IdentityModel JWT library generates the same token when the same Expires is provided?

I have the following code in my application to generate a JWT for authentication purposes in a REST API:
private string GenerateToken(List<Claim> identityFields)
{
var key = Convert.FromBase64String(__SECRET);
var securityKey = new SymmetricSecurityKey(key);
var descriptor = new SecurityTokenDescriptor
{
Subject = new ClaimsIdentity(identityFields.ToArray()),
Expires = DateTime.UtcNow.AddDays(1),
SigningCredentials = new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256Signature)
};
var handler = new JwtSecurityTokenHandler();
var token = handler.CreateJwtSecurityToken(descriptor);
return handler.WriteToken(token);
}
The "identityFields" parameter is an object with values from the user (which are not relevant here).
My main problem is that, when I sent two request to that method in the same second (I mean, for example, 2019-01-23 14:57:59.827 and 2019-01-23 14:57:59.350), it generates the same JWT for both session (taking in mind that the "identityFields are the same, of course), because the "Expires" property doesn't concern about the milliseconds.
How can solve that issue?
I'm using the official JWT library (System.IdentityModel.Tokens.Jwt), is not custom code.
Thank you!
I recommend a look at https://blogs.msdn.microsoft.com/webdev/2016/10/27/bearer-token-authentication-in-asp-net-core/
Depending on your specific use case you should be able to add custom claims to your token so that you can extrac the needed information directly from the token.
If you need to keep the information secret (since anyone can decode a JWT token) you could add a unique identifier to as a custom claim and use that to look up information in your database.

IdentityServer4 ValidIssuers

Is there any way to tell IdentityServer4's authentication system to allow multiple issuers for the tokens?
I have an application that is using Identity Server to issue bearer tokens, and as long as the front end and the back end use the same URL to get tokens from authentication works fine.
However, I now have a need to have the same site accessed through multiple CNAMEs, meaning that the client will request tokens from two different URLs.
The error that is sent to the logs is:
info: Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerMiddleware[7]
Bearer was not authenticated. Failure message: IDX10205: Issuer validation failed. Issuer: 'http://domainb.com'. Did not match: validationParameters.ValidIssuer: 'http://domaina.com' or validationParameters.ValidIssuers: 'null'.
The presence of a ValidIssuers collection seems to indicate that you can set multiple places from which the API will accept tokens, but I cannot find anything like that exposed in options exposed by UseIdentityServerAuthentication.
I am aware of the Authority option, but that only allows me to set a single valid authority.
Is there are any way of setting multiple valid issuers, or setting it to use something other than the hostname as the issuer id?
UPDATE
My identity server configuration on the server side looks like this:
services.AddIdentityServer(options => {
options.IssuerUri = "http://authserver"; })
.AddAspNetIdentity<ApplicationUser>();
this is from the auth server side of things.
On the client API, the UseIdentityServerAuthentication call looks like this:
app.UseIdentityServerAuthentication(new IdentityServerAuthenticationOptions()
{
Authority = AppSettingsConfigurationRoot["Authentication:AuthorityEndpoint"],
RequireHttpsMetadata = false,
ApiName = "rqapi",
AutomaticAuthenticate = true,
ClaimsIssuer = "http://localhost:5001"
});
The address in the {{AppSettingsConfigurationROot["Authentication:AuthorityEndpoint"] is usually set at the public DNS name of the server so that the token issuer as seen by AngularJS matches the URL of the IdentityServer from the point of view of the C# API.
As Original Poster wrote in a comment, the (now, 2020, deprecated) IdentityServer4.AccessTokenValidation package doesn't expose the right options. To read more about the recent deprecation check this blogpost, but if you still are using it, here's how I solved this issue.
The AddIdentityServerAuthentication(...) extension method is a wrapper (the code is super readable!) to combine two authentication schemes:
JwtBearer
OAuth2Introspection
It uses its own configuration class, and simply doesn't expose all the JwtBearer options (possibly just an omission, possibly because some options are not valid for both schemes.
If -like me- you only need JwtBearer you might get away with simply using just that, and using the ValidIssuers array. So:
services.AddAuthentication("Bearer")
.AddJwtBearer(options =>
{
options.Authority = "https://example.org";
options.Audience = "foo-api"; // options.ApiName in the IDS4 variant
options.TokenValidationParameters = new TokenValidationParameters
{
ValidIssuers = new[]
{
"https://example.org", // first issuer
"https://example.com", // some other issuer
},
NameClaimType = "name", // To mimick IDS4's variant
RoleClaimType = "role", // To mimick IDS4's variant
};
});
As far as I understand, this will use example.org as the Authority and get the openid-configuration and so forth from that domain. But any JWT token offered to this API would be accepted as long as one of the ValidIssuers is the iss (issuer claim) in the token.

Validating ADAL JWT token in C# REST service

I have a web application which uses the ADAL library for authentication through Azure Active Directory.
This web application makes a call to a C# REST service by passing the ADAL token string as a parameter. In my REST service, I want to validate this token. If the token is valid only then the service will perform the operation.
I searched a lot but could not find a way to validate the JWT token in my rest service. Can you guys please help me on this?
You have two options:
1. Use OWIN middleware
Use middleware that will handle token validation for you. A common case will be the OWIN middleware, which does all the magic for you. Usually, this is the best approach, as it allows you to focus your code on the business logic for your API, not on low-level token validation. For a sample REST API that uses OWIN, check out these two samples:
https://github.com/Azure-Samples/active-directory-dotnet-webapp-webapi-openidconnect
https://github.com/Azure-Samples/active-directory-dotnet-webapp-webapi-openidconnect-aspnet5
2. Manual JWT validation
You can use the JSON Web Token Handler for ASP.NET to do manual JWT token validation. (Ok, so it's not entirely manual, but it is manually invoked.) There's also a sample for this:
https://github.com/Azure-Samples/active-directory-dotnet-webapi-manual-jwt-validation (the actual JWT validation happens in Global.asax.cs and looks something like this:
JwtSecurityTokenHandler tokenHandler = new JwtSecurityTokenHandler();
TokenValidationParameters validationParameters = new TokenValidationParameters
{
ValidAudience = audience,
ValidIssuer = issuer,
IssuerSigningTokens = signingTokens,
CertificateValidator = X509CertificateValidator.None
};
try
{
// Validate token.
SecurityToken validatedToken = new JwtSecurityToken();
ClaimsPrincipal claimsPrincipal = tokenHandler.ValidateToken(jwtToken, validationParameters, out validatedToken);
// Do other validation things, like making claims available to controller...
}
catch (SecurityTokenValidationException)
{
// Token validation failed
HttpResponseMessage response = BuildResponseErrorMessage(HttpStatusCode.Unauthorized);
return response;
}

Categories