Search code examples
c#.netowin.net-framework-versionrefresh-token

How to capture a SecurityTokenExpiredException in OWIN middleware?


I have a Web API with OWIN that uses JwtBearerAuthenticationOptions (.Net Framework 4.5.2) to validate authentication tokens.

While following this excellent article by Rui Figueiredo in order to add a Refresh Token ability to the API, it seems I don't have JwtBearerEvents in OWIN. E.g. This code works for me in ASP.NET Core (in ConfigureServices):

services.AddAuthentication(x =>
{
    x.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
    x.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(x =>
{
    x.RequireHttpsMetadata = false;
    x.SaveToken = true;
    x.TokenValidationParameters = GetDefaultValidationParameters();
    x.Events = new JwtBearerEvents
    {
        OnAuthenticationFailed = context =>
        {
            if (context.Exception.GetType() == typeof(SecurityTokenExpiredException))
            {
                context.Response.Headers.Add("Token-Expired", "true");
            }
            return Task.CompletedTask;
        }
    };
});

I can't seem to grasp how to achieve the same using the OWIN pipeline. What I've tried is inserting a middleware in ConfigureAuth:

private static void ConfigureAuth(IAppBuilder pApp)
{
    pApp.Use(async (context, next) =>
    {
        try
        {
            await next.Invoke();
        }
        catch (SecurityTokenExpiredException)
        {
            context.Response.Headers.Add("Token - Expired", new[] { "true" });
            throw;
        }
    });
    var issuer = "issuer";
    var audience = "all";
    var key = Encoding.ASCII.GetBytes("MySecretKey");
    pApp.UseJwtBearerAuthentication(
        new JwtBearerAuthenticationOptions
        {
            AuthenticationMode = Microsoft.Owin.Security.AuthenticationMode.Active,
            AllowedAudiences = new[] { audience },
            IssuerSecurityKeyProviders = new IIssuerSecurityKeyProvider[]
            {
                new SymmetricKeyIssuerSecurityKeyProvider(issuer, key)
            },
            TokenValidationParameters = tokenValidationParameters,
            TokenHandler = new CustomJWTTokenHandler()
        });
}

But to no avail. The 401 status comes without the Token-Expired header in this case.

Does anybody have any pointers on how to do this properly in Katana?


Solution

  • Solved it. Following the lead of these answers I added a custom authorization attribute to my base controller, i.e:

    public class CustomAuthorization : AuthorizeAttribute
    {
        protected override void HandleUnauthorizedRequest(HttpActionContext actionContext)
        {
            base.HandleUnauthorizedRequest(actionContext);
            var ctx = actionContext;
            var token = ctx.Request.Headers.Authorization.Parameter;
            var handler = new CustomJWTTokenHandler();
            if (ctx.Response.StatusCode == HttpStatusCode.Unauthorized && handler.TokenHasExpired(token))
            {
                ctx.Response.Headers.Add("Token-Expired", "true");
            }
        }
    }
    

    and implemented an expiration check in my CustomJWTTokenHandler class like this:

    public bool TokenHasExpired(string tokenString)
    {
        var token = ReadToken(tokenString);
        var hasExpired = token.ValidTo < DateTime.UtcNow;
        return hasExpired;
    }
    

    HTH