Reputation: 3024
I am currently following this tutorial to implement Jwt Refresh Tokens. Currently I'm trying to add a header called Token-Expired : "true" when I get a specific exception when responding to an API request.
In the tutorial, this section shows how to do it in the Startup.cs
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
//...
services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = "bearer";
options.DefaultChallengeScheme = "bearer";
}).AddJwtBearer("bearer", options =>
{
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateAudience = false,
ValidateIssuer = false,
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("the server key used to sign the JWT token is here, use more than 16 chars")),
ValidateLifetime = true,
ClockSkew = TimeSpan.Zero //the default for this setting is 5 minutes
};
options.Events = new JwtBearerEvents
{
OnAuthenticationFailed = context =>
{
if (context.Exception.GetType() == typeof(SecurityTokenExpiredException))
{
context.Response.Headers.Add("Token-Expired", "true");
}
return Task.CompletedTask;
}
};
});
}
The problem is I am using ASP.NET Web Api 2 and not .net core 2.1. How can I add this code to mine? One way that I think might work is that I can add it in my TokenValidation class but I don't know how to do so:
public class TokenValidationHandler : DelegatingHandler
{
private static bool RetrieveToken(HttpRequestMessage request, out string token)
{
token = null;
IEnumerable<string> authHeaders;
if (!request.Headers.TryGetValues("Authorization", out authHeaders) || authHeaders.Count() > 1)
{
return false;
}
var bearerToken = authHeaders.ElementAt(0);
token = bearerToken.StartsWith("Bearer ") ? bearerToken.Substring(7) : bearerToken;
return true;
}
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
HttpStatusCode statusCode;
string token;
//determine whether a jwt exists or not
if (!RetrieveToken(request, out token))
{
statusCode = HttpStatusCode.Unauthorized;
//allow requests with no token - whether a action method needs an authentication can be set with the claimsauthorization attribute
return base.SendAsync(request, cancellationToken);
}
try
{
const string sec = HostConfig.SecurityKey;
var now = DateTime.UtcNow;
var securityKey = new SymmetricSecurityKey(System.Text.Encoding.Default.GetBytes(sec));
SecurityToken securityToken;
JwtSecurityTokenHandler handler = new JwtSecurityTokenHandler();
TokenValidationParameters validationParameters = new TokenValidationParameters()
{
ValidAudience = HostConfig.Audience,
ValidIssuer = HostConfig.Issuer,
//Set false to ignore expiration date
ValidateLifetime = false,
ValidateIssuerSigningKey = true,
LifetimeValidator = this.LifetimeValidator,
IssuerSigningKey = securityKey
};
//extract and assign the user of the jwt
Thread.CurrentPrincipal = handler.ValidateToken(token, validationParameters, out securityToken);
HttpContext.Current.User = handler.ValidateToken(token, validationParameters, out securityToken);
return base.SendAsync(request, cancellationToken);
}
catch (SecurityTokenValidationException e)
{
statusCode = HttpStatusCode.Unauthorized;
}
catch (Exception ex)
{
statusCode = HttpStatusCode.InternalServerError;
}
return Task<HttpResponseMessage>.Factory.StartNew(() => new HttpResponseMessage(statusCode) { });
}
public bool LifetimeValidator(DateTime? notBefore, DateTime? expires, SecurityToken securityToken, TokenValidationParameters validationParameters)
{
if (expires != null)
{
if (DateTime.UtcNow < expires) return true;
}
return false;
}
}
Upvotes: 2
Views: 557
Reputation: 235
Please add one more SecurityTokenExpiredException catch block to catch the token expiration error and add response header inside the catch block like below.
public class TokenValidationHandler : DelegatingHandler
{
private static bool RetrieveToken(HttpRequestMessage request, out string token)
{
token = null;
IEnumerable<string> authHeaders;
if (!request.Headers.TryGetValues("Authorization", out authHeaders) || authHeaders.Count() > 1)
{
return false;
}
var bearerToken = authHeaders.ElementAt(0);
token = bearerToken.StartsWith("Bearer ") ? bearerToken.Substring(7) : bearerToken;
return true;
}
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
HttpResponseMessage response = new HttpResponseMessage();
string token;
//determine whether a jwt exists or not
if (!RetrieveToken(request, out token))
{
response.StatusCode = HttpStatusCode.Unauthorized;
//allow requests with no token - whether a action method needs an authentication can be set with the claimsauthorization attribute
return base.SendAsync(request, cancellationToken);
}
try
{
const string sec = HostConfig.SecurityKey;
var now = DateTime.UtcNow;
var securityKey = new SymmetricSecurityKey(System.Text.Encoding.Default.GetBytes(sec));
SecurityToken securityToken;
JwtSecurityTokenHandler handler = new JwtSecurityTokenHandler();
TokenValidationParameters validationParameters = new TokenValidationParameters()
{
ValidAudience = HostConfig.Audience,
ValidIssuer = HostConfig.Issuer,
//Set false to ignore expiration date
ValidateLifetime = false,
ValidateIssuerSigningKey = true,
LifetimeValidator = this.LifetimeValidator,
IssuerSigningKey = securityKey
};
//extract and assign the user of the jwt
Thread.CurrentPrincipal = handler.ValidateToken(token, validationParameters, out securityToken);
HttpContext.Current.User = handler.ValidateToken(token, validationParameters, out securityToken);
return base.SendAsync(request, cancellationToken);
}
catch (SecurityTokenExpiredException e)
{
var expireResponse = base.SendAsync(request, cancellationToken).Result;
response.Headers.Add("Token-Expired", "true");
response.StatusCode = HttpStatusCode.Unauthorized;
}
catch (SecurityTokenValidationException e)
{
response.StatusCode = HttpStatusCode.Unauthorized;
}
catch (Exception ex)
{
response.StatusCode = HttpStatusCode.InternalServerError;
}
return Task<HttpResponseMessage>.Factory.StartNew(() => response);
}
public bool LifetimeValidator(DateTime? notBefore, DateTime? expires, SecurityToken securityToken, TokenValidationParameters validationParameters)
{
if (expires != null)
{
if (DateTime.UtcNow < expires) return true;
}
return false;
}
}
Upvotes: 1
Reputation: 789
You also need Microsoft.Owin.Host.SystemWeb package to install first.Then Create One Class With Name Startup.cs
this will help you..
public class Startup
{
public void Configuration(IAppBuilder app)
{
//
}
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
//...
services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = "bearer";
options.DefaultChallengeScheme = "bearer";
}).AddJwtBearer("bearer", options =>
{
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateAudience = false,
ValidateIssuer = false,
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("the server key used to sign the JWT token is here, use more than 16 chars")),
ValidateLifetime = true,
ClockSkew = TimeSpan.Zero //the default for this setting is 5 minutes
};
options.Events = new JwtBearerEvents
{
OnAuthenticationFailed = context =>
{
if (context.Exception.GetType() == typeof(SecurityTokenExpiredException))
{
context.Response.Headers.Add("Token-Expired", "true");
}
return Task.CompletedTask;
}
};
});
}
}
In addition to this, if your startup class is somehow not in your default name space, add a web config line to the <appSettings>
area like: <add key="owin:AppStartup" value="[NameSpace].Startup" />
To use ConfigureServices
method you need to have Build-in dependency injection is only available in ASP.NET Core. You will need to use third party IoC container such as -
Autofac for Web API
or
Ninject
For that get below library.
Microsoft.Extensions.DependencyInjection
Upvotes: 0