Welcome to this Quickstart for Duende IdentityServer!
The previous quickstart introduced API access with interactive applications, but by far the most complex task for a typical client is to manage the access token.
In addition to the written steps below a YouTube video is available:
Given that the access token has a finite lifetime, you typically want to
ASP.NET Core has built-in facilities that can help you with some of those tasks (like caching or sessions), but there is still quite some work left to do. Duende.AccessTokenManagement can help. It provides abstractions for storing tokens, automatic refresh of expired tokens, etc.
To allow the web client to request a refresh token set the AllowOfflineAccess property to true in the client configuration.
Update the Client in src/IdentityServer/Config.cs as follows:
new Client
{
ClientId = "web",
ClientSecrets = { new Secret("secret".Sha256()) },
AllowedGrantTypes = GrantTypes.Code,
// where to redirect to after login
RedirectUris = { "https://localhost:5002/signin-oidc" },
// where to redirect to after logout
PostLogoutRedirectUris = { "https://localhost:5002/signout-callback-oidc" },
AllowOfflineAccess = true,
AllowedScopes =
{
IdentityServerConstants.StandardScopes.OpenId,
IdentityServerConstants.StandardScopes.Profile,
"verification",
"api1"
}
}
To get the refresh token the offline_access scope has to be requested by the client.
In src/WebClient/Program.cs add the scope to the scope list:
options.Scope.Add("offline_access");
When running the solution the refresh token should now be visible under Properties on the landing page of the client.
In the WebClient project add a reference to the NuGet package Duende.AccessTokenManagement.OpenIdConnect
and in Program.cs add the needed types to dependency injection:
builder.Services.AddOpenIdConnectAccessTokenManagement();
In CallApi.cshtml.cs update the method body of OnGet
as follows:
public async Task OnGet()
{
var tokenInfo = await HttpContext.GetUserAccessTokenAsync();
var client = new HttpClient();
client.SetBearerToken(tokenInfo.AccessToken!);
var content = await client.GetStringAsync("https://localhost:6001/identity");
var parsed = JsonDocument.Parse(content);
var formatted = JsonSerializer.Serialize(parsed, new JsonSerializerOptions { WriteIndented = true });
Json = formatted;
}
There are two changes here that utilize the AccessTokenManagement NuGet package:
On each call to OnGet in CallApi.cshtml.cs a new HttpClient is created in the code above. Recommended however is to use the HttpClientFactory pattern so that instances can be reused.
Duende.AccessTokenManagement.OpenIdConnect builds on top of HttpClientFactory to create HttpClient instances that automatically retrieve the needed access token and refresh if needed.
In the client in Program.cs under the call to AddOpenIdConnectAccessTokenManagement register the HttpClient:
builder.Services.AddUserAccessTokenHttpClient("apiClient", configureClient: client =>
{
client.BaseAddress = new Uri("https://localhost:6001");
});
Now the OnGet method in CallApi.cshtml.cs can be even more straightforward:
public class CallApiModel(IHttpClientFactory httpClientFactory) : PageModel
{
public string Json = string.Empty;
public async Task OnGet()
{
var client = httpClientFactory.CreateClient("apiClient");
var content = await client.GetStringAsync("https://localhost:6001/identity");
var parsed = JsonDocument.Parse(content);
var formatted = JsonSerializer.Serialize(parsed, new JsonSerializerOptions { WriteIndented = true });
Json = formatted;
}
}
Note that: