Hi,
When the access token expires, the user is neither signed out nor is the token refreshed. We expect that the user should be prompted to log in once the token has expired. I have attached the repository link for reproducing this issue. Please let me know if you need any additional information.
Simply run the application and leave it idle for a few minutes. The application will encounter an ‘unauthorized’ error without logging out, but it will resume functioning after a refresh.
Please advise on how to handle token expiration in a Blazor server application.
Provide us with the following info:
- ABP Framework version: v8.1
- UI Type: Blazor Server
- Database System: EF Core SQL Server
- **Tiered **: yes
Repo to reproduce: https://github.com/antosubash/ABPAccessTokenProblem
9 Answer(s)
-
0
Hi,
You can try this:
[Dependency(ReplaceServices = true)] [ExposeServices(typeof(IUserExceptionInformer))] public class MyUserExceptionInformer : IUserExceptionInformer, IScopedDependency { protected IHttpContextAccessor HttpContextAccessor { get; } public ILogger<UserExceptionInformer> Logger { get; set; } protected IUiMessageService MessageService { get; } protected IExceptionToErrorInfoConverter ExceptionToErrorInfoConverter { get; } protected AbpExceptionHandlingOptions Options { get; } protected IJSRuntime JsRuntime { get; } public MyUserExceptionInformer( IUiMessageService messageService, IExceptionToErrorInfoConverter exceptionToErrorInfoConverter, IOptions<AbpExceptionHandlingOptions> options, IHttpContextAccessor httpContextAccessor, IJSRuntime jsRuntime) { MessageService = messageService; ExceptionToErrorInfoConverter = exceptionToErrorInfoConverter; Options = options.Value; Logger = NullLogger<UserExceptionInformer>.Instance; HttpContextAccessor = httpContextAccessor; JsRuntime = jsRuntime; } public new void Inform(UserExceptionInformerContext context) { //TODO: Create sync versions of the MessageService APIs. var errorInfo = GetErrorInfo(context); if (errorInfo.Details.IsNullOrEmpty()) { MessageService.Error(errorInfo.Message!); } else { MessageService.Error(errorInfo.Details!, errorInfo.Message); } } public new async Task InformAsync(UserExceptionInformerContext context) { var errorInfo = GetErrorInfo(context); if (errorInfo.Message == "Unauthorized") { var httpContext = HttpContextAccessor.HttpContext; var openIdConnectOptions = httpContext.RequestServices.GetRequiredService<IOptionsMonitor<OpenIdConnectOptions>>().Get("oidc"); var response = await openIdConnectOptions.Backchannel.IntrospectTokenAsync(new TokenIntrospectionRequest { Address = openIdConnectOptions.Configuration?.IntrospectionEndpoint ?? openIdConnectOptions.Authority!.EnsureEndsWith('/') + "connect/introspect", ClientId = openIdConnectOptions.ClientId!, ClientSecret = openIdConnectOptions.ClientSecret, Token = await httpContext.GetTokenAsync("access_token") }); if (response.IsError || !response.IsActive) { // handle the token is not active await JsRuntime.InvokeVoidAsync("eval", "window.location.href = '/Account/Logout';"); } return; } if (errorInfo.Details.IsNullOrEmpty()) { await MessageService.Error(errorInfo.Message!); } else { await MessageService.Error(errorInfo.Details!, errorInfo.Message); } } protected virtual RemoteServiceErrorInfo GetErrorInfo(UserExceptionInformerContext context) { return ExceptionToErrorInfoConverter.Convert(context.Exception, options => { options.SendExceptionsDetailsToClients = Options.SendExceptionsDetailsToClients; options.SendStackTraceToClients = Options.SendStackTraceToClients; }); } }
public override void ConfigureServices(ServiceConfigurationContext context) { context.Services.RemoveAll(x => x.ImplementationType == typeof(UserExceptionInformer)); }
-
0
Is there a reason why the Blazor Server template does not use the standard OpenId refresh token flow to obtain new Access Tokens?
-
0
Hi,
Although the token has expired, the authorization server login status has not expired, so when you refresh the page it will challenge again and log in directly.
-
0
Hi,
thank you for your reply, although it doesn't really answer the question.
Even though we might get a proper logout working, and the user can at any time refresh the page - why should (s)he?!
We don't see it as good user experience to just trigger a logout every 30mins. That's not how websites should behave nowadays.
So is there any reason why the template doesn't do a refresh token flow and why we shouldn't? Or is it just not implemented yet?
Other Framework also refresh it in the background. Like Duende BFF uses AccessTokenManagement. Here is the RefreshUserAccessToken in Duende, for example https://github.com/DuendeSoftware/Duende.AccessTokenManagement/blob/2a6d0a60a564a590ee4f0c87c08e8406eb753e88/src/Duende.AccessTokenManagement.OpenIdConnect/UserAccessTokenManagementService.cs#L145
-
0
Hi,
You can refer to the Microsoft's official implementation https://github.com/dotnet/blazor-samples/blob/main/8.0/BlazorWebAppOidc/BlazorWebAppOidc/CookieOidcRefresher.cs https://github.com/dotnet/blazor-samples/blob/main/8.0/BlazorWebAppOidc/BlazorWebAppOidc/CookieOidcServiceCollectionExtensions.cs
-
0
Hi,
I will consider adding refresh token to Blazor UI in the next version.
-
0
https://github.com/abpframework/abp/issues/19522
-
0
Thanks, is there already an estimate for 8.2 release?
-
0
https://github.com/abpframework/abp/milestone/95 Not yet.