ASP.NET Core JWT Bearer Token Custom Validation
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.
public Task ValidateIdentity(OAuthValidateIdentityContext context)
{
var requestIPAddress = context.Ticket.Identity.FindFirst(ClaimTypes.Dns)?.Value;
if (requestIPAddress == null)
context.SetError("Token Invalid", "The IP Address not right");
string clientAddress = JWTHelper.GetClientIPAddress();
if (!requestIPAddress.Equals(clientAddress))
context.SetError("Token Invalid", "The IP Address not right");
return Task.FromResult<object>(null);
}
JWTHelper.GetClientIPAddress()
internal static string GetClientIPAddress()
{
System.Web.HttpContext context = System.Web.HttpContext.Current;
string ipAddress = context.Request.ServerVariables["HTTP_X_FORWARDED_FOR"];
if (!string.IsNullOrEmpty(ipAddress))
{
string[] addresses = ipAddress.Split(',');
if (addresses.Length != 0)
{
return addresses[0];
}
}
return context.Request.ServerVariables["REMOTE_ADDR"];
}
hope this help!
Just to complement another solution and without injection into ISecurityTokenValidator, could be like
In your ISecurityTokenValidator Implementation (CustomJwtSecurityTokenHandler in this case)
public class CustomJwtSecurityTokenHandler : ISecurityTokenValidator {
...
//Set IHttpContextAccessor as public property to set later in Starup class
public IHttpContextAccessor _httpContextAccessor { get; set; };
//Remove injection of httpContextAccessor;
public CustomJwtSecurityTokenHandler()
{
_tokenHandler = new JwtSecurityTokenHandler();
}
...
And in Startup class configure property "CustomJwtSecurityTokenHandler" as global member
public readonly CustomJwtSecurityTokenHandler customJwtSecurityTokenHandler = new()
In ConfigureServices method of Startup class add the global customJwtSecurityTokenHandler.
public void ConfigureServices(IServiceCollection services)
{
...
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(
o =>
{
...
//Add the global ISercurityTokenValidator implementation
o.SecurityTokenValidators.Add(this.customJwtSecurityTokenHandler );
}
);
...
}
Then in Configure method of Startup class pass IHttpContextAccessor instance to property of the global customJwtSecurityTokenHandler (ISecurityTokenValidator)
public void Configure(IApplicationBuilder app, IHostingEnvironment env,
ILoggerFactory loggerFactory, IApplicationLifetime appLifetime,
IServiceProvider serviceProvider)
{
...
var httpContextAccessor = serviceProvider.GetService<IHttpContextAccessor>();
//And add to property, and not by constructor
customJwtSecurityTokenHandler.httpContextAccessor = httpContextAccessor;
...
}
In my case I've configured SecurityTokenValidator in ConfigureService so In this time there is not exist any instace of IServiceProvider, then in Configure method you can use IServiceProvider to get IHttpContextAccessor
In ASP.NET Core, HttpContext
could be obtained using IHttpContextAccessor
service. Use DI to pass IHttpContextAccessor
instance into your handler and get value of IHttpContextAccessor.HttpContext
property.
IHttpContextAccessor
service is not registered by default, so you first need to add the following in your Startup.ConfigureServices
method:
services.TryAddSingleton<IHttpContextAccessor, HttpContextAccessor>();
then modify your CustomJwtSecurityTokenHandler
class:
private readonly IHttpContextAccessor _httpContextAccessor;
public CustomJwtSecurityTokenHandler(IHttpContextAccessor httpContextAccessor)
{
_httpContextAccessor = httpContextAccessor;
_tokenHandler = new JwtSecurityTokenHandler();
}
...
public ClaimsPrincipal ValidateToken(string securityToken, TokenValidationParameters validationParameters, out SecurityToken validatedToken)
{
var httpContext = _httpContextAccessor.HttpContext;
}
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 use IServiceProvider serviceProvider
:
public void Configure(IApplicationBuilder app, IHostingEnvironment env,
ILoggerFactory loggerFactory, IApplicationLifetime appLifetime,
IServiceProvider serviceProvider)
{
...
var httpContextAccessor = serviceProvider.GetService<IHttpContextAccessor>();
// and extend ConfigureAuth
ConfigureAuth(app, httpContextAccessor);
...
}