3

In my webapi application created from template in VS2013 I have added custom OAuthBearerAuthenticationProvider class in Startup.Auth.cs file:

public class CustomBearerAuthenticationProvider : OAuthBearerAuthenticationProvider
{
    public override Task ValidateIdentity(OAuthValidateIdentityContext context)
    {
        UserManager<ApplicationUser> userManager = context.OwinContext.GetUserManager<ApplicationUserManager>();
        var user = userManager.FindById(context.Ticket.Identity.GetUserId());
        var claims = context.Ticket.Identity.Claims;
        if (claims.FirstOrDefault(claim => claim.Type == "AspNet.Identity.SecurityStamp") == null 
            || claims.Any(claim => claim.Type == "AspNet.Identity.SecurityStamp" 
                && !claim.Value.Equals(user.SecurityStamp)))
        {
            context.Rejected();
        }
        return Task.FromResult<object>(null);
    }
}

Also I have added the variable:

public static OAuthBearerAuthenticationOptions OAuthBearerOptions { get; private set; }

And in the ConfigureAuth(IAppBuilder app) method I have added the following lines of code to use the custom OAuthBearerAuthenticationProvider class:

OAuthBearerOptions = new OAuthBearerAuthenticationOptions();
OAuthBearerOptions.AccessTokenFormat = OAuthOptions.AccessTokenFormat;
OAuthBearerOptions.AccessTokenProvider = OAuthOptions.AccessTokenProvider;
OAuthBearerOptions.AuthenticationMode = OAuthOptions.AuthenticationMode;
OAuthBearerOptions.AuthenticationType = OAuthOptions.AuthenticationType;
OAuthBearerOptions.Description = OAuthOptions.Description;
OAuthBearerOptions.Provider = new CustomBearerAuthenticationProvider();
OAuthBearerOptions.SystemClock = OAuthOptions.SystemClock;

app.UseOAuthBearerAuthentication(OAuthBearerOptions);

All those changes I have made to implement my own custom logic of bearer token verification. For some reason the SecurityStamp verification is not implemented in Webapi application created from template in VS 2013. I thought this should have been done by default.

To verify the SecurityStamp validation concept I have changed SecurityStamp in the database and after that called some webapi method from client using old bearer token, i.e. containing old SecurityStamp claim. Please note my webapi controller is annotated with [Authorize] attribute. After that ValidateIdentity(OAuthValidateIdentityContext context) method has been called and context.Rejected() line has been executed and I was expecting the webapi method should not be called after that and the 401 Unauthorized response should be send back to the client.

But nothing of this happened. Webapi method did get called and the client did successfully get sensitive data from server whereas should not, because the old bearer token the client sent to the server for authentication and authorization must not be valid after password change.

I thought if context.Rejected() have been called in ValidateIdentity method any [Authorize] decorated webapi method should not be called and the client should receive something like 401 Unauthorized response.

Am I misunderstanding the whole thing? If I am could anyone explain how it works, please? Why after context.Rejected() has been called the [Authorize] annotated controller's webapi method gets called and successfully returns sensitive data? Why the 401 Unauthorized response has not been sent instead? How to achieve the goal which is the 401 Unauthorized response to be sent back to the client when SecurityStamp claim is not the same as in the database currently?

abatishchev
  • 98,240
  • 88
  • 296
  • 433
Sergey
  • 1,020
  • 11
  • 22

1 Answers1

2

Finally I was able to find the explanation of how the things works. It is the Hongye Sun's comment to his answer to the stackoverflow question: How do you reject a Katana Bearer token's identity

I cite it here: "UseOAuthBearerTokens will register Bearer authentication middleware and authorization server middleware into the pipeline. If you call both methods, you will register two Bearer auth middlewares. You need to call UseOAuthAuthorizationServer to register authorization server only."

So I replaced this line of code:

app.UseOAuthBearerTokens(OAuthOptions);

To this one:

app.UseOAuthAuthorizationServer(OAuthOptions);

And things started to work as they should. I. e. the [Authorize] annotated controller's webapi method not called and the 401 Unauthorized response sent back after context.Rejected() has been called.

Community
  • 1
  • 1
Sergey
  • 1,020
  • 11
  • 22