oauth 2.0 - microsoft - OAuth token expiration in MVC6 app



microsoft oauth2 (1)

So I have an MVC6 app that includes an identity server (using ThinkTecture's IdentityServer3) and an MVC6 web services application.

In the web services application I am using this code in Startup:

app.UseOAuthBearerAuthentication(options =>
{
    options.Authority = "http://localhost:6418/identity";
    options.AutomaticAuthentication = true;
    options.Audience = "http://localhost:6418/identity/resources";
});

Then I have a controller with an action that has the Authorize attribute.

I have a JavaScript application that authenticates with the identity server, and then uses the provided JWT token to access the web services action.

This works, and I can only access the action with a valid token.

The problem comes when the JWT has expired. What I'm getting is what appears to be a verbose ASP.NET 500 error page, that returns exception information for the following exception:

System.IdentityModel.Tokens.SecurityTokenExpiredException IDX10223: Lifetime validation failed. The token is expired.

I am fairly new to OAuth and securing Web APIs in general, so I may be way off base, but a 500 error doesn't seem appropriate to me for an expired token. It's definitely not friendly for a web service client.

Is this the expected behavior, and if not, is there something I need to do to get a more appropriate response?


Edit: this bug was fixed in ASP.NET Core RC2 and the workaround described in this answer is no longer needed.


Note: this workaround won't work on ASP.NET 5 RC1, due to this other bug. You can either migrate to the RC2 nightly builds or create a custom middleware that catches the exceptions thrown by the JWT bearer middleware and returns a 401 response:

app.Use(next => async context => {
    try {
        await next(context);
    }

    catch {
        // If the headers have already been sent, you can't replace the status code.
        // In this case, throw an exception to close the connection.
        if (context.Response.HasStarted) {
            throw;
        }

        context.Response.StatusCode = 401;
    }
});

Sadly, that's how the JWT/OAuth2 bearer middleware (managed by MSFT) currently works by default, but it should be eventually fixed. You can see this GitHub ticket for more information: https://github.com/aspnet/Security/issues/411

Luckily, you can "easily" work around that by using the AuthenticationFailed notification:

app.UseOAuthBearerAuthentication(options => {
    options.Notifications = new OAuthBearerAuthenticationNotifications {
        AuthenticationFailed = notification => {
            notification.HandleResponse();

            return Task.FromResult<object>(null);
        }
    };
});