Browse Source

Revoke unused access tokens from Blazor.

pull/19479/head
maliming 3 years ago
parent
commit
cb4ddf9211
No known key found for this signature in database GPG Key ID: A646B9CB645ECEA4
  1. 1
      framework/src/Volo.Abp.AspNetCore.Components.WebAssembly/Volo.Abp.AspNetCore.Components.WebAssembly.csproj
  2. 118
      framework/src/Volo.Abp.AspNetCore.Components.WebAssembly/Volo/Abp/AspNetCore/Components/WebAssembly/WebAssemblyAuthenticationStateProvider.cs
  3. 6
      framework/src/Volo.Abp.AspNetCore.Components.WebAssembly/Volo/Abp/AspNetCore/Components/WebAssembly/WebAssemblyAuthenticationStateProviderOptions.cs
  4. 4
      framework/src/Volo.Abp.AspNetCore.Components.WebAssembly/Volo/Abp/AspNetCore/Components/WebAssembly/WebAssemblyRemoteCurrentPrincipalAccessor.cs
  5. 2
      framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/CurrentUserDto.cs
  6. 3
      framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/AbpApplicationConfigurationAppService.cs

1
framework/src/Volo.Abp.AspNetCore.Components.WebAssembly/Volo.Abp.AspNetCore.Components.WebAssembly.csproj

@ -27,6 +27,7 @@
<PackageReference Include="Microsoft.AspNetCore.Components.Authorization" />
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Authentication" />
<PackageReference Include="Microsoft.AspNetCore.WebUtilities" />
<PackageReference Include="IdentityModel" />
</ItemGroup>
</Project>

118
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<TRemoteAuthenticationState,
where TProviderOptions : new()
where TAccount : RemoteUserAccount
{
protected ILogger<RemoteAuthenticationService<TRemoteAuthenticationState, TAccount, TProviderOptions>> Logger { get; }
protected WebAssemblyCachedApplicationConfigurationClient WebAssemblyCachedApplicationConfigurationClient { get; }
protected IOptions<WebAssemblyAuthenticationStateProviderOptions> WebAssemblyAuthenticationStateProviderOptions { get; }
protected IHttpClientFactory HttpClientFactory { get; }
[Obsolete]
public WebAssemblyAuthenticationStateProvider(
IJSRuntime jsRuntime,
IOptionsSnapshot<RemoteAuthenticationOptions<TProviderOptions>> options,
NavigationManager navigation,
AccountClaimsPrincipalFactory<TAccount> accountClaimsPrincipalFactory,
ILogger<WebAssemblyAuthenticationStateProvider<TRemoteAuthenticationState, TAccount, TProviderOptions>> logger,
WebAssemblyCachedApplicationConfigurationClient webAssemblyCachedApplicationConfigurationClient)
: base(jsRuntime, options, navigation, accountClaimsPrincipalFactory)
{
WebAssemblyCachedApplicationConfigurationClient = webAssemblyCachedApplicationConfigurationClient;
}
protected readonly static ConcurrentDictionary<string, string> AccessTokens = new ConcurrentDictionary<string, string>();
public WebAssemblyAuthenticationStateProvider(
IJSRuntime jsRuntime,
@ -35,11 +33,33 @@ public class WebAssemblyAuthenticationStateProvider<TRemoteAuthenticationState,
NavigationManager navigation,
AccountClaimsPrincipalFactory<TAccount> accountClaimsPrincipalFactory,
ILogger<RemoteAuthenticationService<TRemoteAuthenticationState, TAccount, TProviderOptions>>? logger,
ILogger<WebAssemblyAuthenticationStateProvider<TRemoteAuthenticationState, TAccount, TProviderOptions>> logger1,
WebAssemblyCachedApplicationConfigurationClient webAssemblyCachedApplicationConfigurationClient)
WebAssemblyCachedApplicationConfigurationClient webAssemblyCachedApplicationConfigurationClient,
IOptions<WebAssemblyAuthenticationStateProviderOptions> webAssemblyAuthenticationStateProviderOptions,
IHttpClientFactory httpClientFactory)
: base(jsRuntime, options, navigation, accountClaimsPrincipalFactory, logger)
{
Logger = logger ?? NullLogger<RemoteAuthenticationService<TRemoteAuthenticationState, TAccount, TProviderOptions>>.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<AuthenticationState> GetAuthenticationStateAsync()
@ -51,6 +71,76 @@ public class WebAssemblyAuthenticationStateProvider<TRemoteAuthenticationState,
await WebAssemblyCachedApplicationConfigurationClient.InitializeAsync();
}
var accessToken = await FindAccessTokenAsync();
if (!accessToken.IsNullOrWhiteSpace())
{
AccessTokens.TryAdd(accessToken, accessToken);
}
await TryRevokeOldAccessTokensAsync();
return state;
}
protected virtual async Task<string?> 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<OidcProviderOptions>();
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<TRemoteAuthenticationState, TAccount, TProviderOptions>));
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; }
}

6
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";
}

4
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())
{

2
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; }
}

3
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()
};
}

Loading…
Cancel
Save