Skip to content

Commit 65397a0

Browse files
committed
Align IdentityServer with products commit 779b24a0dc877dc784fd2f0540c8976c00ef06cd
1 parent a175a91 commit 65397a0

9 files changed

Lines changed: 342 additions & 51 deletions

File tree

astro/src/content/docs/general/licensing.md

Lines changed: 45 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -228,7 +228,7 @@ will cause the host to log an error for every consecutive authenticated session:
228228
BFF is running in trial mode. The maximum number of allowed authenticated sessions (5) has been exceeded.
229229
230230
See https://duende.link/l/bff/trial for more information.
231-
````
231+
```
232232

233233
The trial mode session limit is not distributed or shared across multiple nodes.
234234

@@ -242,9 +242,10 @@ when trial mode is not enough.
242242

243243
## License Key
244244

245-
The license key can be configured in one of two ways:
245+
The license key can be configured in one of three ways:
246246

247247
* Via a well-known file on the file system
248+
* Via `IConfiguration` (for example, `appsettings.json` or environment variables)
248249
* Programmatically in your startup code
249250

250251
You can also use other configuration sources such as Azure Key Vault, by using the
@@ -281,6 +282,48 @@ MyIdentityServer/
281282
To verify your `ContentRootPath` at runtime, inspect `builder.Environment.ContentRootPath`.
282283
:::
283284

285+
### Configuration :badge[v8.0]
286+
287+
288+
IdentityServer can read the license key directly from `IConfiguration`, so you do not need to write any startup code.
289+
If `LicenseKey` is not set in your `AddIdentityServer` call, IdentityServer checks the following configuration keys in order,
290+
using the first non-empty value it finds:
291+
292+
1. `Duende:IdentityServer:LicenseKey`
293+
2. `Duende:LicenseKey`
294+
295+
Whitespace is trimmed, and empty or whitespace-only values are ignored.
296+
297+
Add the license key to `appsettings.json` using the IdentityServer-specific key:
298+
299+
```json title="appsettings.json"
300+
{
301+
"Duende": {
302+
"IdentityServer": {
303+
"LicenseKey": "eyJhbG..."
304+
}
305+
}
306+
}
307+
```
308+
309+
Or use the shorter key:
310+
311+
```json title="appsettings.json"
312+
{
313+
"Duende": {
314+
"LicenseKey": "eyJhbG..."
315+
}
316+
}
317+
```
318+
319+
Because `IConfiguration` supports many providers, you can also supply the key via environment variables
320+
(for example, `Duende__IdentityServer__LicenseKey` or `Duende__LicenseKey`), Azure App Configuration, Azure Key Vault,
321+
or any other configuration source.
322+
323+
:::note
324+
Loading the license key from configuration is not currently supported in Duende BFF.
325+
:::
326+
284327
### Startup
285328

286329
If you prefer to load the license key programmatically, you can do so in your startup

astro/src/content/docs/identityserver/diagnostics/data.mdx

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -650,6 +650,8 @@ Diagnostics data is written to logs in one or more chunks containing data format
650650
"ShowTokenEndpointAuthenticationMethods":true,
651651
"ExpandRelativePathsInCustomEntries":true,
652652
"ResponseCacheInterval":null,
653+
"EnableDiscoveryDocumentCache":false,
654+
"DiscoveryDocumentCacheDuration":"00:01:00",
653655
"CustomEntries":{
654656

655657
}
@@ -911,11 +913,7 @@ Diagnostics data is written to logs in one or more chunks containing data format
911913
"HS384",
912914
"HS512"
913915
],
914-
"Preview":{
915-
"EnableDiscoveryDocumentCache":false,
916-
"StrictClientAssertionAudienceValidation":false,
917-
"DiscoveryDocumentCacheDuration":"00:01:00"
918-
},
916+
"StrictClientAssertionAudienceValidation":false,
919917
"Diagnostics":{
920918
"LogFrequency":"00:10:00",
921919
"ChunkSize":8160

astro/src/content/docs/identityserver/reference/v8/options.md

Lines changed: 52 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,41 @@ Top-level settings. Available directly on the `IdentityServerOptions` object.
8686

8787
The allowed signature algorithms for client authentication using client assertions (`private_key_jwt`). The `alg` header of client assertions is validated against this collection, and the `token_endpoint_auth_signing_alg_values_supported` discovery property is populated with these values. Defaults to `[RS256, RS384, RS512, PS256, PS384, PS512, ES256, ES384, ES512]`. If set to an empty collection, all algorithms are allowed but `token_endpoint_auth_signing_alg_values_supported` will not be set.
8888

89+
- **`LicenseKey`**
90+
91+
The license key for Duende IdentityServer. Set this property to the content of your license key file.
92+
93+
If you do not set `LicenseKey` in code, IdentityServer automatically reads it from `IConfiguration` at startup.
94+
It checks the following configuration keys in order, using the first non-empty value it finds:
95+
96+
1. `Duende:IdentityServer:LicenseKey`
97+
2. `Duende:LicenseKey`
98+
99+
Whitespace is trimmed, and empty or whitespace-only values are ignored. This means you can supply the license key via `appsettings.json`,
100+
environment variables, Azure Key Vault, or any other [configuration provider](https://learn.microsoft.com/en-us/aspnet/core/fundamentals/configuration/)
101+
without writing any code.
102+
103+
See the [licensing documentation](/general/licensing.md#license-key) for all supported approaches.
104+
105+
- **`StrictClientAssertionAudienceValidation`**
106+
107+
When using [private key JWT](/identityserver/tokens/client-authentication.md#private-key-jwts)
108+
client authentication, there is a theoretical vulnerability where a relying party trusting
109+
multiple OpenID Providers could be attacked if one of those providers is malicious or compromised.
110+
The OpenID Foundation proposed a two-part fix: strictly validate the audience and set an explicit
111+
`typ` header in the authentication JWT.
112+
113+
Setting this flag to `true` strictly validates that the audience is equal to the issuer and
114+
validates the token's `typ` header, as specified in
115+
[RFC 7523 bis](https://datatracker.ietf.org/doc/draft-ietf-oauth-rfc7523bis/). Defaults to
116+
`false`.
117+
118+
When this flag is `false`, validation behavior is determined based on the `typ` header being
119+
present. When the token sets the `typ` header to `client-authentication+jwt`, IdentityServer
120+
assumes the client's intention is to apply strict audience validation. If `typ` is not present,
121+
[default audience validation](/identityserver/apis/aspnetcore/jwt.md#adding-audience-validation)
122+
is used.
123+
89124
## Key management
90125

91126
Automatic key management settings. Available on the `KeyManagement` property of the `IdentityServerOptions` object.
@@ -293,6 +328,21 @@ var idsvrBuilder = builder.Services.AddIdentityServer(options =>
293328
options.Discovery.CustomEntries.Add("my_custom_endpoint", "~/custom");
294329
```
295330

331+
- **`EnableDiscoveryDocumentCache`**
332+
333+
Enables caching of the discovery document response. In large deployments where many concurrent
334+
users consume the [discovery endpoint](/identityserver/reference/v8/endpoints/discovery.md) to
335+
retrieve metadata about your IdentityServer, enabling this cache can increase throughput.
336+
Defaults to `false`.
337+
338+
Keep the cache duration low if you use the `CustomEntries` element on the discovery document or
339+
implement a custom `IDiscoveryResponseGenerator`.
340+
341+
- **`DiscoveryDocumentCacheDuration`**
342+
343+
The duration for which the discovery document is cached when `EnableDiscoveryDocumentCache` is
344+
`true`. Defaults to 1 minute.
345+
296346
## Authentication
297347

298348
Login/logout related settings. Available on the `Authentication` property of the `IdentityServerOptions`
@@ -556,7 +606,7 @@ User interaction settings, including urls for pages in the UI, names of paramete
556606
The collection of OIDC prompt modes supported and that will be published in discovery. By
557607
default, this includes all values in `Constants.SupportedPromptModes`. If the
558608
`CreateAccountUrl` option is set, then the "create" value is also included. If additional
559-
prompt values are added, a customized [`IAuthorizeInteractionResponseGenerator"`](/identityserver/ui/custom.md) is also required to handle those values.
609+
prompt values are added, a customized [`IAuthorizeInteractionResponseGenerator`](/identityserver/ui/custom.md) is also required to handle those values.
560610

561611
## Caching
562612

@@ -737,7 +787,7 @@ Settings for [server-side sessions](/identityserver/ui/server-side-sessions/inde
737787
- **`InvalidRedirectUriPrefixes`**
738788

739789
Collection of URI scheme prefixes that should never be used as custom URI
740-
schemes in the `redirect_uri` passed to tha authorize endpoint or the
790+
schemes in the `redirect_uri` passed to the authorize endpoint or the
741791
`post_logout_redirect_uri` passed to the end_session endpoint. Defaults to
742792
_["javascript:", "file:", "data:", "mailto:", "ftp:", "blob:", "about:",
743793
"ssh:", "tel:", "view-source:", "ws:", "wss:"]_.
@@ -787,37 +837,3 @@ Demonstration of Proof-of-Possession settings. Available on the `DPoP` property
787837

788838
Maximum size of diagnostic data log message chunks in kilobytes. Defaults to 8160 bytes. 8 KB is a conservative limit for the max size of a log message that is imposed by some logging tools.
789839
We take 32 bytes less than that to allow for additional formatting of the log message.
790-
791-
## Preview Features
792-
793-
Preview Features settings. Available on the `Preview` property of the `IdentityServerOptions` object.
794-
795-
:::note
796-
Duende IdentityServer may ship preview features, which can be configured using preview options.
797-
Note that preview features can be removed and may break in future releases.
798-
:::
799-
800-
#### Discovery Document Cache
801-
802-
In large deployments of Duende IdentityServer, where a lot of concurrent users attempt to
803-
consume the [discovery endpoint](/identityserver/reference/v8/endpoints/discovery.md) to retrieve
804-
metadata about your IdentityServer, you can increase throughput by enabling the
805-
discovery document cache preview using the _`EnableDiscoveryDocumentCache`_ flag.
806-
This will cache discovery document information for the duration specified in the
807-
_`DiscoveryDocumentCacheDuration`_ option.
808-
809-
It's best to keep the cache time low if you use the _`CustomEntries`_ element on the
810-
discovery document or implement a custom _`IDiscoveryResponseGenerator`_.
811-
812-
#### Strict Audience Validation
813-
814-
When using [_private key JWT_](/identityserver/tokens/client-authentication.md#private-key-jwts),
815-
there is a theoretical vulnerability where a Relying Party trusting multiple OpenID Providers
816-
could be attacked if one of the OpenID Providers is malicious or compromised.
817-
818-
The OpenID Foundation proposed a two-part fix: strictly validate the audience and set an
819-
explicit `typ` header in the authentication JWT.
820-
821-
You can [enable strict audience validation in Duende IdentityServer](/identityserver/tokens/client-authentication.md#strict-audience-validation)
822-
using the _`StrictClientAssertionAudienceValidation`_ flag, which strictly validates that
823-
the audience is equal to the issuer and validates the token's `typ` header.

astro/src/content/docs/identityserver/reference/v8/response-handling/http-response-writer.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,12 @@ The `IHttpResponseWriter` interface is the contract for services that can produc
1313
This is a low level abstraction that is intended to be used if you need to customize the serialization, encoding, or
1414
HTTP headers in a response from a protocol endpoint.
1515

16+
IdentityServer ships a concrete implementation, `AuthorizeInteractionPageHttpWriter`, that handles redirects to login,
17+
consent, create-account, and custom interaction pages. It is public and designed to be subclassed: you can override
18+
`BuildReturnUrlAsync`, `BuildRedirectUrlAsync`, or `WriteResponseAsync` to change how those redirects are constructed
19+
and delivered. See [Customizing authorize interaction redirects](/identityserver/ui/custom-redirect.md) for a how-to
20+
guide and code examples.
21+
1622
#### Duende.IdentityServer.Hosting.IHttpResponseWriter
1723

1824
```csharp

astro/src/content/docs/identityserver/tokens/client-authentication.md

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -396,16 +396,20 @@ a victim OpenID Provider.
396396
The OpenID Foundation proposed a two-part fix: strictly validate the audience and set an
397397
explicit `typ` header (with value `client-authentication+jwt`) in the authentication JWT.
398398

399-
You can enable strict audience validation using the [
400-
*`StrictClientAssertionAudienceValidation`*](/identityserver/reference/v8/options.md#strict-audience-validation)
401-
flag, which always strictly validates that the audience is equal to the issuer and validates the token's
399+
You can enable strict audience validation by setting [`StrictClientAssertionAudienceValidation`](/identityserver/reference/v8/options.md#main)
400+
to `true`. When enabled, IdentityServer strictly validates that the audience is equal to the issuer identifier and validates the token's
402401
`typ` header, as specified in [RFC 7523 bis](https://datatracker.ietf.org/doc/draft-ietf-oauth-rfc7523bis/).
403402

404-
When *`StrictClientAssertionAudienceValidation`* is not enabled, validation behavior is determined based
403+
`StrictClientAssertionAudienceValidation` defaults to `false`. When `false`, IdentityServer accepts the following legacy audience values in addition to the issuer identifier:
404+
405+
* The token endpoint URL
406+
* The CIBA endpoint URL
407+
* The PAR endpoint URL
408+
409+
When `StrictClientAssertionAudienceValidation` is `false`, validation behavior is also determined based
405410
on the `typ` header being present. When the token sets the `typ` header to `client-authentication+jwt`,
406411
IdentityServer assumes the client's intention is to apply strict audience validation.
407-
If `typ` is not
408-
present, [default audience validation](/identityserver/apis/aspnetcore/jwt.md#adding-audience-validation)
412+
If `typ` is not present, [default audience validation](/identityserver/apis/aspnetcore/jwt.md#adding-audience-validation)
409413
is used.
410414

411415
### Mutual TLS Client Certificates
Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
---
2+
title: "Customizing authorize interaction redirects"
3+
description: "How to subclass AuthorizeInteractionPageHttpWriter to customize how IdentityServer redirects users to login, consent, and other interaction pages."
4+
date: 2026-05-08
5+
sidebar:
6+
label: "Custom redirect writer"
7+
order: 55
8+
---
9+
10+
When IdentityServer needs to send a user to an interaction page, like login, consent, create-account, or a [custom page](/identityserver/ui/custom.md),
11+
it builds a redirect URL and writes an HTTP 303 response. The class responsible for this is `AuthorizeInteractionPageHttpWriter`, which is public and designed to be subclassed.
12+
13+
You might want to customize this behavior to:
14+
15+
* Set a cookie before the redirect (for example, to carry state that survives the round-trip through the interaction page).
16+
* Append a custom query parameter to the interaction page URL (for example, a tenant identifier or a UI hint).
17+
* Change the redirect status code or add extra response headers.
18+
19+
## How it works
20+
21+
`AuthorizeInteractionPageHttpWriter` implements `IHttpResponseWriter<AuthorizeInteractionPageResult>` and exposes three virtual methods you can override independently:
22+
23+
| Method | Responsibility |
24+
|-------------------------|---------------------------------------------------------------------|
25+
| `BuildReturnUrlAsync` | Builds the URL that points back to the authorize callback endpoint. |
26+
| `BuildRedirectUrlAsync` | Combines the interaction page URL with the return URL. |
27+
| `WriteResponseAsync` | Writes the HTTP response (status code, `Location` header). |
28+
29+
The default `WriteHttpResponse` implementation calls all three in sequence. You only need to override the method that covers the behavior you want to change.
30+
31+
## Example: appending a custom query parameter
32+
33+
The example below adds a `ui_hint` query parameter to every redirect URL so the interaction page can adjust its appearance based on the originating client.
34+
35+
```csharp
36+
// CustomRedirectWriter.cs
37+
using Duende.IdentityServer.Configuration;
38+
using Duende.IdentityServer.Endpoints.Results;
39+
using Duende.IdentityServer.Hosting;
40+
using Duende.IdentityServer.Services;
41+
using Microsoft.AspNetCore.Http;
42+
using Microsoft.Extensions.Primitives;
43+
44+
public class CustomRedirectWriter : AuthorizeInteractionPageHttpWriter
45+
{
46+
public CustomRedirectWriter(
47+
IdentityServerOptions options,
48+
IServerUrls urls,
49+
IUiLocalesService localesService,
50+
IAuthorizationParametersMessageStore? authorizationParametersMessageStore = null)
51+
: base(options, urls, localesService, authorizationParametersMessageStore)
52+
{
53+
}
54+
55+
protected override async Task<string> BuildRedirectUrlAsync(
56+
AuthorizeInteractionPageResult result,
57+
string returnUrl,
58+
HttpContext context)
59+
{
60+
var redirectUrl = await base.BuildRedirectUrlAsync(result, returnUrl, context);
61+
62+
// Append a ui_hint parameter so the interaction page knows which client triggered the flow.
63+
var clientId = result.Request?.ClientId;
64+
if (!string.IsNullOrEmpty(clientId))
65+
{
66+
redirectUrl += (redirectUrl.Contains('?') ? "&" : "?")
67+
+ "ui_hint=" + Uri.EscapeDataString(clientId);
68+
}
69+
70+
return redirectUrl;
71+
}
72+
}
73+
```
74+
75+
## Example: setting a cookie before the redirect
76+
77+
Override `WriteResponseAsync` when you need to write response headers or cookies in addition to the redirect itself.
78+
79+
```csharp
80+
// CookieRedirectWriter.cs
81+
protected override Task WriteResponseAsync(HttpContext context, string redirectUrl)
82+
{
83+
// Set a short-lived cookie that the interaction page can read.
84+
context.Response.Cookies.Append("idsrv.hint", "active", new CookieOptions
85+
{
86+
HttpOnly = true,
87+
Secure = true,
88+
SameSite = SameSiteMode.Lax,
89+
MaxAge = TimeSpan.FromMinutes(5)
90+
});
91+
92+
return base.WriteResponseAsync(context, redirectUrl);
93+
}
94+
```
95+
96+
## Registering your writer
97+
98+
Register your subclass using `AddHttpWriter<TResult, TWriter>()` in your IdentityServer setup:
99+
100+
```csharp
101+
// Program.cs
102+
builder.Services.AddIdentityServer()
103+
.AddHttpWriter<AuthorizeInteractionPageResult, CustomRedirectWriter>();
104+
```
105+
106+
This replaces the default `AuthorizeInteractionPageHttpWriter` for `AuthorizeInteractionPageResult` responses. All other result types keep their default writers.
107+
108+
:::note
109+
The return URL built by `BuildReturnUrlAsync` points back into the authorize endpoint. Validate it using the [interaction service](/identityserver/reference/v8/services/interaction-service.md)
110+
before following it in your interaction page to guard against open-redirect attacks.
111+
:::

astro/src/content/docs/identityserver/ui/server-side-sessions/index.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,3 +100,10 @@ When listing sessions, prefer `GetSessionsAsync` over `QuerySessionsAsync`.
100100
The `QuerySessionsAsync` method performs a full-text search and may be slower to retrieve a list of sessions than `GetSessionsAsync`.
101101
Use `QuerySessionsAsync` only when more advanced filtering is required for the solution you are building.
102102
:::
103+
104+
### Session Overwrite and Orphaned Grant Cleanup
105+
106+
When a session cookie key is reused by a different user or a new session, IdentityServer automatically revokes the grants that belonged to the previous session.
107+
This keeps your token store clean and prevents tokens from a prior user's session from remaining valid after the session is overwritten.
108+
109+
See [Session Management](/identityserver/ui/server-side-sessions/session-management.md#orphaned-grant-revocation-on-session-overwrite) for details.

astro/src/content/docs/identityserver/ui/server-side-sessions/session-management.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,3 +106,17 @@ await _sessionManagementService.RemoveSessionsAsync(new RemoveSessionsContext {
106106
```
107107

108108
Internally this uses the `IServerSideTicketStore`, `IPersistedGrantStore` and `IBackChannelLogoutService` features from IdentityServer.
109+
110+
## Orphaned Grant Revocation on Session Overwrite
111+
112+
When a session cookie key is reused by a different user or a new session (that is, the subject ID or session ID stored in the cookie
113+
no longer matches what is in the server-side store), IdentityServer automatically revokes the grants that belonged to the previous
114+
session before writing the new one. The grants revoked are:
115+
116+
* refresh tokens
117+
* reference tokens
118+
* authorization codes
119+
* backchannel authentication requests
120+
121+
This prevents tokens from a previous user's session from remaining valid after the session is overwritten. Without this cleanup,
122+
a user whose session was silently replaced could retain working tokens even though their session no longer exists in the store.

0 commit comments

Comments
 (0)