Как обрабатывать токен доступа с истекшим сроком действия в asp.net ядро, использующее токен обновления с OpenId Connect
Я настроил сервер Asos OpenIdConnect, используя и asp.net core MVC приложение, которое использует " Microsoft.AspNetCore.Идентификация.OpenIdConnect": "1.0.0 и" Microsoft.AspNetCore.Идентификация.Cookies": "1.0.0". Я протестировал рабочий процесс "код авторизации", и все работает.
клиентское веб-приложение обрабатывает аутентификацию, как ожидалось, и создает файл cookie, хранящий id_token, access_token и refresh_token.
как заставить Microsoft.AspNetCore.Идентификация.OpenIdConnect запросить новый access_token, когда он истекает?
asp.net приложение core mvc игнорирует истекший access_token.
Я хотел бы, чтобы openidconnect увидел истекший access_token, а затем сделал вызов с помощью токена обновления, чтобы получить новый access_token. Он также должен обновлять значения cookie. Если запрос токена обновления не выполняется, я ожидаю, что openidconnect "выйдет" из cookie (удалите его или нечто.)
app.UseCookieAuthentication(new CookieAuthenticationOptions
        {
            AutomaticAuthenticate = true,
            AutomaticChallenge = true,
            AuthenticationScheme = "Cookies"
        });
app.UseOpenIdConnectAuthentication(new OpenIdConnectOptions
        {
            ClientId = "myClient",
            ClientSecret = "secret_secret_secret",
            PostLogoutRedirectUri = "http://localhost:27933/",
            RequireHttpsMetadata = false,
            GetClaimsFromUserInfoEndpoint = true,
            SaveTokens = true,
            ResponseType = OpenIdConnectResponseType.Code,
            AuthenticationMethod = OpenIdConnectRedirectBehavior.RedirectGet,
            Authority = http://localhost:27933,
            MetadataAddress = "http://localhost:27933/connect/config",
            Scope = { "email", "roles", "offline_access" },
        });
2 ответов
Кажется, что в аутентификации openidconnect нет программирования для asp.net ядро для управления access_token на сервере после получения.
Я обнаружил, что могу перехватить событие проверки cookie и проверить, истек ли токен доступа. Если это так, выполните ручной HTTP-вызов конечной точки маркера с помощью grant_type=refresh_token.
на вызывающий контекст.ShouldRenew = true; это приведет к обновлению cookie и отправке обратно клиенту в ответ.
Я предоставил основу того, что я сделал, и буду работать над обновлением этого ответа после того, как все работы будут решены.
app.UseCookieAuthentication(new CookieAuthenticationOptions
        {
            AutomaticAuthenticate = true,
            AutomaticChallenge = true,
            AuthenticationScheme = "Cookies",
            ExpireTimeSpan = new TimeSpan(0, 0, 20),
            SlidingExpiration = false,
            CookieName = "WebAuth",
            Events = new CookieAuthenticationEvents()
            {
                OnValidatePrincipal = context =>
                {
                    if (context.Properties.Items.ContainsKey(".Token.expires_at"))
                    {
                        var expire = DateTime.Parse(context.Properties.Items[".Token.expires_at"]);
                        if (expire > DateTime.Now) //TODO:change to check expires in next 5 mintues.
                        {
                            logger.Warn($"Access token has expired, user: {context.HttpContext.User.Identity.Name}");
                            //TODO: send refresh token to ASOS. Update tokens in context.Properties.Items
                            //context.Properties.Items["Token.access_token"] = newToken;
                            context.ShouldRenew = true;
                        }
                    }
                    return Task.FromResult(0);
                }
            }
        });
вы должны включить генерацию refresh_token, установив в startup.cs:
- установка значений AuthorizationEndpointPath = "/ connect / authorize"; / / необходимо для refreshtoken
- установка значений в TokenEndpointPath = "/ connect / token"; / / стандартное имя конечной точки токена
в поставщике маркеров перед проверкой запроса маркера в конце метода HandleTokenrequest убедитесь, что вы установили автономный область применения:
        // Call SetScopes with the list of scopes you want to grant
        // (specify offline_access to issue a refresh token).
        ticket.SetScopes(
            OpenIdConnectConstants.Scopes.Profile,
            OpenIdConnectConstants.Scopes.OfflineAccess);
если это правильно настроено, вы должны получить refresh_token обратно при входе в систему с паролем grant_type.
затем от вашего клиента вы должны выдать следующий запрос (я использую Aurelia):
refreshToken() {
    let baseUrl = yourbaseUrl;
    let data = "client_id=" + this.appState.clientId
               + "&grant_type=refresh_token"
               + "&refresh_token=myRefreshToken";
    return this.http.fetch(baseUrl + 'connect/token', {
        method: 'post',
        body : data,
        headers: {
            'Content-Type': 'application/x-www-form-urlencoded',
            'Accept': 'application/json' 
        }
    });
}
и это все, убедитесь, что ваш поставщик auth в HandleRequestToken не пытается манипулировать запросом, который имеет тип refresh_token:
    public override async Task HandleTokenRequest(HandleTokenRequestContext context)
    {
        if (context.Request.IsPasswordGrantType())
        {
            // Password type request processing only
            // code that shall not touch any refresh_token request
        }
        else if(!context.Request.IsRefreshTokenGrantType())
        {
            context.Reject(
                    error: OpenIdConnectConstants.Errors.InvalidGrant,
                    description: "Invalid grant type.");
            return;
        }
        return;
    }
refresh_token должен просто пройти через это метод и обрабатывается другой частью промежуточного программного обеспечения, которая обрабатывает refresh_token.
если вы хотите получить более глубокие знания о том, что делает сервер аутентификации, вы можете взглянуть на код OpenIdConnectServerHandler:
на стороне клиента, вы также должны быть в состоянии обработайте автоматическое обновление маркера, вот пример перехватчика http для Angular 1.X, где обрабатывается 401 ответ, обновите токен, затем повторите запрос:
'use strict';
app.factory('authInterceptorService',
    ['$q', '$injector', '$location', 'localStorageService',
    function ($q, $injector, $location, localStorageService) {
    var authInterceptorServiceFactory = {};
    var $http;
    var _request = function (config) {
        config.headers = config.headers || {};
        var authData = localStorageService.get('authorizationData');
        if (authData) {
            config.headers.Authorization = 'Bearer ' + authData.token;
        }
        return config;
    };
    var _responseError = function (rejection) {
        var deferred = $q.defer();
        if (rejection.status === 401) {
            var authService = $injector.get('authService');
            console.log("calling authService.refreshToken()");
            authService.refreshToken().then(function (response) {
                console.log("token refreshed, retrying to connect");
                _retryHttpRequest(rejection.config, deferred);
            }, function () {
                console.log("that didn't work, logging out.");
                authService.logOut();
                $location.path('/login');
                deferred.reject(rejection);
            });
        } else {
            deferred.reject(rejection);
        }
        return deferred.promise;
    };
    var _retryHttpRequest = function (config, deferred) {
        console.log('autorefresh');
        $http = $http || $injector.get('$http');
        $http(config).then(function (response) {
            deferred.resolve(response);
        },
        function (response) {
            deferred.reject(response);
        });
    }
    authInterceptorServiceFactory.request = _request;
    authInterceptorServiceFactory.responseError = _responseError;
    authInterceptorServiceFactory.retryHttpRequest = _retryHttpRequest;
    return authInterceptorServiceFactory;
}]);
и вот пример, который я только что сделал для Aurelia, на этот раз я обернул свой http-клиент в HTTP-обработчик, который проверяет, истек ли токен или нет. Если он истек, он сначала обновит токен, а затем выполнит запрос. Он использует обещание сохранить интерфейс со службами данных на стороне клиента последовательный. Этот обработчик предоставляет тот же интерфейс, что и клиент Aurelia-fetch.
import {inject} from 'aurelia-framework';
import {HttpClient} from 'aurelia-fetch-client';
import {AuthService} from './authService';
@inject(HttpClient, AuthService)
export class HttpHandler {
    constructor(httpClient, authService) {
        this.http = httpClient;
        this.authService = authService;
    }
    fetch(url, options){
        let _this = this;
        if(this.authService.tokenExpired()){
            console.log("token expired");
            return new Promise(
                function(resolve, reject) {
                    console.log("refreshing");
                    _this.authService.refreshToken()
                    .then(
                       function (response) {
                           console.log("token refreshed");
                        _this.http.fetch(url, options).then(
                            function (success) { 
                                console.log("call success", url);
                                resolve(success);
                            }, 
                            function (error) { 
                                console.log("call failed", url);
                                reject(error); 
                            }); 
                       }, function (error) {
                           console.log("token refresh failed");
                           reject(error);
                    });
                }
            );
        } 
        else {
            // token is not expired, we return the promise from the fetch client
            return this.http.fetch(url, options); 
        }
    }
}
для jquery вы можете посмотреть jquery oAuth:
https://github.com/esbenp/jquery-oauth
надеюсь, что это помогает.
