diff --git a/framework/src/Volo.Abp.AspNetCore.Components.WebAssembly/Volo.Abp.AspNetCore.Components.WebAssembly.csproj b/framework/src/Volo.Abp.AspNetCore.Components.WebAssembly/Volo.Abp.AspNetCore.Components.WebAssembly.csproj
index 8d720a19ee..d9e5081421 100644
--- a/framework/src/Volo.Abp.AspNetCore.Components.WebAssembly/Volo.Abp.AspNetCore.Components.WebAssembly.csproj
+++ b/framework/src/Volo.Abp.AspNetCore.Components.WebAssembly/Volo.Abp.AspNetCore.Components.WebAssembly.csproj
@@ -27,6 +27,7 @@
+
diff --git a/framework/src/Volo.Abp.AspNetCore.Components.WebAssembly/Volo/Abp/AspNetCore/Components/WebAssembly/WebAssemblyAuthenticationStateProvider.cs b/framework/src/Volo.Abp.AspNetCore.Components.WebAssembly/Volo/Abp/AspNetCore/Components/WebAssembly/WebAssemblyAuthenticationStateProvider.cs
index dc6b3d3536..ee9fdcc523 100644
--- a/framework/src/Volo.Abp.AspNetCore.Components.WebAssembly/Volo/Abp/AspNetCore/Components/WebAssembly/WebAssemblyAuthenticationStateProvider.cs
+++ b/framework/src/Volo.Abp.AspNetCore.Components.WebAssembly/Volo/Abp/AspNetCore/Components/WebAssembly/WebAssemblyAuthenticationStateProvider.cs
@@ -1,9 +1,15 @@
using System;
+using System.Collections.Concurrent;
+using System.Linq;
+using System.Net.Http;
+using System.Text.Json.Serialization;
using System.Threading.Tasks;
+using IdentityModel.Client;
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Authorization;
using Microsoft.AspNetCore.Components.WebAssembly.Authentication;
using Microsoft.Extensions.Logging;
+using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Options;
using Microsoft.JSInterop;
@@ -14,20 +20,12 @@ public class WebAssemblyAuthenticationStateProvider> Logger { get; }
protected WebAssemblyCachedApplicationConfigurationClient WebAssemblyCachedApplicationConfigurationClient { get; }
+ protected IOptions WebAssemblyAuthenticationStateProviderOptions { get; }
+ protected IHttpClientFactory HttpClientFactory { get; }
- [Obsolete]
- public WebAssemblyAuthenticationStateProvider(
- IJSRuntime jsRuntime,
- IOptionsSnapshot> options,
- NavigationManager navigation,
- AccountClaimsPrincipalFactory accountClaimsPrincipalFactory,
- ILogger> logger,
- WebAssemblyCachedApplicationConfigurationClient webAssemblyCachedApplicationConfigurationClient)
- : base(jsRuntime, options, navigation, accountClaimsPrincipalFactory)
- {
- WebAssemblyCachedApplicationConfigurationClient = webAssemblyCachedApplicationConfigurationClient;
- }
+ protected readonly static ConcurrentDictionary AccessTokens = new ConcurrentDictionary();
public WebAssemblyAuthenticationStateProvider(
IJSRuntime jsRuntime,
@@ -35,11 +33,33 @@ public class WebAssemblyAuthenticationStateProvider accountClaimsPrincipalFactory,
ILogger>? logger,
- ILogger> logger1,
- WebAssemblyCachedApplicationConfigurationClient webAssemblyCachedApplicationConfigurationClient)
+ WebAssemblyCachedApplicationConfigurationClient webAssemblyCachedApplicationConfigurationClient,
+ IOptions webAssemblyAuthenticationStateProviderOptions,
+ IHttpClientFactory httpClientFactory)
: base(jsRuntime, options, navigation, accountClaimsPrincipalFactory, logger)
{
+ Logger = logger ?? NullLogger>.Instance;
+
WebAssemblyCachedApplicationConfigurationClient = webAssemblyCachedApplicationConfigurationClient;
+ WebAssemblyAuthenticationStateProviderOptions = webAssemblyAuthenticationStateProviderOptions;
+ HttpClientFactory = httpClientFactory;
+
+ AuthenticationStateChanged += async state =>
+ {
+ var user = await state;
+ if (user.User.Identity == null || !user.User.Identity.IsAuthenticated)
+ {
+ return;
+ }
+
+ var accessToken = await FindAccessTokenAsync();
+ if (!accessToken.IsNullOrWhiteSpace())
+ {
+ AccessTokens.TryAdd(accessToken, accessToken);
+ }
+
+ await TryRevokeOldAccessTokensAsync();
+ };
}
public async override Task GetAuthenticationStateAsync()
@@ -51,6 +71,76 @@ public class WebAssemblyAuthenticationStateProvider FindAccessTokenAsync()
+ {
+ var result = await RequestAccessToken();
+ if (result.Status != AccessTokenResultStatus.Success)
+ {
+ return null;
+ }
+
+ result.TryGetToken(out var token);
+ return token?.Value;
+ }
+
+ protected virtual async Task TryRevokeOldAccessTokensAsync()
+ {
+ if (AccessTokens.Count <= 1)
+ {
+ return;
+ }
+
+ var oidcProviderOptions = Options.ProviderOptions?.As();
+ var authority = oidcProviderOptions?.Authority;
+ var clientId = oidcProviderOptions?.ClientId;
+
+ if (authority.IsNullOrWhiteSpace() || clientId.IsNullOrWhiteSpace())
+ {
+ return;
+ }
+
+ var revokeAccessTokens = AccessTokens.Select(x => x.Value);
+ var currentAccessToken = await FindAccessTokenAsync();
+ foreach (var accessToken in revokeAccessTokens)
+ {
+ if (accessToken == currentAccessToken)
+ {
+ continue;
+ }
+
+ var httpClient = HttpClientFactory.CreateClient(nameof(WebAssemblyAuthenticationStateProvider));
+ var result = await httpClient.RevokeTokenAsync(new TokenRevocationRequest
+ {
+ Address = authority.EnsureEndsWith('/') + WebAssemblyAuthenticationStateProviderOptions.Value.TokenRevocationUrl,
+ ClientId = clientId,
+ Token = accessToken,
+ });
+
+ if (!result.IsError)
+ {
+ AccessTokens.TryRemove(accessToken, out _);
+ }
+ else
+ {
+ Logger.LogError(result.Raw);
+ }
+ }
+ }
+}
+
+internal class OidcUser
+{
+ [JsonPropertyName("access_token")]
+ public string? AccessToken { get; set; }
}
diff --git a/framework/src/Volo.Abp.AspNetCore.Components.WebAssembly/Volo/Abp/AspNetCore/Components/WebAssembly/WebAssemblyAuthenticationStateProviderOptions.cs b/framework/src/Volo.Abp.AspNetCore.Components.WebAssembly/Volo/Abp/AspNetCore/Components/WebAssembly/WebAssemblyAuthenticationStateProviderOptions.cs
new file mode 100644
index 0000000000..c8d904d872
--- /dev/null
+++ b/framework/src/Volo.Abp.AspNetCore.Components.WebAssembly/Volo/Abp/AspNetCore/Components/WebAssembly/WebAssemblyAuthenticationStateProviderOptions.cs
@@ -0,0 +1,6 @@
+namespace Volo.Abp.AspNetCore.Components.WebAssembly;
+
+public class WebAssemblyAuthenticationStateProviderOptions
+{
+ public string TokenRevocationUrl { get; set; } = "connect/revocat";
+}
diff --git a/framework/src/Volo.Abp.AspNetCore.Components.WebAssembly/Volo/Abp/AspNetCore/Components/WebAssembly/WebAssemblyRemoteCurrentPrincipalAccessor.cs b/framework/src/Volo.Abp.AspNetCore.Components.WebAssembly/Volo/Abp/AspNetCore/Components/WebAssembly/WebAssemblyRemoteCurrentPrincipalAccessor.cs
index e96e315144..b94dc89966 100644
--- a/framework/src/Volo.Abp.AspNetCore.Components.WebAssembly/Volo/Abp/AspNetCore/Components/WebAssembly/WebAssemblyRemoteCurrentPrincipalAccessor.cs
+++ b/framework/src/Volo.Abp.AspNetCore.Components.WebAssembly/Volo/Abp/AspNetCore/Components/WebAssembly/WebAssemblyRemoteCurrentPrincipalAccessor.cs
@@ -75,6 +75,10 @@ public class WebAssemblyRemoteCurrentPrincipalAccessor : CurrentPrincipalAccesso
{
claims.Add(new Claim(AbpClaimTypes.PhoneNumberVerified, applicationConfiguration.CurrentUser.PhoneNumberVerified.ToString()));
}
+ if (applicationConfiguration.CurrentUser.SessionId != null)
+ {
+ claims.Add(new Claim(AbpClaimTypes.SessionId, applicationConfiguration.CurrentUser.SessionId));
+ }
if (!applicationConfiguration.CurrentUser.Roles.IsNullOrEmpty())
{
diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/CurrentUserDto.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/CurrentUserDto.cs
index 63a4b7a0e9..9870165e8c 100644
--- a/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/CurrentUserDto.cs
+++ b/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/CurrentUserDto.cs
@@ -34,4 +34,6 @@ public class CurrentUserDto
public bool PhoneNumberVerified { get; set; }
public string[] Roles { get; set; } = default!;
+
+ public string? SessionId { get; set; }
}
diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/AbpApplicationConfigurationAppService.cs b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/AbpApplicationConfigurationAppService.cs
index 38d68aa96c..f00c4ac250 100644
--- a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/AbpApplicationConfigurationAppService.cs
+++ b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/AbpApplicationConfigurationAppService.cs
@@ -158,7 +158,8 @@ public class AbpApplicationConfigurationAppService : ApplicationService, IAbpApp
EmailVerified = _currentUser.EmailVerified,
PhoneNumber = _currentUser.PhoneNumber,
PhoneNumberVerified = _currentUser.PhoneNumberVerified,
- Roles = _currentUser.Roles
+ Roles = _currentUser.Roles,
+ SessionId = _currentUser.FindSessionId()
};
}