Skip to content

Docs for Storage/Spaces/IdentityServer/User Management preview.6 releases - #1268

Draft
Erwinvandervalk wants to merge 1 commit into
mainfrom
ev/session-3158b4d7
Draft

Erwinvandervalk wants to merge 1 commit into
mainfrom
ev/session-3158b4d7

Conversation

@Erwinvandervalk

Copy link
Copy Markdown
Contributor

Updates the docs for the preview release published to nuget.org on October 16, 2026:

  • Duende Storage 2.0.0-preview.6 (replaces 2.0.0-preview.5)
  • Duende Spaces 1.0.0-preview.6
  • Duende IdentityServer 8.1.0-preview.6
  • Duende User Management 2.0.0-preview.6

Every change was checked against the code and PublicApi snapshots in products-private: storage-2.0.0-preview.3..storage-2.0.0-preview.6, spaces-1.0.0-preview.3..spaces-1.0.0-preview.6, is-8.1.0-preview.4..360f647a (the released is-8.1.0-preview.6 commit), and um-2.0.0-preview.3..um-2.0.0-preview.6.

Spaces

  • spaces/getting-started.md: Spaces is now added with AddSpaces(options => ...) on IIdentityServerBuilder, replacing services.AddSpaces() and Configure<SpacesOptions>. Adds a caution that using Spaces with the Entity Framework stores fails at startup with OptionsValidationException. Explains that Data Protection is partitioned per space (SpacesOptions.PartitionDataProtection, GetUnpartitioned()), and covers the keyed HybridCache under SpacesServiceProviderKeys.SpacesCache: Spaces no longer registers the app's default HybridCache.
  • spaces/index.mdx: The "What Spaces Does Not Isolate" section now says that Data Protection and space-aware HybridCache entries are isolated by default, and that IdentityServer signing keys stay global.

Storage

  • duende-storage/getting-started.md: duende storage migrate now supports --provider oracle, so the old "no Oracle in the CLI" caveat is removed. Also notes that the purge service follows the registered TimeProvider.
  • duende-storage/schemas.md: New "List Schemas" section. ISchemaAdmin.QueryAsync returns real schema ids. Schemas from earlier previews are upgraded the first time they're read by id, and until then they're reported as unknown.
  • duende-storage/multiple-storage-instances.md: Fixed the sample to call .AddSpaces(...) on the IdentityServer builder.
  • diagnostics/otel.md: Documents the Duende.Storage meter (duende.storage.operation.count and duende.storage.operation.duration, with their tags) and the Duende.Storage activity source.

IdentityServer

  • data/providers/entityframework-core.md: Adds a caution that the EF stores can't be combined with Spaces as of 8.1.0-preview.6.
  • The EnableJwtRequestUri SSRF link and the per-instance purge/outbox behavior were already documented in reference/v8/options.md, tokens/jar.mdx and multiple-storage-instances.md.

User Management

  • authentication/passkeys.mdx: New sections on custom signature verifiers (ISignatureVerifier, AddPasskeySignatureVerifier<T>()) and custom attestation format validators (IAttestationFormatValidator, AttestationContext, AttestationValidationResult, AddAttestationFormatValidator<T>()). Also notes that a ChallengeSize below 16 fails at startup.
  • reference/configuration.md: ChallengeSize now says it fails options validation at startup instead of throwing ArgumentOutOfRangeException.

Not changed

  • AttributeSchema and AttributeSchemaFactory: AttributeSchema was already internal, no page referenced it, and AttributeSchemaFactory is in the Duende.Storage.EntityAttributeValue.Internal namespace, so the docs don't cover it.
  • No page pins a package version; the install commands use --prerelease.

Verified with a local npx astro build, which succeeded.

…or preview.6

Duende Storage 2.0.0-preview.6, Duende Spaces 1.0.0-preview.6, IdentityServer 8.1.0-preview.6, User Management 2.0.0-preview.6.

@maartenba maartenba left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @Erwinvandervalk ! Left some comments.

Comment on lines +143 to +144
The purge service waits on the registered `TimeProvider`, like the outbox processor. If you replace `TimeProvider`, for
example with a fake clock in integration tests, it drives the purge schedule too.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This sentence hints at something. How does it wait? What is the TimeProvider's role here and how can it drive the purge schedule?

Comment on lines +255 to +259
`ISchemaAdmin.QueryAsync` returns a page of `SchemaSummary` records, each with the schema's real `SchemaId`. A schema
created by an earlier preview of Duende Storage is upgraded to carry its id the first time it is read by id
(`ISchemaAdmin.GetAsync` or `ISchemaStore.GetAsync`); until that first read happens, `QueryAsync` reports that schema's id
as unknown. Read every schema you expect to administer by id at least once after an upgrade, so `QueryAsync` results are
complete.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need the "earlier preview" language here? Let's assume this is the first time someone used the library. As someone using this library for the first time I have no clear understanding what this paragraph means.

Register Spaces and the IdentityServer storage adapters. `AddStorage` selects the database provider;
`AddConfigurationStorage()` and `AddOperationalStorage()` route configuration and operational data to it:
`AddConfigurationStorage()` and `AddOperationalStorage()` route configuration and operational data to it. Add Spaces
with the `AddSpaces` extension on `IIdentityServerBuilder`:

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sentence ends with a colon and alludes code sample is to follow. Consider moving the caution after the code block (or before the code introduction sentence)

Comment on lines +193 to +196
Spaces registers its own keyed `HybridCache` under `SpacesServiceProviderKeys.SpacesCache` instead of registering or
sharing the app's default `HybridCache`. If your app already relies on an unkeyed `HybridCache` for its own caching,
call `AddHybridCache()` yourself; Spaces no longer does this for you. Entries created through `IHybridCacheFactory`
are automatically partitioned by the resolved space, so callers don't need to build space-aware cache keys themselves.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Spaces registers its own keyed `HybridCache` under `SpacesServiceProviderKeys.SpacesCache` instead of registering or
sharing the app's default `HybridCache`. If your app already relies on an unkeyed `HybridCache` for its own caching,
call `AddHybridCache()` yourself; Spaces no longer does this for you. Entries created through `IHybridCacheFactory`
are automatically partitioned by the resolved space, so callers don't need to build space-aware cache keys themselves.
Spaces registers its own keyed `HybridCache` under `SpacesServiceProviderKeys.SpacesCache` instead of registering or
sharing the app's default `HybridCache`. If your app already relies on an unkeyed `HybridCache` for its own caching,
call `AddHybridCache()` yourself. Entries created through `IHybridCacheFactory`
are automatically partitioned by the resolved space, so callers don't need to build space-aware cache keys themselves.

Language like "Spaces no longer does this for you" is not required between previews.

credentials, ASP.NET Core Data Protection, external caches, telemetry, rate limits and custom services to decide whether
:::caution[Spaces Do Not Isolate Every Process-Level Dependency]
Spaces isolate persisted data and, by default, [ASP.NET Core Data Protection](/general/data-protection.md) and keyed
`HybridCache` entries created through `IHybridCacheFactory`. They do not automatically isolate every process-level

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IHybridCacheFactory and SpacesServiceProviderKeys.SpacesCache appear here without being explained. A short sample would help, such as [FromKeyedServices(SpacesServiceProviderKeys.SpacesCache)] HybridCache cache or a call through IHybridCacheFactory.

:::

When IdentityServer, User Management or Spaces data is stored in [Duende Storage](/identityserver/data/providers/duende-storage/index.mdx),
Duende Storage emits its own metrics from the meter named `Duende.Storage`. Collect them with `AddMeter("Duende.Storage")`.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need a constant for that meter?

`AAGUID` values for well-known authenticators are published by the FIDO Alliance in the [FIDO Metadata Service](https://fidoalliance.org/metadata/). You can use the metadata service to look up AAGUIDs for specific authenticator models and build your allowlist from there.
:::

## Custom Signature Verifiers

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ed25519SignatureVerifier and AndroidKeyAttestationFormatValidator are used but never defined, so readers may think they ship with the product. Either add a minimal implementation, or say clearly that they are example class names you need to implement yourself.


## Custom Signature Verifiers

Each WebAuthn credential signs its registration and authentication ceremonies using a COSE algorithm identifier. User Management ships verifiers for the common algorithms (ES256, ES384, ES512, RS256, RS384, RS512, PS256, PS384, PS512, and RS1), but you can add a verifier for an algorithm that is not built in by implementing `ISignatureVerifier`.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

RS1 is listed among the built-in algorithms. Should we warn that it's SHA-1, only there for older authenticators, and show how to leave it out using SupportedAlgorithms?

```

* `Algorithm` - the COSE algorithm identifier this verifier handles (see [IANA COSE Algorithms](https://www.iana.org/assignments/cose/cose.xhtml#algorithms))
* `VerifySignature` - verifies `signature` over `data` using the COSE-encoded public key in `publicKeyCbor`. `data` is the exact signed payload before algorithm-specific hashing: for authentication assertions and packed attestation this is `authenticatorData || clientDataHash`, and for TPM attestation it is `certInfo`. The method should return `false` on failure rather than throwing.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

VerifySignature(..., byte[]? signature): when is signature null, and should implementations return false in that case?

* `InvalidCertificate` - a certificate in the attestation chain is invalid
* `AaguidMismatch` - the certificate AAGUID does not match the authenticator data

Implementations must use a fixed, non-empty `Format` and be resolvable from the root service provider. They are retained by the singleton authentication options validator, so scoped dependencies are not supported, and constructors and format lookup must not depend directly or indirectly on `UserAuthenticationOptions`, to avoid circular validation.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Implementations must use a fixed, non-empty `Format` and be resolvable from the root service provider. They are retained by the singleton authentication options validator, so scoped dependencies are not supported, and constructors and format lookup must not depend directly or indirectly on `UserAuthenticationOptions`, to avoid circular validation.
Implementations must use a fixed, non-empty `Format` and be resolvable from the root service provider. They are retained by the singleton authentication options validator, so scoped dependencies are not supported. Don't inject `IOptions<UserAuthenticationOptions>` into your validator to avoid circular validation.

@maartenba maartenba added the documentation Improvements or additions to documentation label Oct 19, 2026
@maartenba

Copy link
Copy Markdown
Member

@Erwinvandervalk Can you rebase onto main and consider using the versioning support to have switches between vCurrent (8.0) and vNew (8.1)?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants