This is my jwt setup in Startup.cs
services.AddIdentity<User, Role>()
.AddUserManager<CustomUserManager>()
.AddEntityFrameworkStores<ManagementStudioDbContext>();
services.AddAuthentication(x =>
{
x.DefaultAuthenticateScheme = "bearer";
x.DefaultChallengeScheme = "bearer";
})
.AddJwtBearer("bearer",x =>
{
x.RequireHttpsMetadata = false;
x.SaveToken = true;
//x.TokenValidationParameters = tokenValidationParameters;
x.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Environment.GetEnvironmentVariable(MSCASGlobals.MS_SecretKey))),
ValidateIssuer = true,
ValidateAudience = true,
ValidateLifetime = true,
ValidIssuer = "SDD",
ValidAudience = "SDD",
ClockSkew = TimeSpan.Zero,
};
x.Events = new JwtBearerEvents
{
OnAuthenticationFailed = context =>
{
if(context.Exception.GetType() == typeof(SecurityTokenExpiredException))
{
context.Response.Headers.Add("Token-Expired", "true");
}
return Task.CompletedTask;
}
};
});
When I try to run my Authentication API, I get this error:
Unable to resolve service for type
'Microfoft.IdentityModel.Tokens.TokenValidationparameters' while
attempting to active 'Appone.Connect.Api.Token'
This is the code to create the token:
var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(configuration[AppOne.ClassLibrary.Globals.MSCASGlobals.MS_SecretKey]));
var credentials = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
var tokenDescriptor = new SecurityTokenDescriptor
{
Subject = new ClaimsIdentity(claims),
Expires = DateTime.UtcNow.AddMinutes(2),
SigningCredentials = credentials,
IssuedAt = DateTime.Now,
Issuer = "SDD",
Audience = "SDD"
};
var token = tokenHandler.CreateToken(tokenDescriptor);
var refreshToken = tokens.GenerateRefreshToken();
Connect.Api is this controller class. It is already injected in my Startup.cs
public class Token : Controller
{
public Token()
{
}
public string GenerateRefreshToken()
{
var random = new byte[64];
var rng = RandomNumberGenerator.Create();
rng.GetBytes(random);
return Convert.ToBase64String(random).Replace("/", "_").Replace("+", "_");
}
public string RefreshToken(string Token, string RefreshToken, string SecretKey)
{
var validatedToken = GetPrincipalFromToken(Token, SecretKey);
if (validatedToken == null)
{
return null;
}
return "Hello";
}
public ClaimsPrincipal GetPrincipalFromToken(string Token, string SecretKey)
{
var tokenHandler = new JwtSecurityTokenHandler();
var tokenValidationParameters = new TokenValidationParameters
{
ValidateAudience = false, //you might want to validate the audience and issuer depending on your use case
ValidateIssuer = false,
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(SecretKey)),
ValidateLifetime = false //here we are saying that we don't care about the token's expiration date
};
try
{
var principal = tokenHandler.ValidateToken(Token, tokenValidationParameters, out var validatedToken);
if (!ValidateSecurityAlgorithm(validatedToken))
{
return null;
};
return principal;
}
catch(Exception e)
{
return null;
}
}
private bool ValidateSecurityAlgorithm(SecurityToken SecurityToken)
{
var res = (SecurityToken is JwtSecurityToken jwtSecurityToken) && jwtSecurityToken.Header.Alg.Equals(SecurityAlgorithms.HmacSha256,StringComparison.InvariantCultureIgnoreCase);
return res;
}
}
Related
its config jwt Program.cs
#region JWT
IdentityModelEventSource.ShowPII = true;
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme).AddJwtBearer(options => {
var secretkey = Encoding.UTF8.GetBytes(builder.Configuration["JWT:Secret"]);
var encryptionkey = Encoding.UTF8.GetBytes(builder.Configuration["JWT:Encryptkey"]);
var validationParameters = new TokenValidationParameters
{
ClockSkew = TimeSpan.Zero, // default: 5 min
RequireSignedTokens = true,
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(secretkey),
RequireExpirationTime = true,
ValidateLifetime = true,
ValidateAudience = true, //default : false
ValidAudience = builder.Configuration["JWT:ValidAudience"],
ValidateIssuer = true, //default : false
ValidIssuer = builder.Configuration["JWT:ValidIssuer"],
TokenDecryptionKey = new SymmetricSecurityKey(encryptionkey)
};
options.RequireHttpsMetadata = false;
options.SaveToken = true;
options.TokenValidationParameters = validationParameters;
});
#endregion
its my code for create JWT Secure Token
i use SecurityAlgorithms.Aes128KW for hashing
private Tuple<string, DateTime> GenerateWebTokenJwt(User userInfo)
{
IList<string> userRoles = null;
userRoles = _userManager.GetRolesAsync(userInfo).Result;
if (userRoles == null)
{
return new Tuple<string, DateTime>("", DateTime.Now);
}
var exportTime = DateTime.Now.AddHours(24);
IDictionary<string, object> authClaims = new Dictionary<string, object>();
authClaims.Add(JwtRegisteredClaimNames.Sub, userInfo.FirstName);
authClaims.Add(JwtRegisteredClaimNames.Email, userInfo.Email);
authClaims.Add(JwtRegisteredClaimNames.Name, userInfo.UserName);
authClaims.Add(JwtRegisteredClaimNames.Aud, userInfo.PhoneNumber);
authClaims.Add(JwtRegisteredClaimNames.Sid, userInfo.Id.ToString());
authClaims.Add(JwtRegisteredClaimNames.Exp, exportTime.Date.Year + "/" + exportTime.Month + "/" + exportTime.Date
+ "-" + exportTime.Hour + ":" + exportTime.Minute + ":" + exportTime.Second);
authClaims.Add(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString());
if (userRoles != null)
{
for (int i = 0; i < userRoles.Count; i++)
{
authClaims.Add(ClaimTypes.Role + i, userRoles[i]);
}
}
var encryptionkey = Encoding.UTF8.GetBytes(_configuration["JWT:Encryptkey"]); //must be 16 character
var encryptingCredentials = new EncryptingCredentials(new SymmetricSecurityKey(encryptionkey), SecurityAlgorithms.Aes128KW, SecurityAlgorithms.Aes128CbcHmacSha256);
var securityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_configuration["JWT:Secret"]));
var Credemtials = new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256);
var descriptor = new SecurityTokenDescriptor
{
Issuer = _configuration["JWT:ValidIssuer"],
Audience = _configuration["JWT:ValidAudience"],
IssuedAt = DateTime.Now,
NotBefore = DateTime.Now.AddMinutes(Convert.ToInt32(_configuration["JWT:NotBeforeMinutes"])),
Expires = DateTime.Now.AddMinutes(Convert.ToInt32(_configuration["JWT:ExpirationMinutes"])),
SigningCredentials = Credemtials,
EncryptingCredentials = encryptingCredentials,
Claims = authClaims,
};
var tokenHandler = new JwtSecurityTokenHandler();
var securityToken = tokenHandler.CreateToken(descriptor);
string encryptedJwt = tokenHandler.WriteToken(securityToken);
return new Tuple<string, DateTime>(encryptedJwt, securityToken.ValidTo); ;
}
i can not use CreateJwtSecurityToken
var securityToken = tokenHandler.CreateJwtSecurityToken(descriptor);
its true i use CreateToken ?
my code for check validation bot Show Error On Run time
private bool ValidateToken(string authToken)
{
try
{
JwtSecurityTokenHandler tokenHandler = new JwtSecurityTokenHandler();
//string sToken = authToken.Substring(7, authToken.Length - 7);
if (!tokenHandler.CanReadToken(authToken))
{
return false;
}
JwtSecurityToken jwtToken = tokenHandler.ReadToken(authToken) as JwtSecurityToken;
if (jwtToken == null)
{
return false;
}
TokenValidationParameters parameters = new TokenValidationParameters()
{
ValidateIssuer = true,
ValidateAudience = true,
ValidateLifetime = true,
RequireExpirationTime = true,
ValidAudience = _configuration["JWT:ValidAudience"],
ValidIssuer = _configuration["JWT:ValidIssuer"],
//EncryptingCredentials = encryptingCredentials,
IssuerSigningKey = new SymmetricSecurityKey(Encoding.Default.GetBytes(_configuration["JWT:Secret"]))
};
SecurityToken securityToken;
ClaimsPrincipal principal = tokenHandler.ValidateToken(authToken, parameters, out securityToken);
if (principal == null)
{
return false;
}
}
catch (Exception ex)
{
return false;
}
return true;
}
error in validation is:
{"IDX10609: Decryption failed. No Keys tried: token: 'eyJhbGciOiJBMTI4S1ciLCJlbmMiOiJBMTI4Q0JDLUhTMjU2IiwidHlwIjoiSldUIn0.NQG56EqqvkTu0K02GYK9QIl-Sapf0tMXoWXq_ZXs99Fvzr9az0BRCA.39UAZmnrfigG4HvQxg8yrw.GbrJorcklXbnAA_VvCpHUdml2Q3MbLRbPQ8ItAcsdZnQ7vWizk2fiHVdbxoxj-9obRAEQ8m10NLC04qHg6ItnW8Pl0Awus_Fsw8kwPHKBB3LD194ifsN5T8o4CVuXQtZdNz5AcLfMRWwTIBMHs4jvP_sRRu2GQi08G0LtipKoYEKKEbk9rw0GiLEDYYVXnsFVLxNHyM9tHkmxFlbqdRTG7S6elLD1QbvCRFBbkEW7teafYE-CcxIFKb73YMPRpm1_9VpJBLCFNZSpefB4GwPpGUds0t6wVSGmKaBDyd1KCsCNgzyeYZlkmXbU-S2S7KVSU00oMpOwj3D1sUmsiWmue7nWuw3tQv-pwzVk89_xzAGecieQpY3F7QqzjTR6mkSj1xN8V65z0qQsLgA6DmrsB5AHpp4koTjqMWBMTpLKW7siY6n31GcJP6NNdADPl_hpcfuicVOeP4Zfl6RtHhePJyI4hHk-9pNkzc-i5DGSGdaTFDjg9JuLNLmGU5xKdmMXFWBUHqpgRJm1_ZlGzP2zpn7tTxXtp1l4wv4K8AzKhLmMDXullMeBAytS4-hiWNfclVttw_qLH15DEOCF11foA.xdyKycxHBsRF2jn5Y0IcPQ'."}
sample jwe Code is:
eyJhbGciOiJBMTI4S1ciLCJlbmMiOiJBMTI4Q0JDLUhTMjU2IiwidHlwIjoiSldUIn0.A0huSqmRHCi2VjCBNhzBLSCE6kNIFAERvBPMEPMUAXdDH60gvkxweg.UV6tmuaPHVpQ9H1WyFOB-w.9ljq5kzXk98WiNQrwOy-NlYb4HXC1uZx_jjn3o31qIfur_ORmOK3Uq6Zu9RcVe_IbY3A200lJMUcbeL_c5vNoUs6E1-F-8ffgpQiu-jR-lvZFsBi6_qUD7___qxl_aNBZdk1CqM5gKJeDKN9QEHm17QsH27hcF6HgHOh7-soIH0BdkYxejkpV2b3marLW9Zp6JeGsRSYxMMN4a7r1XSUp-UvIE0LBbhq0Bnz0vhzYbEALDlAwT_zSonDoMFNR5mbwIDcVMhYxwXZvxMfsAHDK4k8_nffqxFQ6foljL1ErjkT02IvUHl9uh2tsSQiOjcAfJu9oRnmUJXvFBeOKjLEa-jIwPvteXWWMJIEh_Yl_aqeSUWc_emiEt1gHlfBpCzmj3XQaPTWLSAuJDyVb6OuMjDlZ8l830SO6vkbfI3q0WtYziuB4hLmm1S4BV_2Qn9KemxWdDN82YfsQg5Skr0mEX4VIXu-FDsako1VEQtxygNLrngLG5iI-RRmXhlhqEy0nkQCup7SqYo45DZkFsJZKQJ8Bs0BCZjacjndK5_vG504O06ui5v8kbTixjp6xjyyEGeALtjMeRSW3y-YUvuwWw.kCx6iQOPEasw395EIpcZhg
I Find awnser in https://www.scottbrady91.com/c-sharp/json-web-encryption-jwe-in-dotnet-core
ty Scott Brady
private bool ValidateToken(string authToken)
{
try
{
var tokenHandler = new JwtSecurityTokenHandler();
if (!tokenHandler.CanReadToken(authToken))
{
return false;
}
JwtSecurityToken jwtToken = tokenHandler.ReadToken(authToken) as JwtSecurityToken;
if (jwtToken == null)
{
return false;
}
var encryptionkey = Encoding.UTF8.GetBytes(_configuration["JWT:Encryptkey"]); //must be 16 character
var encryptingCredentials = new EncryptingCredentials(new SymmetricSecurityKey(encryptionkey), SecurityAlgorithms.Aes128KW, SecurityAlgorithms.Aes128CbcHmacSha256);
ClaimsPrincipal claimsPrincipal = tokenHandler.ValidateToken(
authToken,
new TokenValidationParameters
{
ValidateLifetime = true,
ValidAudience = _configuration["JWT:ValidAudience"],
ValidIssuer = _configuration["JWT:ValidIssuer"],
RequireSignedTokens = false,
TokenDecryptionKey = new SymmetricSecurityKey(encryptionkey),
},
out SecurityToken securityToken);
if (claimsPrincipal == null)
{
return false;
}
}
catch (Exception ex)
{
return false;
}
return true;
}
and Create JWE Token Code is:
private Tuple<string, DateTime> GenerateWebTokenJwt(User userInfo)
{
IList<string> userRoles = null;
userRoles = _userManager.GetRolesAsync(userInfo).Result;
if (userRoles == null)
{
return new Tuple<string, DateTime>("", DateTime.Now);
}
var exportTime = DateTime.Now.AddHours(24);
IDictionary<string, object> authClaims = new Dictionary<string, object>();
authClaims.Add(JwtRegisteredClaimNames.Sub, userInfo.FirstName);
authClaims.Add(JwtRegisteredClaimNames.Email, userInfo.Email);
authClaims.Add(JwtRegisteredClaimNames.Name, userInfo.UserName);
authClaims.Add(JwtRegisteredClaimNames.Aud, userInfo.PhoneNumber);
authClaims.Add(JwtRegisteredClaimNames.Sid, userInfo.Id.ToString());
authClaims.Add(JwtRegisteredClaimNames.Exp, exportTime.Date.Year + "/" + exportTime.Month + "/" + exportTime.Date
+ "-" + exportTime.Hour + ":" + exportTime.Minute + ":" + exportTime.Second);
authClaims.Add(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString());
if (userRoles != null)
{
for (int i = 0; i < userRoles.Count; i++)
{
authClaims.Add(ClaimTypes.Role + i, userRoles[i]);
}
}
var handler = new JwtSecurityTokenHandler();
var encryptionkey = Encoding.UTF8.GetBytes(_configuration["JWT:Encryptkey"]); //must be 16 character
var encryptingCredentials = new EncryptingCredentials(new SymmetricSecurityKey(encryptionkey), SecurityAlgorithms.Aes128KW, SecurityAlgorithms.Aes128CbcHmacSha256);
var tokenDescriptor = new SecurityTokenDescriptor
{
Audience = _configuration["JWT:ValidAudience"],
Issuer = _configuration["JWT:ValidIssuer"],
Expires = DateTime.Now.AddMinutes(Convert.ToInt32(_configuration["JWT:ExpirationMinutes"])),
Subject = new ClaimsIdentity(new List<Claim> { new Claim("sub", "scott") }),
EncryptingCredentials = encryptingCredentials
};
string token = handler.CreateEncodedJwt(tokenDescriptor);
return new Tuple<string, DateTime>(token, DateTime.Now.AddMinutes(Convert.ToInt32(_configuration["JWT:ExpirationMinutes"])));
}
In an ASP.NET Core 5 project with RestAPI, I'm using JWT Bearer with tokens and refresh-tokens. I configured the startup like this:
var jwtSecretKey = Configuration.GetValue<string>("Jwt:Key");
var key = Encoding.UTF8.GetBytes(jwtSecretKey);
var tokenValidationParameters = new TokenValidationParameters
{
SaveSigninToken = true,
ValidateActor = true,
ValidateIssuer = true,
ValidateAudience = true,
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
ValidIssuer = Configuration["Jwt:Issuer"],
ValidAudience = Configuration["Jwt:Audience"],
IssuerSigningKey = new SymmetricSecurityKey(key),
// set clockskew to zero so tokens expire exactly at token expiration time (instead of 5 minutes later)
ClockSkew = TimeSpan.Zero
};
services.AddAuthentication(options =>
{
options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(options =>
{
options.RequireHttpsMetadata = false;
options.SaveToken = true;
options.TokenValidationParameters = tokenValidationParameters;
});
For generating the Tokens I used this code (Something to notice is I used UTC dates):
string validIssuer = _configuration["Jwt:Issuer"];
string validAudience = _configuration["Jwt:Audience"];
var jwtSecretKey = _configuration.GetValue<string>("Jwt:Key");
var issuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtSecretKey));
var accessTimeout = _configuration.GetValue<int>("Jwt:MinutosExpiracionTokenAcceso");
DateTime fechaHoy = DateTime.UtcNow;
DateTime fechaExpiracionToken = DateTime.UtcNow.AddMinutes(accessTimeout);
var userRoles = await _userManager.GetRolesAsync(user);
var claims = new List<Claim>
{
new Claim(ClaimTypes.Sid, user.Id),
new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()) // (JWT ID) Unique Identifier for JWT, can be used to prevent the JWT from being replayed. Is best suited for refresh tokens
};
foreach (var userRole in userRoles)
{
claims.Add(new Claim(ClaimTypes.Role, userRole));
}
var token = new JwtSecurityToken(
validIssuer,
validAudience,
claims,
fechaHoy,
fechaExpiracionToken,
new SigningCredentials(issuerSigningKey, SecurityAlgorithms.HmacSha256)
);
var refreshTimeout = _configuration.GetValue<int>("Jwt:MinutosExpiracionTokenAutorizacion");
var refreshToken = new RefreshToken()
{
JwtId = token.Id,
IsUsed = false,
UserId = user.Id,
AddedDate = DateTime.UtcNow,
ExpiryDate = DateTime.UtcNow.AddMinutes(refreshTimeout),
IsRevoked = false,
Token = RandomString(25) + Guid.NewGuid()
};
await _unitOfWork.GetRepository<RefreshToken>().AddAsync(refreshToken);
await _unitOfWork.SaveChangesAsync();
var tokensDto = new TokensResponseDto
{
Token = new JwtSecurityTokenHandler().WriteToken(token),
RefreshToken = refreshToken.Token
};
return tokensDto;
I'm facing issues validating the token (_tokenValidationParameters are the same as used in startup):
var jwtTokenHandler = new JwtSecurityTokenHandler();
ClaimsPrincipal principal = null;
SecurityToken validatedToken = null;
principal = jwtTokenHandler.ValidateToken(tokenRequest.Token, _tokenValidationParameters, out validatedToken);
It's throwing the mentioned exception with no specific dates, no matter whether I use localtime such as DateTime.Now instead of UTC.
I tried by adding LifetimeValidator in TokenValidationParameters in startup like this:
LifetimeValidator = (DateTime? notBefore,
DateTime? expires,
SecurityToken securityToken,
TokenValidationParameters validationParameters
) =>
{
if (expires != null)
{
if (DateTime.UtcNow < expires.Value.ToUniversalTime())
{
return true; // Still valid
}
}
return false; // Expired
}
However, ValidateToken fails with this exception:
IDX10230: Lifetime validation failed. Delegate returned false, securitytoken: 'System.IdentityModel.Tokens.Jwt.JwtSecurityToken'.
Thanks for any help
I had a similar problem. My server (generating token) time and my client (consuming token) time were different. Synchronized and worked perfectly.
In my case when I deployed the application on one of the Linux server, I had a similar "IDX10222" error, The reason behind this error is that the server current time is running behind the token creation time, so server is unable to validate the token.
For Example - Token Creation Time - 10:00:00 on dd:MM:yyyy UTC,
Linux Server Time - 08:00:00 on dd:MM:yyyy UTC
Then we get this error - Lifetime validation failed. The token is not yet valid.
Please check your machine datetime and fix if it is not in sync with the current time.
You Try:
/*
ClaimsPrincipal principal = null;
principal = jwtTokenHandler.ValidateToken(tokenRequest.Token, _tokenValidationParameters, out validatedToken);
*/
validatedToken = jwtTokenHandler.ReadJwtToken(token);
/*Your own validation logic*/
I am using angular and asp.net core I have implemented JWT Token and refresh token with the help of
this artical I have written code to check Jwt is valid in Authguard if jwt is not valid so with refresh token will make call to api and get the new Jwt and refresh token . this scenario is happing only if I am performing any event like refreshing page and navigating to another page. I want to know is there any way that if our jwt token expire without performing any event angular detect it and automatically call to api with refersh token and get the new jwt token
here are codes
authguard
here in authguard isUserAuthenticated() is going to check jwt expired or not if expired it will make api call with refresh token and get the new jwt and refresh token
async canActivate(
route: ActivatedRouteSnapshot,
state: RouterStateSnapshot): Promise<boolean> {
if((await this._authService.isUserAuthenticated()) === true){
return true;
}
else{
this._router.navigate([RouteConstant.SIGN_IN]);
return false;
}
}
public async isUserAuthenticated(){
if(this._storageService.getToken() !== null){
if(!this._jwtHelperService.isTokenExpired(this._storageService.getToken()?.toString())){
return true;
}
else{
if(!this._storageService.refreshTokenExists()){
return false;
}
else{
let authTokenClient: AuthTokenClient = {
token: this._storageService.getToken() as string,
refreshToken: this._storageService.getRefreshToken() as string
};
let response: AuthToken = await this._serverService.refreshAuthToken(authTokenClient).toPromise<AuthToken>();
console.log(response);
this.setAuth(response);
return !this._jwtHelperService.isTokenExpired(this._storageService.getToken()?.toString());
}
}
}
else{
return false;
}
}
}
public setAuth(authToken: AuthToken){
this._storageService.saveToken(authToken.token?.toString());
this._storageService.saveRefreshToken(authToken.refreshToken);
}
public async refreshAuthToken(authTokenClient: AuthTokenClient) {
const response = await this._http.post<AuthToken>(environment.apiHost + UrlConstant.ACCOUNT_REFRESH, authTokenClient,{observe: 'response'}).toPromise();
console.log(response);
const newToken = (<any>response).body.token;
console.log(newToken);
const newRefreshToken = (<any>response).body.refreshToken;
console.log(newRefreshToken);
localStorage.setItem("token", newToken);
localStorage.setItem("refreshToken",newRefreshToken);
if (newToken && newRefreshToken == null){
return false
}
else {
return true;
}
}
Startup.cs
services.AddAuthentication(opt =>
{
opt.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
opt.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidateAudience = true,
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
ValidIssuer = jwtSettings.ValidIssuer,
ValidAudience = jwtSettings.ValidAudience,
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration[ConfigurationKeyConstant.JWT_SECRET_KEY])),
ClockSkew = TimeSpan.Zero
};
});
Controller.cs
[Route("refresh")]
public IActionResult Refresh(AuthTokenBase authTokenBase)
{
if (authTokenBase is null)
{
return BadRequest("Invalid client request");
}
string accessToken = authTokenBase.Token;
string refreshToken = authTokenBase.RefreshToken;
var principal = this._jwtHandler.GetPrincipalFromExpiredToken(accessToken);
var username = principal.Identity.Name;
string newAccessToken = this._jwtHandler.GenerateToken();
string newRefreshToken = this._jwtHandler.GenerateRefreshToken();
return Ok(new AuthToken()
{
Token = newAccessToken,
RefreshToken = newRefreshToken,
IsAuthSuccessful = true
});
}
Under your ConfigureServices at service.AddAuthentication() you can add event for OnAuthenticationFailed.
And there check for exception type if its SecurityTokenExpiredException, then set response header tokenExpired=true.
Later at client side check response header and call refresh token API based on tokenExpired value.
pseudo code:
services.AddAuthentication(opt =>
{
opt.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
opt.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidateAudience = true,
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
ValidIssuer = jwtSettings.ValidIssuer,
ValidAudience = jwtSettings.ValidAudience,
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration[ConfigurationKeyConstant.JWT_SECRET_KEY])),
ClockSkew = TimeSpan.Zero
};
options.Events = new JwtBearerEvents {
OnAuthenticationFailed = context => {
if (context.Exception.GetType() == typeof(SecurityTokenExpiredException))
{
context.Response.Headers.Add("IS-TOKEN-EXPIRED", "true");
}
return Task.CompletedTask;
}
};
});
Here in response header it sets IS-TOKEN-EXPIRED as true, if token is expired.
For more detail you can check this Blog article written by me.
You can use angular interceptor.
Intercept every error in http request
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
var modifiedRequest = this.normalizeRequestHeaders(request);
return next.handle(modifiedRequest)
.pipe(
catchError(error => {
if (error instanceof HttpErrorResponse && error.status === 401) {
return this.tryGetRefreshTokenService(request, next, error);
} else {
return this.handleErrorResponse(error);
}
}),
switchMap((event) => {
return this.handleSuccessResponse(event);
})
);
}
I'm trying to use JWT tokens. I managed to generate a valid JWTTokenString and validated it on the JWT debugger but I'm having an impossible time validating the token in .Net. Here's the code I have so far:
class Program {
static string key = "401b09eab3c013d4ca54922bb802bec8fd5318192b0a75f201d8b3727429090fb337591abd3e44453b954555b7a0812e1081c39b740293f765eae731f5a65ed1";
static void Main(string[] args) {
var stringToken = GenerateToken();
ValidateToken(stringToken);
}
private static string GenerateToken() {
var securityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(key));
var credentials = new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256);
var header = new JwtHeader(credentials);
var payload = new JwtPayload {
{ "some ", "hello "},
{ "scope", "world"},
};
var secToken = new JwtSecurityToken(header, payload);
var handler = new JwtSecurityTokenHandler();
return handler.WriteToken(secToken);
}
private static bool ValidateToken(string authToken) {
var tokenHandler = new JwtSecurityTokenHandler();
var validationParameters = GetValidationParameters();
SecurityToken validatedToken;
IPrincipal principal = tokenHandler.ValidateToken(authToken, validationParameters, out validatedToken);
Thread.CurrentPrincipal = principal;
return true;
}
private static TokenValidationParameters GetValidationParameters() {
return new TokenValidationParameters() {
//NOT A CLUE WHAT TO PLACE HERE
};
}
}
All I want is a function that receives a token and returns true or false based on its validity. From research I've seen people use IssuerSigningToken to assign the validation key. But when I try to use it, it doesn't seem to exist. Could anyone give me a hand on validating the token?
You must use the same key to validate the token as the one you use to generate it. Also you need to disable some validations such as expiration, issuer and audiance, because the token you generate doesn't have these information (or you can add these information). Here's a working example:
class Program
{
static string key = "401b09eab3c013d4ca54922bb802bec8fd5318192b0a75f201d8b3727429090fb337591abd3e44453b954555b7a0812e1081c39b740293f765eae731f5a65ed1";
static void Main(string[] args)
{
var stringToken = GenerateToken();
ValidateToken(stringToken);
}
private static string GenerateToken()
{
var securityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(key));
var credentials = new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256);
var secToken = new JwtSecurityToken(
signingCredentials: credentials,
issuer: "Sample",
audience: "Sample",
claims: new[]
{
new Claim(JwtRegisteredClaimNames.Sub, "meziantou")
},
expires: DateTime.UtcNow.AddDays(1));
var handler = new JwtSecurityTokenHandler();
return handler.WriteToken(secToken);
}
private static bool ValidateToken(string authToken)
{
var tokenHandler = new JwtSecurityTokenHandler();
var validationParameters = GetValidationParameters();
SecurityToken validatedToken;
IPrincipal principal = tokenHandler.ValidateToken(authToken, validationParameters, out validatedToken);
return true;
}
private static TokenValidationParameters GetValidationParameters()
{
return new TokenValidationParameters()
{
ValidateLifetime = false, // Because there is no expiration in the generated token
ValidateAudience = false, // Because there is no audiance in the generated token
ValidateIssuer = false, // Because there is no issuer in the generated token
ValidIssuer = "Sample",
ValidAudience = "Sample",
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(key)) // The same key as the one that generate the token
};
}
}
In my case I only want to validate if the signature is correct. Most likely you will probably want to use #meziantou answer. But if you only want to verify that the message has not been tampered with here is an example. Lastly since I am the only person generating this tokens and I know I will be generating them using HmacSha256 I am using this approach.
using Microsoft.IdentityModel.Tokens;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Security.Cryptography;
using System.Text;
class Program
{
static readonly byte[] key = Encoding.UTF8.GetBytes("f645b33ef0d04cbe859777ac6f46226d");
// use this algorithm for example to work
static readonly string securityAlgorithm = SecurityAlgorithms.HmacSha256;
static void Main()
{
var token = GenerateToken();
var isTokenValid = IsJwtTokenValid(token);
if (isTokenValid)
Console.WriteLine(true);
}
/// <summary>
/// This method assumes token has been hashed using HMACSHA256 algorithm!
/// </summary>
private static bool IsJwtTokenValid(string token)
{
// example of token:
// header payload signature
// eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJmb29AZ21haWwuY29tIiwiZXhwIjoxNjQ1NzM1MDU2fQ.Gtrm2G_35ynyNd1-CjZ1HsvvFFItEsXPvwhaOsN81HQ
// from JWT spec
static string Base64UrlEncode(byte[] input)
{
var output = Convert.ToBase64String(input);
output = output.Split('=')[0]; // Remove any trailing '='s
output = output.Replace('+', '-'); // 62nd char of encoding
output = output.Replace('/', '_'); // 63rd char of encoding
return output;
}
try
{
// position of second period in order to split header+payload and signature
int index = token.IndexOf('.', token.IndexOf('.') + 1);
// Example: Gtrm2G_35ynyNd1-CjZ1HsvvFFItEsXPvwhaOsN81HQ
string signature = token[(index + 1)..];
// Bytes of header + payload
// In other words bytes of: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJmb29AZ21haWwuY29tIiwiZXhwIjoxNjQ1NzM1MDU2fQ
byte[] bytesToSign = Encoding.UTF8.GetBytes(token[..index]);
// compute hash
var hash = new HMACSHA256(key).ComputeHash(bytesToSign);
var computedSignature = Base64UrlEncode(hash);
// make sure that signatures match
return computedSignature.Length == signature.Length
&& computedSignature.SequenceEqual(signature);
}
catch
{
return false;
}
}
private static string GenerateToken()
{
var securityKey = new SymmetricSecurityKey(key);
var credentials = new SigningCredentials(securityKey, securityAlgorithm);
var secToken = new JwtSecurityToken(
signingCredentials: credentials,
claims: new[]
{
new Claim(JwtRegisteredClaimNames.Sub, "foo#gmail.com")
},
expires: DateTime.UtcNow.AddDays(1));
var handler = new JwtSecurityTokenHandler();
return handler.WriteToken(secToken);
}
}
Validate Token in Jwt Middleware Class that Invoke Method fire in every request for Authorization
JwtMiddleware
{
private readonly RequestDelegate _next;
private readonly TokenValidationParameters _tokenValidationParams;
public JwtMiddleware(RequestDelegate next, TokenValidationParameters
tokenValidationParams)
{
_next = next;
_tokenValidationParams = tokenValidationParams;
}
public async Task Invoke(HttpContext context)
{
try{
var token = context.Request.Headers["Authorization"].FirstOrDefault()?.Split(" ").Last();
var jwtTokenHandler = new JwtSecurityTokenHandler();
// Validation 1 - Validation JWT token format
var tokenInVerification = jwtTokenHandler.ValidateToken(token, _tokenValidationParams, out var validatedToken);
if (validatedToken is JwtSecurityToken jwtSecurityToken)
{
var result = jwtSecurityToken.Header.Alg.Equals(SecurityAlgorithms.HmacSha256, StringComparison.InvariantCultureIgnoreCase);
if (result == false)
{
Error Invalid = new Error()
{
Success = false,
Errors = "Token is Invalid"
};
context.Items["Error"] = Invalid;
}
}
}
catch (Exception ex)
{
Error Invalid = new Error()
{
Success = false,
Errors = "Token does not match or may expired."
};
context.Items["Error"] = Invalid ; // userService.GetById(userId);
}
await _next(context);
}
}
I have JwtToken created like this:
X509Certificate2 cert = certificateStore.Certificate;
var now = DateTime.UtcNow;
var tokenHandler = new JwtSecurityTokenHandler();
var tokenDescriptor = new SecurityTokenDescriptor()
{
Subject = new ClaimsIdentity(new[]
{
new Claim(JwtClaimTypes.Subject, upn),
new Claim(REQUEST_TYPE_NAME, requestType),
new Claim(DOMAIN_NAME, domain),
}),
Lifetime = new Lifetime(now, now.AddMinutes(60)),
SigningCredentials = new X509SigningCredentials(cert),
TokenIssuerName = ISSUER
};
SecurityToken token = tokenHandler.CreateToken(tokenDescriptor);
This is correct. Token is created and its first claim is named "sub" which is content of JwtTokenTypes.Subject. I checked it through jwt web.
Problem is, I have this method for resolving claims:
if (string.IsNullOrWhiteSpace(token)) throw new MissingTokenException("Token should not be null.");
var tokenHandler = new JwtSecurityTokenHandler();
var securityToken = new X509SecurityToken(new X509Certificate2(new X509RawDataKeyIdentifierClause(certificateStore.Certificate).GetX509RawData()));
var validationParameters = new TokenValidationParameters()
{
IssuerSigningToken = securityToken,
ValidateAudience = false,
ValidateActor = false,
ValidIssuer = ISSUER
};
SecurityToken securedToken = new JwtSecurityToken();
ClaimsPrincipal claimsPrincipal = tokenHandler.ValidateToken(token, validationParameters, out securedToken);
Claim claim = claimsPrincipal.FindFirst(m => string.Equals(m.Type, REQUEST_TYPE_NAME, StringComparison.OrdinalIgnoreCase));
if (claim != null && !string.Equals(claim.Value, requestType, StringComparison.OrdinalIgnoreCase))
{
throw new MismatchedTokenException("Token is not of the proper type.");
}
upn = claimsPrincipal.Claims.FirstOrDefault(m => m.Type.Equals(JwtClaimTypes.Subject) || m.Type.Equals(ClaimTypes.NameIdentifier))?.Value;
domain = claimsPrincipal.Claims.FirstOrDefault(m => m.Type.Equals(DOMAIN_NAME))?.Value;
In the end of the method you can see I check claim name for JwtClaimTypes.Subject, which it should be, and for ClaimTypes.NameIdentifiew, which it actually is.
Do you have any idea why this transformations occur or how to prevent them?
You need to add before:
JwtSecurityTokenHandler.InboundClaimTypeMap.Clear();
that clears the mapping of claims for jwt.
The mapping is done to adapt the jwt claims to .net types of claims.
If you avoid the mapping you need to set the claim types for name and role if you want to use that from the Identity (IPrincipal.IsInRole or Identity.Name).
You can do this when creating the ClaimsIdentity:
Subject = new ClaimsIdentity(new[]
{
new Claim(JwtClaimTypes.Subject, upn),
new Claim(REQUEST_TYPE_NAME, requestType),
new Claim(DOMAIN_NAME, domain),
}, "<auth type>", "name", "role"),
you'll have to change the authType to something like cookie, and the name and role claim name most probably would be like that.
The other way is to set it on the token validation parameter:
var validationParameters = new TokenValidationParameters()
{
IssuerSigningToken = securityToken,
ValidateAudience = false,
ValidateActor = false,
ValidIssuer = ISSUER,
NameClaimType = "name",
RoleClaimType = "role"
};