-
Notifications
You must be signed in to change notification settings - Fork 68
Blazor Server
Blazor Server applications have the same token management requirements like a regular ASP.NET Core web application. Due to the fact that Blazor puts an abstraction layer between the Blazor application and the hosting/rendering engine, you cannot use HttpContext
.
This means
- you cannot use the
HttpContext
extension methods - you cannot use the ASP.NET authentication session to store tokens
- you cannot use HTTP message handlers to automatically attach tokens to API calls
This document describes some workarounds. Also see the BlazorServer
sample for the full source code.
Since the tokens cannot be managed in the authentication session, you need to provide an alternative store like in-memory or a database. The OpenID Connect provides event from where you can get the initial tokens after authentication:
public class OidcEvents : OpenIdConnectEvents
{
private readonly IUserTokenStore _store;
public OidcEvents(IUserTokenStore store)
{
_store = store;
}
public override async Task TokenValidated(TokenValidatedContext context)
{
var exp = DateTimeOffset.UtcNow.AddSeconds(Double.Parse(context.TokenEndpointResponse!.ExpiresIn));
await _store.StoreTokenAsync(context.Principal!, new UserToken
{
AccessToken = context.TokenEndpointResponse.AccessToken,
Expiration = exp,
RefreshToken = context.TokenEndpointResponse.RefreshToken,
Scope = context.TokenEndpointResponse.Scope
});
await base.TokenValidated(context);
}
}
Blazor has its own abstraction to access the current user, it's called the AuthenticationStateProvider
.
The following is a sample API client that retrieves the token for the current user from the token management service and calls an API:
public class RemoteApiService
{
private readonly HttpClient _client;
private readonly AuthenticationStateProvider _authenticationStateProvider;
private readonly IUserTokenManagementService _tokenManagementService;
public RemoteApiService(
HttpClient client,
AuthenticationStateProvider authenticationStateProvider,
IUserTokenManagementService tokenManagementService)
{
_client = client;
_authenticationStateProvider = authenticationStateProvider;
_tokenManagementService = tokenManagementService;
}
public async Task<Data> GetData()
{
var request = new HttpRequestMessage(HttpMethod.Get, "endpoint");
var response = await SendRequestAsync(request);
// rest omitted
}
private async Task<HttpResponseMessage> SendRequestAsync(HttpRequestMessage request)
{
var state = await _authenticationStateProvider.GetAuthenticationStateAsync();
var token = await _tokenManagementService.GetAccessTokenAsync(state.User);
request.SetBearerToken(token.AccessToken);
return await _client.SendAsync(request);
}
}
This wrapper could then be registered with the DI system, e.g.:
builder.Services.AddTransient<RemoteApiService>();
builder.Services.AddHttpClient<RemoteApiService>(client =>
{
client.BaseAddress = new Uri("https://api.company.com/invoices/");
});