Browse Source

Merge 2acf2ac280 into 0cd6b7b794

pull/25011/merge
lanpin 4 days ago
committed by GitHub
parent
commit
5568604d9a
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 99
      framework/src/Volo.Abp.AspNetCore/Microsoft/Extensions/DependencyInjection/CookieAuthenticationOptionsExtensions.cs
  2. 3
      framework/src/Volo.Abp.AspNetCore/Volo.Abp.AspNetCore.csproj
  3. 6
      framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/AbpAspNetCoreModule.cs

99
framework/src/Volo.Abp.AspNetCore/Microsoft/Extensions/DependencyInjection/CookieAuthenticationOptionsExtensions.cs

@ -7,6 +7,7 @@ using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Authentication.OpenIdConnect;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Volo.Abp.DistributedLocking;
using Volo.Abp.Threading;
namespace Microsoft.Extensions.DependencyInjection;
@ -35,6 +36,104 @@ public static class CookieAuthenticationOptionsExtensions
if (!tokenExpiresAt.IsNullOrWhiteSpace() && DateTimeOffset.TryParseExact(tokenExpiresAt, "o", CultureInfo.InvariantCulture, DateTimeStyles.RoundtripKind, out var expiresAt) &&
expiresAt <= DateTimeOffset.UtcNow.Add(advance.Value))
{
var refreshToken = principalContext.Properties.GetTokenValue("refresh_token");
if (refreshToken.IsNullOrWhiteSpace())
{
await SignOutAndInvokePreviousHandlerAsync(principalContext, previousHandler);
return;
}
logger.LogInformation("The access_token expires within {AdvanceSeconds}s but a refresh_token is available; attempting to refresh.", advance.Value.TotalSeconds);
var openIdConnectOptions = await GetOpenIdConnectOptions(principalContext, oidcAuthenticationScheme);
var tokenEndpoint = openIdConnectOptions.Configuration?.TokenEndpoint;
if (tokenEndpoint.IsNullOrWhiteSpace() && !openIdConnectOptions.Authority.IsNullOrWhiteSpace())
{
tokenEndpoint = openIdConnectOptions.Authority.EnsureEndsWith('/') + "connect/token";
}
if (tokenEndpoint.IsNullOrWhiteSpace())
{
logger.LogWarning("No token endpoint configured. Skipping token refresh.");
await SignOutAndInvokePreviousHandlerAsync(principalContext, previousHandler);
return;
}
var clientId = principalContext.Properties.GetString("client_id");
var clientSecret = principalContext.Properties.GetString("client_secret");
var refreshRequest = new RefreshTokenRequest
{
Address = tokenEndpoint,
ClientId = clientId ?? openIdConnectOptions.ClientId!,
ClientSecret = clientSecret ?? openIdConnectOptions.ClientSecret,
RefreshToken = refreshToken
};
var cancellationTokenProvider = principalContext.HttpContext.RequestServices.GetRequiredService<ICancellationTokenProvider>();
const int RefreshTokenLockTimeoutSeconds = 3;
const string RefreshTokenLockKeyFormat = "refresh_token_lock_{0}";
var userKey =
principalContext.Principal?.FindFirst("sub")?.Value
?? principalContext.Principal?.FindFirst(System.Security.Claims.ClaimTypes.NameIdentifier)?.Value
?? "unknown";
var lockKey = string.Format(CultureInfo.InvariantCulture, RefreshTokenLockKeyFormat, userKey);
var lockTimeout = TimeSpan.FromSeconds(RefreshTokenLockTimeoutSeconds);
var abpDistributedLock = principalContext.HttpContext.RequestServices.GetRequiredService<IAbpDistributedLock>();
await using (var handle = await abpDistributedLock.TryAcquireAsync(lockKey, lockTimeout, cancellationTokenProvider.Token))
{
if (handle != null)
{
var response = await openIdConnectOptions.Backchannel.RequestRefreshTokenAsync(refreshRequest, cancellationTokenProvider.Token);
if (response.IsError)
{
logger.LogError("Token refresh failed: {Error}", response.Error);
await SignOutAndInvokePreviousHandlerAsync(principalContext, previousHandler);
return;
}
if (response.ExpiresIn <= 0)
{
logger.LogWarning("The token endpoint response does not contain a valid expires_in value. Skipping token refresh.");
await SignOutAndInvokePreviousHandlerAsync(principalContext, previousHandler);
return;
}
if (response.AccessToken.IsNullOrWhiteSpace())
{
logger.LogWarning("The token endpoint response does not contain a new access_token. Skipping token refresh.");
await SignOutAndInvokePreviousHandlerAsync(principalContext, previousHandler);
return;
}
if (response.RefreshToken.IsNullOrWhiteSpace())
{
logger.LogInformation("The token endpoint response does not contain a new refresh_token. The old refresh_token will continue to be used until it expires.");
}
logger.LogInformation("Token refreshed successfully. Updating cookie with new tokens.");
var newTokens = new[]
{
new AuthenticationToken { Name = "access_token", Value = response.AccessToken },
new AuthenticationToken { Name = "refresh_token", Value = response.RefreshToken ?? refreshToken },
new AuthenticationToken { Name = "expires_at", Value = DateTimeOffset.UtcNow.AddSeconds(response.ExpiresIn).ToString("o", CultureInfo.InvariantCulture) }
};
principalContext.Properties.StoreTokens(newTokens);
principalContext.ShouldRenew = true;
await InvokePreviousHandlerAsync(principalContext, previousHandler);
return;
}
}
logger.LogInformation("The access_token expires within {AdvanceSeconds}s; signing out.", advance.Value.TotalSeconds);
await SignOutAndInvokePreviousHandlerAsync(principalContext, previousHandler);
return;

3
framework/src/Volo.Abp.AspNetCore/Volo.Abp.AspNetCore.csproj

@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<Project Sdk="Microsoft.NET.Sdk.Web">
<Import Project="..\..\..\configureawait.props" />
<Import Project="..\..\..\common.props" />
@ -23,6 +23,7 @@
<ProjectReference Include="..\Volo.Abp.AspNetCore.Abstractions\Volo.Abp.AspNetCore.Abstractions.csproj" />
<ProjectReference Include="..\Volo.Abp.Auditing\Volo.Abp.Auditing.csproj" />
<ProjectReference Include="..\Volo.Abp.Authorization\Volo.Abp.Authorization.csproj" />
<ProjectReference Include="..\Volo.Abp.DistributedLocking.Abstractions\Volo.Abp.DistributedLocking.Abstractions.csproj" />
<ProjectReference Include="..\Volo.Abp.ExceptionHandling\Volo.Abp.ExceptionHandling.csproj" />
<ProjectReference Include="..\Volo.Abp.Http\Volo.Abp.Http.csproj" />
<ProjectReference Include="..\Volo.Abp.Security\Volo.Abp.Security.csproj" />

6
framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/AbpAspNetCoreModule.cs

@ -1,4 +1,4 @@
using System;
using System;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting.StaticWebAssets;
using Microsoft.AspNetCore.RequestLocalization;
@ -18,6 +18,7 @@ using Volo.Abp.Security;
using Volo.Abp.Uow;
using Volo.Abp.Validation;
using Volo.Abp.VirtualFileSystem;
using Volo.Abp.DistributedLocking;
namespace Volo.Abp.AspNetCore;
@ -30,7 +31,8 @@ namespace Volo.Abp.AspNetCore;
typeof(AbpAuthorizationModule),
typeof(AbpValidationModule),
typeof(AbpExceptionHandlingModule),
typeof(AbpAspNetCoreAbstractionsModule)
typeof(AbpAspNetCoreAbstractionsModule),
typeof(AbpDistributedLockingAbstractionsModule)
)]
public class AbpAspNetCoreModule : AbpModule
{

Loading…
Cancel
Save