Пример #1
0
        /// <inheritdoc/>
        public async Task <TokenResponse> RefreshUserAccessTokenAsync(
            string refreshToken,
            UserAccessTokenParameters parameters = null,
            CancellationToken cancellationToken  = default)
        {
            _logger.LogDebug("Refreshing refresh token: {token}", refreshToken);

            parameters ??= new UserAccessTokenParameters();

            var requestDetails = await _configService.GetRefreshTokenRequestAsync(parameters);

            requestDetails.RefreshToken = refreshToken;

#if NET5_0
            requestDetails.Options.TryAdd(AccessTokenManagementDefaults.AccessTokenParametersOptionsName, parameters);
#elif NETCOREAPP3_1
            requestDetails.Properties[AccessTokenManagementDefaults.AccessTokenParametersOptionsName] = parameters;
#endif

            if (!string.IsNullOrEmpty(parameters.Resource))
            {
                requestDetails.Resource.Add(parameters.Resource);
            }

            var httpClient = _httpClientFactory.CreateClient(AccessTokenManagementDefaults.BackChannelHttpClientName);
            return(await httpClient.RequestRefreshTokenAsync(requestDetails, cancellationToken));
        }
Пример #2
0
        /// <inheritdoc/>
        public async Task <UserAccessToken> GetTokenAsync(ClaimsPrincipal user, UserAccessTokenParameters parameters = null)
        {
            parameters ??= new UserAccessTokenParameters();
            var result = await _contextAccessor.HttpContext.AuthenticateAsync(parameters.SignInScheme);

            if (!result.Succeeded)
            {
                return(null);
            }

            if (result.Properties == null)
            {
                return(null);
            }

            var tokens = result.Properties.Items.Where(i => i.Key.StartsWith(TokenPrefix)).ToList();

            if (tokens == null || !tokens.Any())
            {
                throw new InvalidOperationException(
                          "No tokens found in cookie properties. SaveTokens must be enabled for automatic token refresh.");
            }

            var tokenName = $"{TokenPrefix}{OpenIdConnectParameterNames.AccessToken}";

            if (!string.IsNullOrEmpty(parameters.Resource))
            {
                tokenName += $"::{parameters.Resource}";
            }

            var expiresName = $"{TokenPrefix}expires_at";

            if (!string.IsNullOrEmpty(parameters.Resource))
            {
                expiresName += $"::{parameters.Resource}";
            }

            var accessToken  = tokens.SingleOrDefault(t => t.Key == tokenName);
            var refreshToken = tokens.SingleOrDefault(t => t.Key == $"{TokenPrefix}{OpenIdConnectParameterNames.RefreshToken}");
            var expiresAt    = tokens.SingleOrDefault(t => t.Key == expiresName);

            DateTimeOffset?dtExpires = null;

            if (expiresAt.Value != null)
            {
                dtExpires = DateTimeOffset.Parse(expiresAt.Value, CultureInfo.InvariantCulture);
            }

            return(new UserAccessToken
            {
                AccessToken = accessToken.Value,
                RefreshToken = refreshToken.Value,
                Expiration = dtExpires
            });
        }
Пример #3
0
        /// <summary>
        /// Set an access token on the HTTP request
        /// </summary>
        /// <param name="request"></param>
        /// <param name="forceRenewal"></param>
        /// <returns></returns>
        protected virtual async Task SetTokenAsync(HttpRequestMessage request, bool forceRenewal)
        {
            var parameters = new UserAccessTokenParameters
            {
                SignInScheme    = _parameters.SignInScheme,
                ChallengeScheme = _parameters.ChallengeScheme,
                Resource        = _parameters.Resource,
                ForceRenewal    = forceRenewal,
                Context         = _parameters.Context
            };

            var token = await _httpContextAccessor.HttpContext.GetUserAccessTokenAsync(parameters);

            if (!string.IsNullOrWhiteSpace(token))
            {
                request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token);
            }
        }
Пример #4
0
        /// <inheritdoc/>
        public async Task RevokeRefreshTokenAsync(
            ClaimsPrincipal user,
            UserAccessTokenParameters parameters = null,
            CancellationToken cancellationToken  = default)
        {
            parameters ??= new UserAccessTokenParameters();
            var userToken = await _userTokenStore.GetTokenAsync(user);

            if (!string.IsNullOrEmpty(userToken?.RefreshToken))
            {
                var response = await _tokenEndpointService.RevokeRefreshTokenAsync(userToken.RefreshToken, parameters, cancellationToken);

                if (response.IsError)
                {
                    _logger.LogError("Error revoking refresh token. Error = {error}", response.Error);
                }
            }
        }
Пример #5
0
        /// <inheritdoc />
        public virtual async Task <RefreshTokenRequest> GetRefreshTokenRequestAsync(UserAccessTokenParameters parameters = null)
        {
            var(options, configuration) = await GetOpenIdConnectSettingsAsync(parameters?.ChallengeScheme ?? _userAccessTokenManagementOptions.SchemeName);

            var requestDetails = new RefreshTokenRequest
            {
                Address = configuration.TokenEndpoint,

                ClientId     = options.ClientId,
                ClientSecret = options.ClientSecret
            };

            var assertion = await CreateAssertionAsync();

            if (assertion != null)
            {
                requestDetails.ClientAssertion = assertion;
            }

            return(requestDetails);
        }
Пример #6
0
        /// <inheritdoc/>
        public async Task StoreTokenAsync(ClaimsPrincipal user, string accessToken, DateTimeOffset expiration,
                                          string refreshToken = null, UserAccessTokenParameters parameters = null)
        {
            parameters ??= new UserAccessTokenParameters();
            var result = await _contextAccessor.HttpContext.AuthenticateAsync(parameters.SignInScheme);

            if (!result.Succeeded)
            {
                throw new Exception("Can't store tokens. User is anonymous");
            }

            // in case you want to filter certain claims before re-issuing the authentication session
            var transformedPrincipal = await FilterPrincipalAsync(result.Principal);

            var tokenName = OpenIdConnectParameterNames.AccessToken;

            if (!string.IsNullOrEmpty(parameters.Resource))
            {
                tokenName += $"::{parameters.Resource}";
            }

            var expiresName = "expires_at";

            if (!string.IsNullOrEmpty(parameters.Resource))
            {
                expiresName += $"::{parameters.Resource}";
            }

            result.Properties.Items[$".Token.{tokenName}"]   = accessToken;
            result.Properties.Items[$".Token.{expiresName}"] = expiration.ToString("o", CultureInfo.InvariantCulture);

            if (refreshToken != null)
            {
                result.Properties.UpdateTokenValue(OpenIdConnectParameterNames.RefreshToken, refreshToken);
            }

            await _contextAccessor.HttpContext.SignInAsync(parameters.SignInScheme, transformedPrincipal, result.Properties);
        }
Пример #7
0
        /// <inheritdoc/>
        public async Task <TokenRevocationResponse> RevokeRefreshTokenAsync(
            string refreshToken,
            UserAccessTokenParameters parameters = null,
            CancellationToken cancellationToken  = default)
        {
            _logger.LogDebug("Revoking refresh token: {token}", refreshToken);

            parameters ??= new UserAccessTokenParameters();

            var requestDetails = await _configService.GetTokenRevocationRequestAsync(parameters);

            requestDetails.Token         = refreshToken;
            requestDetails.TokenTypeHint = OidcConstants.TokenTypes.RefreshToken;

#if NET5_0_OR_GREATER
            requestDetails.Options.TryAdd(AccessTokenManagementDefaults.AccessTokenParametersOptionsName, parameters);
#else
            requestDetails.Properties[AccessTokenManagementDefaults.AccessTokenParametersOptionsName] = parameters;
#endif

            var httpClient = _httpClientFactory.CreateClient(AccessTokenManagementDefaults.BackChannelHttpClientName);
            return(await httpClient.RevokeTokenAsync(requestDetails, cancellationToken));
        }
Пример #8
0
        /// <inheritdoc/>
        public async Task StoreTokenAsync(ClaimsPrincipal user, string accessToken, DateTimeOffset expiration,
                                          string refreshToken = null, UserAccessTokenParameters parameters = null)
        {
            parameters ??= new UserAccessTokenParameters();
            var result = await _contextAccessor.HttpContext.AuthenticateAsync(parameters.SignInScheme);

            if (!result.Succeeded)
            {
                throw new Exception("Can't store tokens. User is anonymous");
            }

            var tokenName = OpenIdConnectParameterNames.AccessToken;

            if (!string.IsNullOrEmpty(parameters.Resource))
            {
                tokenName += $"::{parameters.Resource}";
            }

            var expiresName = "expires_at";

            if (!string.IsNullOrEmpty(parameters.Resource))
            {
                expiresName += $"::{parameters.Resource}";
            }

            result.Properties.Items[$".Token.{tokenName}"]   = accessToken;
            result.Properties.Items[$".Token.{expiresName}"] = expiration.ToString("o", CultureInfo.InvariantCulture);

            if (refreshToken != null)
            {
                result.Properties.UpdateTokenValue(OpenIdConnectParameterNames.RefreshToken, refreshToken);
            }

            // can not work in blazor server project. better way to signin?
            // ref https://github.com/IdentityModel/IdentityModel.AspNetCore/issues/186
            // await _contextAccessor.HttpContext.SignInAsync(parameters.SignInScheme, result.Principal, result.Properties);
        }
Пример #9
0
 /// <summary>
 /// ctor
 /// </summary>
 /// <param name="httpContextAccessor"></param>
 /// <param name="parameters"></param>
 public UserAccessTokenHandler(IHttpContextAccessor httpContextAccessor, UserAccessTokenParameters parameters = null)
 {
     _httpContextAccessor = httpContextAccessor;
     _parameters          = parameters ?? new UserAccessTokenParameters();
 }
Пример #10
0
 /// <inheritdoc/>
 public Task ClearTokenAsync(ClaimsPrincipal user, UserAccessTokenParameters parameters = null)
 {
     // todo
     return(Task.CompletedTask);
 }
Пример #11
0
        /// <inheritdoc/>
        public async Task <string> GetUserAccessTokenAsync(
            ClaimsPrincipal user,
            UserAccessTokenParameters parameters = null,
            CancellationToken cancellationToken  = default)
        {
            parameters ??= new UserAccessTokenParameters();

            if (user == null || !user.Identity.IsAuthenticated)
            {
                return(null);
            }

            var userName  = user.FindFirst(JwtClaimTypes.Name)?.Value ?? user.FindFirst(JwtClaimTypes.Subject)?.Value ?? "unknown";
            var userToken = await _userTokenStore.GetTokenAsync(user, parameters);

            if (userToken == null)
            {
                _logger.LogDebug("No token data found in user token store.");
                return(null);
            }

            if (userToken.AccessToken.IsPresent() && userToken.RefreshToken.IsMissing())
            {
                _logger.LogDebug("No refresh token found in user token store for user {user} / resource {resource}. Returning current access token.", userName, parameters.Resource ?? "default");
                return(userToken.AccessToken);
            }

            if (userToken.AccessToken.IsMissing() && userToken.RefreshToken.IsPresent())
            {
                _logger.LogDebug(
                    "No access token found in user token store for user {user} / resource {resource}. Trying to refresh.",
                    userName, parameters.Resource ?? "default");
            }

            var dtRefresh = DateTimeOffset.MinValue;

            if (userToken.Expiration.HasValue)
            {
                dtRefresh = userToken.Expiration.Value.Subtract(_options.User.RefreshBeforeExpiration);
            }

            if (dtRefresh < _clock.UtcNow || parameters.ForceRenewal)
            {
                _logger.LogDebug("Token for user {user} needs refreshing.", userName);

                try
                {
                    return(await UserRefreshDictionary.GetOrAdd(userToken.RefreshToken, _ =>
                    {
                        return new Lazy <Task <string> >(async() =>
                        {
                            var refreshed = await RefreshUserAccessTokenAsync(user, parameters, cancellationToken);
                            return refreshed.AccessToken;
                        });
                    }).Value);
                }
                finally
                {
                    UserRefreshDictionary.TryRemove(userToken.RefreshToken, out _);
                }
            }

            return(userToken.AccessToken);
        }
Пример #12
0
        private async Task <TokenResponse> RefreshUserAccessTokenAsync(ClaimsPrincipal user, UserAccessTokenParameters parameters, CancellationToken cancellationToken = default)
        {
            var userToken = await _userTokenStore.GetTokenAsync(user);

            var response = await _tokenEndpointService.RefreshUserAccessTokenAsync(userToken.RefreshToken, parameters, cancellationToken);

            if (!response.IsError)
            {
                var expiration = DateTime.UtcNow + TimeSpan.FromSeconds(response.ExpiresIn);

                await _userTokenStore.StoreTokenAsync(user, response.AccessToken, expiration, response.RefreshToken, parameters);
            }
            else
            {
                _logger.LogError("Error refreshing access token. Error = {error}", response.Error);
            }

            return(response);
        }
Пример #13
0
        /// <inheritdoc />
        public virtual async Task <TokenRevocationRequest> GetTokenRevocationRequestAsync(UserAccessTokenParameters parameters = null)
        {
            var(options, configuration) = await GetOpenIdConnectSettingsAsync(parameters?.ChallengeScheme ?? _userAccessTokenManagementOptions.SchemeName);

            var requestDetails = new TokenRevocationRequest
            {
                Address = configuration.AdditionalData[OidcConstants.Discovery.RevocationEndpoint].ToString(),

                ClientId     = options.ClientId,
                ClientSecret = options.ClientSecret
            };

            var assertion = await CreateAssertionAsync();

            if (assertion != null)
            {
                requestDetails.ClientAssertion = assertion;
            }

            return(requestDetails);
        }