mirror of https://github.com/abpframework/abp.git
committed by
GitHub
5 changed files with 157 additions and 30 deletions
@ -0,0 +1,100 @@ |
|||||
|
using System; |
||||
|
using System.Globalization; |
||||
|
using System.Threading.Tasks; |
||||
|
using IdentityModel.Client; |
||||
|
using Microsoft.AspNetCore.Authentication; |
||||
|
using Microsoft.AspNetCore.Authentication.Cookies; |
||||
|
using Microsoft.AspNetCore.Authentication.OpenIdConnect; |
||||
|
using Microsoft.Extensions.Logging; |
||||
|
using Microsoft.Extensions.Options; |
||||
|
|
||||
|
namespace Microsoft.Extensions.DependencyInjection; |
||||
|
|
||||
|
public static class CookieAuthenticationOptionsExtensions |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Check the access_token is expired or inactive.
|
||||
|
/// </summary>
|
||||
|
public static CookieAuthenticationOptions CheckTokenExpiration(this CookieAuthenticationOptions options, string oidcAuthenticationScheme = "oidc", TimeSpan? advance = null, TimeSpan? validationInterval = null) |
||||
|
{ |
||||
|
advance ??= TimeSpan.FromMinutes(3); |
||||
|
validationInterval ??= TimeSpan.FromMinutes(1); |
||||
|
options.Events.OnValidatePrincipal = async principalContext => |
||||
|
{ |
||||
|
if (principalContext.Principal == null || principalContext.Principal.Identity == null || !principalContext.Principal.Identity.IsAuthenticated) |
||||
|
{ |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
var logger = principalContext.HttpContext.RequestServices.GetRequiredService<ILogger<CookieAuthenticationOptions>>(); |
||||
|
|
||||
|
var tokenExpiresAt = principalContext.Properties.Items[".Token.expires_at"]; |
||||
|
if (DateTimeOffset.TryParseExact(tokenExpiresAt, "o", null, DateTimeStyles.RoundtripKind, out var expiresAt) && |
||||
|
expiresAt < DateTimeOffset.UtcNow.Subtract(advance.Value)) |
||||
|
{ |
||||
|
logger.LogInformation("The access_token is expired."); |
||||
|
await SignOutAsync(principalContext); |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
if (principalContext.Properties.IssuedUtc != null && DateTimeOffset.UtcNow.Subtract(principalContext.Properties.IssuedUtc.Value) > validationInterval) |
||||
|
{ |
||||
|
logger.LogInformation($"Check the access_token is active every {validationInterval.Value.TotalSeconds} seconds."); |
||||
|
var accessToken = principalContext.Properties.GetTokenValue("access_token"); |
||||
|
if (!accessToken.IsNullOrWhiteSpace()) |
||||
|
{ |
||||
|
var openIdConnectOptions = await GetOpenIdConnectOptions(principalContext, oidcAuthenticationScheme); |
||||
|
|
||||
|
var response = await openIdConnectOptions.Backchannel.IntrospectTokenAsync(new TokenIntrospectionRequest |
||||
|
{ |
||||
|
Address = openIdConnectOptions.Configuration?.IntrospectionEndpoint ?? openIdConnectOptions.Authority.EnsureEndsWith('/') + "connect/introspect", |
||||
|
ClientId = openIdConnectOptions.ClientId, |
||||
|
ClientSecret = openIdConnectOptions.ClientSecret, |
||||
|
Token = accessToken |
||||
|
}); |
||||
|
|
||||
|
if (response.IsError) |
||||
|
{ |
||||
|
logger.LogError(response.Error); |
||||
|
await SignOutAsync(principalContext); |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
if (!response.IsActive) |
||||
|
{ |
||||
|
logger.LogError("The access_token is not active."); |
||||
|
await SignOutAsync(principalContext); |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
logger.LogInformation("The access_token is active."); |
||||
|
principalContext.ShouldRenew = true; |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
logger.LogError("The access_token is not found in the cookie properties, Please make sure SaveTokens of OpenIdConnectOptions is set as true."); |
||||
|
await SignOutAsync(principalContext); |
||||
|
} |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
return options; |
||||
|
} |
||||
|
|
||||
|
private async static Task<OpenIdConnectOptions> GetOpenIdConnectOptions(CookieValidatePrincipalContext principalContext, string oidcAuthenticationScheme) |
||||
|
{ |
||||
|
var openIdConnectOptions = principalContext.HttpContext.RequestServices.GetRequiredService<IOptionsMonitor<OpenIdConnectOptions>>().Get(oidcAuthenticationScheme); |
||||
|
if (openIdConnectOptions.Configuration == null && openIdConnectOptions.ConfigurationManager != null) |
||||
|
{ |
||||
|
openIdConnectOptions.Configuration = await openIdConnectOptions.ConfigurationManager.GetConfigurationAsync(principalContext.HttpContext.RequestAborted); |
||||
|
} |
||||
|
|
||||
|
return openIdConnectOptions; |
||||
|
} |
||||
|
|
||||
|
private async static Task SignOutAsync(CookieValidatePrincipalContext principalContext) |
||||
|
{ |
||||
|
principalContext.RejectPrincipal(); |
||||
|
await principalContext.HttpContext.SignOutAsync(principalContext.Scheme.Name); |
||||
|
} |
||||
|
} |
||||
Loading…
Reference in new issue