yes it works.. thank you. I guess it's a template issue then?
yes done that, same issue
Reproduction Steps:
Expected Behavior:
After logging out and back in, TestUser should have Guest permissions only, with admin privileges revoked. According to the ABP documentation, role changes should take effect at the next request or at least after reauthentication. For us it doesn't even work with a logout/login
Actual Behavior:
Role changes are not applied even after logout/login. It seems role claims are cached or not refreshed properly.
Only solution we found right now is creating a redis cache service and deleting user cache (not ideal)..
Hi! We're preparing to go live soon—do you have an ETA on when this might be resolved? Thanks in advance!
okay thank you, will wait for an update, have a good day!
Thank you for your answer, I implemented the solution and am still facing the same issue :
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using MyProject.MauiBlazor.Consts;
using MyProject.MauiBlazor.Storage;
using IdentityModel.OidcClient;
using Microsoft.AspNetCore.Components.Authorization;
using Volo.Abp.Account.Pro.Public.MauiBlazor.OAuth;
using LoginResult = Volo.Abp.Account.Pro.Public.MauiBlazor.OAuth.LoginResult;
namespace MyProject.MauiBlazor.OAuth;
public class MyProjectCodeFlowExternalAuthService : ExternalAuthServiceBase
{
private readonly OidcClient _oidcClient;
private readonly IStorage _storage;
private readonly AuthenticationStateProvider _authenticationStateProvider;
public MyProjectCodeFlowExternalAuthService(
IAccessTokenStore accessTokenStore,
IStorage storage,
OidcClient oidcClient,
AuthenticationStateProvider authenticationStateProvider)
: base(accessTokenStore)
{
_oidcClient = oidcClient;
_storage = storage;
_authenticationStateProvider = authenticationStateProvider;
}
public override async Task<LoginResult> LoginAsync(LoginInput input)
{
var loginResult = await _oidcClient.LoginAsync(new LoginRequest());
if (loginResult.IsError)
{
return LoginResult.Failed(loginResult.Error, loginResult.ErrorDescription);
}
await SetTokenCacheAsync(loginResult.AccessToken, loginResult.RefreshToken);
var currentUser = new ClaimsPrincipal(new ClaimsIdentity(
new JwtSecurityTokenHandler().ReadJwtToken(loginResult.AccessToken).Claims,
authenticationType: AuthenticationType
));
(_authenticationStateProvider as MyProjectAuthenticationStateProvider)?.NotifyUserChanged(currentUser);
return LoginResult.Success();
}
public override async Task SignOutAsync()
{
var logoutResult = await _oidcClient.LogoutAsync();
await ClearTokenCacheAsync();
var currentUser = new ClaimsPrincipal(new ClaimsIdentity());
(_authenticationStateProvider as MyProjectAuthenticationStateProvider)?.NotifyUserChanged(currentUser);
}
public async Task<string> GetAccessTokenAsync()
{
var token = await _storage.GetAsync(OidcConsts.AccessTokenKeyName);
if (!token.IsNullOrEmpty())
{
var jwtToken = new JwtSecurityTokenHandler().ReadJwtToken(token);
if (jwtToken.ValidTo <= DateTime.UtcNow)
{
var newToken = await TryRefreshTokenAsync();
if (!newToken.IsNullOrEmpty())
{
return newToken;
}
}
}
return token;
}
public async Task<string> TryRefreshTokenAsync()
{
var refreshToken = await _storage.GetAsync(OidcConsts.RefreshTokenKeyName);
if (!refreshToken.IsNullOrEmpty())
{
var refreshResult = await _oidcClient.RefreshTokenAsync(refreshToken);
if (!refreshResult.IsError)
{
await SetTokenCacheAsync(refreshResult.AccessToken, refreshResult.RefreshToken);
// Also update the authentication state when token is refreshed
var currentUser = new ClaimsPrincipal(new ClaimsIdentity(
new JwtSecurityTokenHandler().ReadJwtToken(refreshResult.AccessToken).Claims,
authenticationType: AuthenticationType
));
(_authenticationStateProvider as MyProjectAuthenticationStateProvider)?.NotifyUserChanged(currentUser);
return refreshResult.AccessToken;
}
}
return string.Empty;
}
private async Task SetTokenCacheAsync(string accessToken, string refreshToken)
{
await AccessTokenStore.SetAccessTokenAsync(accessToken);
await _storage.SetAsync(OidcConsts.AccessTokenKeyName, accessToken);
await _storage.SetAsync(OidcConsts.RefreshTokenKeyName, refreshToken);
}
private async Task ClearTokenCacheAsync()
{
await AccessTokenStore.SetAccessTokenAsync(null);
await _storage.RemoveAsync(OidcConsts.AccessTokenKeyName);
await _storage.RemoveAsync(OidcConsts.RefreshTokenKeyName);
}
}
And this is the state provider
using Microsoft.AspNetCore.Components.Authorization;
using System.Security.Claims;
using Volo.Abp.DependencyInjection;
[Volo.Abp.DependencyInjection.Dependency(ReplaceServices = true)]
[ExposeServices(typeof(AuthenticationStateProvider))]
public class MyProjectAuthenticationStateProvider : AuthenticationStateProvider, ISingletonDependency
{
private ClaimsPrincipal _currentUser = new ClaimsPrincipal(new ClaimsIdentity());
public void NotifyUserChanged(ClaimsPrincipal user)
{
_currentUser = user;
NotifyAuthenticationStateChanged(GetAuthenticationStateAsync());
}
public override Task<AuthenticationState> GetAuthenticationStateAsync()
{
return Task.FromResult(new AuthenticationState(_currentUser));
}
}
Am I doing something wrong?
Also want to mention that I tried keeping TriggerUserChanged that comes from ExternalAuthServiceBase, no success