After a lot of reading, i have found a way to implement a custom JWT bearer token validator as below.
Starup.cs
Codes:
public void Configure(IApplicationBuilder app, IHostingEnvironment env,
ILoggerFactory loggerFactory, IApplicationLifetime appLifetime)
{
loggerFactory.AddConsole(Configuration.GetSection("Logging"));
loggerFactory.AddDebug();
app.UseStaticFiles();
app.UseIdentity();
ConfigureAuth(app);
app.UseMvcWithDefaultRoute();
}
private void ConfigureAuth(IApplicationBuilder app)
{
var signingKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(Configuration.GetSection("TokenAuthentication:SecretKey").Value));
var tokenValidationParameters = new TokenValidationParameters
{
// The signing key must match!
ValidateIssuerSigningKey = true,
IssuerSigningKey = signingKey,
// Validate the JWT Issuer (iss) claim
ValidateIssuer = true,
ValidIssuer = Configuration.GetSection("TokenAuthentication:Issuer").Value,
// Validate the JWT Audience (aud) claim
ValidateAudience = true,
ValidAudience = Configuration.GetSection("TokenAuthentication:Audience").Value,
// Validate the token expiry
ValidateLifetime = true,
// If you want to allow a certain amount of clock drift, set that here:
ClockSkew = TimeSpan.Zero
};
var jwtBearerOptions = new JwtBearerOptions();
jwtBearerOptions.AutomaticAuthenticate = true;
jwtBearerOptions.AutomaticChallenge = true;
jwtBearerOptions.TokenValidationParameters = tokenValidationParameters;
jwtBearerOptions.SecurityTokenValidators.Clear();
//below line adds the custom validator class
jwtBearerOptions.SecurityTokenValidators.Add(new CustomJwtSecurityTokenHandler());
app.UseJwtBearerAuthentication(jwtBearerOptions);
var tokenProviderOptions = new TokenProviderOptions
{
Path = Configuration.GetSection("TokenAuthentication:TokenPath").Value,
Audience = Configuration.GetSection("TokenAuthentication:Audience").Value,
Issuer = Configuration.GetSection("TokenAuthentication:Issuer").Value,
SigningCredentials = new SigningCredentials(signingKey, SecurityAlgorithms.HmacSha256)
};
app.UseMiddleware<TokenProviderMiddleware>(Options.Create(tokenProviderOptions));
}
Below is the custom validator class:
public class CustomJwtSecurityTokenHandler : ISecurityTokenValidator
{
private int _maxTokenSizeInBytes = TokenValidationParameters.DefaultMaximumTokenSizeInBytes;
private JwtSecurityTokenHandler _tokenHandler;
public CustomJwtSecurityTokenHandler()
{
_tokenHandler = new JwtSecurityTokenHandler();
}
public bool CanValidateToken
{
get
{
return true;
}
}
public int MaximumTokenSizeInBytes
{
get
{
return _maxTokenSizeInBytes;
}
set
{
_maxTokenSizeInBytes = value;
}
}
public bool CanReadToken(string securityToken)
{
return _tokenHandler.CanReadToken(securityToken);
}
public ClaimsPrincipal ValidateToken(string securityToken, TokenValidationParameters validationParameters, out SecurityToken validatedToken)
{
//How to access HttpContext/IP address from here?
var principal = _tokenHandler.ValidateToken(securityToken, validationParameters, out validatedToken);
return principal;
}
}
In case of stolen token, i would like to add an additional layer of security to validate that the request is coming from the same client who generated the token.
Questions:
- Is there any way i can access
HttpContext
within theCustomJwtSecurityTokenHandler
class so that i could add custom validations based on the current client/requestor? - Is there any other way we can validate the authenticity of the requestor using such method/middleware?
In ASP.NET Core
HttpContext
could be obtained usingIHttpContextAccessor
service. Use DI to passIHttpContextAccessor
instance into your handler and get value ofIHttpContextAccessor.HttpContext
property.IHttpContextAccessor
service is not registered by defaul, so you first need to add the following in yourStartup.ConfigureServices
method:then modify your
CustomJwtSecurityTokenHandler
class:You should also use DI technique for
JwtSecurityTokenHandler
instantiation. Look into Dependency Injection documentation if you are new to all this stuff.Update: how to manually resolve dependencies (more info here)
modify
Configure
method to useIServiceProvider serviceProvider
:For custom JWT validator, I created a JWTCosumerProvider class inhert to IOAuthBearerAuthenticationProvider. And implement the ValidateIdentity() method to check the identity Claim which i stored the client IP address at first place,then compare to current request Id address after.
JWTHelper.GetClientIPAddress()
hope this help!