Repository navigation
Docs for Storage/Spaces/IdentityServer/User Management preview.6 releases - #1268
Erwinvandervalk wants to merge 1 commit into
Conversation
…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
left a comment
There was a problem hiding this comment.
Thanks @Erwinvandervalk ! Left some comments.
| 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. |
There was a problem hiding this comment.
This sentence hints at something. How does it wait? What is the TimeProvider's role here and how can it drive the purge schedule?
| `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. |
There was a problem hiding this comment.
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`: |
There was a problem hiding this comment.
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)
| 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. |
There was a problem hiding this comment.
| 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 |
There was a problem hiding this comment.
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")`. |
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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`. |
There was a problem hiding this comment.
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. |
There was a problem hiding this comment.
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. |
There was a problem hiding this comment.
| 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. |
|
@Erwinvandervalk Can you rebase onto |
Updates the docs for the preview release published to nuget.org on October 16, 2026:
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), andum-2.0.0-preview.3..um-2.0.0-preview.6.Spaces
spaces/getting-started.md: Spaces is now added withAddSpaces(options => ...)onIIdentityServerBuilder, replacingservices.AddSpaces()andConfigure<SpacesOptions>. Adds a caution that using Spaces with the Entity Framework stores fails at startup withOptionsValidationException. Explains that Data Protection is partitioned per space (SpacesOptions.PartitionDataProtection,GetUnpartitioned()), and covers the keyedHybridCacheunderSpacesServiceProviderKeys.SpacesCache: Spaces no longer registers the app's defaultHybridCache.spaces/index.mdx: The "What Spaces Does Not Isolate" section now says that Data Protection and space-awareHybridCacheentries are isolated by default, and that IdentityServer signing keys stay global.Storage
duende-storage/getting-started.md:duende storage migratenow supports--provider oracle, so the old "no Oracle in the CLI" caveat is removed. Also notes that the purge service follows the registeredTimeProvider.duende-storage/schemas.md: New "List Schemas" section.ISchemaAdmin.QueryAsyncreturns real schema ids. Schemas from earlier previews are upgraded the first time they're read by id, and until then they're reported asunknown.duende-storage/multiple-storage-instances.md: Fixed the sample to call.AddSpaces(...)on the IdentityServer builder.diagnostics/otel.md: Documents theDuende.Storagemeter (duende.storage.operation.countandduende.storage.operation.duration, with their tags) and theDuende.Storageactivity 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.EnableJwtRequestUriSSRF link and the per-instance purge/outbox behavior were already documented inreference/v8/options.md,tokens/jar.mdxandmultiple-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 aChallengeSizebelow 16 fails at startup.reference/configuration.md:ChallengeSizenow says it fails options validation at startup instead of throwingArgumentOutOfRangeException.Not changed
AttributeSchemaandAttributeSchemaFactory:AttributeSchemawas already internal, no page referenced it, andAttributeSchemaFactoryis in theDuende.Storage.EntityAttributeValue.Internalnamespace, so the docs don't cover it.--prerelease.Verified with a local
npx astro build, which succeeded.