OpenIdDict (поток кода) - обработка истечения срока действия токена доступа - PullRequest
0 голосов
/ 21 марта 2019

Я работаю над рефакторингом в приложении ASP.Net Core 2.1 для переключения из неявного потока с помощью SPA в поток кода авторизации с помощью клиентского приложения MVC. Поскольку мы используем библиотеку OpenIDDict, я последовал документированному примеру потока кода , который был фантастическим при запуске и запуске, но я быстро обнаружил, что мои маркеры доступа истекают и (как и ожидалось) сервер ресурсов начал отклонение запросов.

Мой вопрос: Как лучше всего обновить токен доступа?

Я новичок в OpenID Connect в целом, но я теоретически понимаю шаблоны из множества доступных ресурсов. Словоблудие все еще немного непрозрачно (грант, принципал, области действия и т. Д.), Но, приведя хороший пример, я уверен, что смогу это сделать.

Заранее спасибо!

Что я пробовал:

Исходя из того, что казалось похожими вопросами, я попытался реализовать поток токенов обновления, используя пример Refresh Flow из того же источника выше. Хотя я считаю, что правильно установил параметры подключения к серверу аутентификации, я не смог найти никаких примеров этого с помощью клиента C # (в приведенном выше примере используется угловое приложение).

Редактировать: Когда я отправляю сообщение моей конечной точке токена с грантом refresh_token, я правильно получаю новый токен доступа. Моя проблема в том, что я не уверен, как лучше справиться с этим оттуда. GetTokenAsync продолжает возвращать устаревший токен.

Запуск клиента:

services.AddAuthentication(options =>
{
    options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
})                
.AddCookie(options =>
{
    options.LoginPath = new PathString("/signin");     
})
.AddOpenIdConnect(options =>
{                    
    // Note: these settings must match the application details
    // inserted in the database at the server level.
    options.ClientId = "Portal"; //TODO replace via configuration   
    options.ClientSecret = "---";                                             

    options.RequireHttpsMetadata = false;
    options.GetClaimsFromUserInfoEndpoint = true;
    options.SaveTokens = true;

    options.ResponseType = OpenIdConnectResponseType.CodeIdToken;
    options.AuthenticationMethod = OpenIdConnectRedirectBehavior.RedirectGet;                    

    // Note: setting the Authority allows the OIDC client middleware to automatically
    // retrieve the identity provider's configuration and spare you from setting
    // the different endpoints URIs or the token validation parameters explicitly.
    options.Authority = "https://localhost:57851"; //TODO replace via configuration

    options.Scope.Add("email");
    options.Scope.Add("roles");                     
    options.Scope.Add("offline_access");

    options.SecurityTokenValidator = new JwtSecurityTokenHandler
    {
        // Disable the built-in JWT claims mapping feature.
        InboundClaimTypeMap = new Dictionary<string, string>()
    };

    options.TokenValidationParameters.NameClaimType = "name";
    options.TokenValidationParameters.RoleClaimType = "role";
});

Auth Startup:

.AddServer(options =>
            {
                // Register the ASP.NET Core MVC services used by OpenIddict.
                // Note: if you don't call this method, you won't be able to
                // bind OpenIdConnectRequest or OpenIdConnectResponse parameters.
                options.UseMvc();

                // Enable the authorization, logout, token and userinfo endpoints.
                options.EnableAuthorizationEndpoint("/connect/authorize")
                    .EnableLogoutEndpoint("/connect/logout")
                    .EnableTokenEndpoint("/connect/token")
                    .EnableUserinfoEndpoint("/api/userinfo");

                options
                    .AllowAuthorizationCodeFlow()
                    .AllowRefreshTokenFlow();

                // Mark the "email", "profile" and "roles" scopes as supported scopes.
                options.RegisterScopes(
                    OpenIdConnectConstants.Scopes.Email,
                    OpenIdConnectConstants.Scopes.Profile,
                    OpenIddictConstants.Scopes.Roles,
                    OpenIddictConstants.Scopes.OfflineAccess);

                // When request caching is enabled, authorization and logout requests
                // are stored in the distributed cache by OpenIddict and the user agent
                // is redirected to the same page with a single parameter (request_id).
                // This allows flowing large OpenID Connect requests even when using
                // an external authentication provider like Google, Facebook or Twitter.
                options.EnableRequestCaching();

                // During development, you can disable the HTTPS requirement.
                if (env.IsDevelopment())
                {
                    options.DisableHttpsRequirement();
                    options.AddEphemeralSigningKey(); // TODO: In production, use a X.509 certificate ?
                }

                options.SetAccessTokenLifetime(TimeSpan.FromMinutes(openIdConnectConfig.AccessTokenLifetimeInMinutes));
                options.SetRefreshTokenLifetime(TimeSpan.FromHours(12));                    
            })
            .AddValidation();

Дескриптор:

var descriptor = new OpenIddictApplicationDescriptor{
ClientId = config.Id,
ClientSecret = config.Secret,
DisplayName = config.DisplayName,                    
PostLogoutRedirectUris = { new Uri($"{config.ClientOrigin}/signout-callback-oidc") },
RedirectUris = { new Uri($"{config.ClientOrigin}/signin-oidc") },
Permissions =
{
    OpenIddictConstants.Permissions.Endpoints.Authorization,
    OpenIddictConstants.Permissions.Endpoints.Logout,
    OpenIddictConstants.Permissions.Endpoints.Token,
    OpenIddictConstants.Permissions.GrantTypes.AuthorizationCode,                        
    OpenIddictConstants.Permissions.GrantTypes.RefreshToken,                        
    OpenIddictConstants.Permissions.Scopes.Email,
    OpenIddictConstants.Permissions.Scopes.Profile,
    OpenIddictConstants.Permissions.Scopes.Roles
}};

Конечная точка токена:

if (request.IsRefreshTokenGrantType()){
// Retrieve the claims principal stored in the refresh token.
var info = await HttpContext.AuthenticateAsync(OpenIdConnectServerDefaults.AuthenticationScheme);

// Retrieve the user profile corresponding to the refresh token.
// Note: if you want to automatically invalidate the refresh token
// when the user password/roles change, use the following line instead:
// var user = _signInManager.ValidateSecurityStampAsync(info.Principal);
var user = await _userManager.GetUserAsync(info.Principal);
if (user == null)
{
    return BadRequest(new OpenIdConnectResponse
    {
        Error = OpenIdConnectConstants.Errors.InvalidGrant,
        ErrorDescription = "The refresh token is no longer valid."
    });
}

// Ensure the user is still allowed to sign in.
if (!await _signInManager.CanSignInAsync(user))
{
    return BadRequest(new OpenIdConnectResponse
    {
        Error = OpenIdConnectConstants.Errors.InvalidGrant,
        ErrorDescription = "The user is no longer allowed to sign in."
    });
}

// Create a new authentication ticket, but reuse the properties stored
// in the refresh token, including the scopes originally granted.
var ticket = await CreateTicketAsync(request, user, info.Properties);
ticket.SetScopes(OpenIdConnectConstants.Scopes.OfflineAccess);      

return SignIn(ticket.Principal, ticket.Properties, ticket.AuthenticationScheme);}
...