Почему метод контроллера webapi вызывается после вызова context.Rejected () в ValidateIdentity (контекст OAuthValidateIdentityContext)

В моем приложении webapi, созданном из шаблона в VS2013, я добавил собственный класс OAuthBearerAuthenticationProvider в файл Startup.Auth.cs:

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);
    }
}

Также я добавил переменную:

public static OAuthBearerAuthenticationOptions OAuthBearerOptions { get; private set; }

А в методе ConfigureAuth (приложение IAppBuilder) я добавил следующие строки кода для использования настраиваемого класса OAuthBearerAuthenticationProvider:

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);

Все изменения, которые я внес для реализации собственной логики проверки токена на предъявителя. По какой-то причине проверка SecurityStamp не реализована в приложении Webapi, созданном из шаблона в VS 2013. Я думал, что это должно было быть сделано по умолчанию.

Чтобы проверить концепцию проверки SecurityStamp, я изменил SecurityStamp в базе данных и после этого вызвал некоторый метод webapi с клиента, используя старый токен-носитель, то есть содержащий старое утверждение SecurityStamp. Обратите внимание, что мой контроллер webapi помечен атрибутом [Authorize]. После этого был вызван метод ValidateIdentity (OAuthValidateIdentityContext context) и была выполнена строка context.Rejected (), и я ожидал, что после этого метод webapi не должен вызываться, а ответ 401 Unauthorized должен быть отправлен обратно клиенту.

Но ничего этого не произошло. Метод Webapi действительно был вызван, и клиент успешно получил конфиденциальные данные с сервера, тогда как не должен, потому что старый токен-носитель, который клиент отправил на сервер для аутентификации и авторизации, не должен быть действительным после изменения пароля.

Я подумал, что если context.Rejected () был вызван в методе ValidateIdentity, любой [Authorize] декорированный метод webapi не должен вызываться, и клиент должен получить что-то вроде 401 Unauthorized response.

Я все неправильно понимаю? Если можно, кто-нибудь может объяснить, как это работает, пожалуйста? Почему после вызова context.Rejected () вызывается метод webapi аннотированного контроллера [Authorize], который успешно возвращает конфиденциальные данные? Почему вместо этого не был отправлен ответ 401 Unauthorized? Как достичь цели, заключающейся в том, что ответ 401 Unauthorized должен быть отправлен обратно клиенту, когда запрос SecurityStamp не такой, как в базе данных в настоящее время?


person Sergey    schedule 28.01.2015    source источник


Ответы (1)


Наконец я смог найти объяснение того, как все работает. Это комментарий Хонье Сан к его ответу на вопрос о переполнении стека: Как отказаться от идентификации токена на предъявителя Katana

Я цитирую это здесь: «UseOAuthBearerTokens зарегистрирует промежуточное ПО для проверки подлинности носителя и промежуточное ПО сервера авторизации в конвейере. Если вы вызовете оба метода, вы зарегистрируете два промежуточного программного обеспечения проверки подлинности носителя. Вам нужно вызвать UseOAuthAuthorizationServer только для регистрации сервера авторизации».

Поэтому я заменил эту строку кода:

app.UseOAuthBearerTokens(OAuthOptions);

К этому:

app.UseOAuthAuthorizationServer(OAuthOptions);

И все заработало как надо. I. e. аннотированный метод webapi контроллера [Authorize] не вызывается, а ответ 401 Unauthorized отправлен обратно после вызова context.Rejected ().

person Sergey    schedule 02.02.2015