Migration — 0.4.0 OidcAppConfig (unified one-declaration config)
Migration — 0.4.0 OidcAppConfig (unified one-declaration config)
Status. Breaking — ships on the 0.4.0 coordinated minor bump alongside the re-applied Phase 69a Remoting sweep. Consumers must update their preset call sites + (eventually) their ClientConfig/ServerConfig wiring. The 0.3.x advisory at 0.3.x-oidc-presets.md introduced the preset shape additively; 0.4.0 collapses the preset return type onto the new unified record.
What changed
A new package ToolUp.AuthProviders.Oidc.Shared (NuGet ToolUp.AuthProviders.Oidc.Shared) ships at 0.4.0, owning the cross-side OidcAppConfig record and the PresetKind provenance DU. Existing companion packages (ToolUp.AuthProviders.Oidc.Client, ToolUp.AuthProviders.Oidc server) gain a transitive dependency on the new package.
Breaking surface changes
| 0.3.x | 0.4.0 |
|---|---|
OidcPresets.entraWorkforce tenantId clientId redirectUri : OidcUIConfig * PresetMetadata |
OidcPresets.entraWorkforce tenantId clientId redirectUri : OidcAppConfig |
OidcPresets.entraExternalId tenantSubdomain clientId redirectUri : OidcUIConfig * PresetMetadata |
OidcPresets.entraExternalId tenantSubdomain clientId redirectUri : OidcAppConfig |
OidcPresets.entraExternalIdWithDomain ... : OidcUIConfig * PresetMetadata |
OidcPresets.entraExternalIdWithDomain ... : OidcAppConfig |
OidcPresets.auth0 domain clientId redirectUri : OidcUIConfig * PresetMetadata |
OidcPresets.auth0 ... : OidcAppConfig |
OidcPresets.generic issuer clientId redirectUri : OidcUIConfig * PresetMetadata |
OidcPresets.generic ... : OidcAppConfig |
OidcCoherenceValidator(cfg: OidcUIConfig, presetMeta: PresetMetadata option, ?timeout) |
OidcCoherenceValidator(cfg: OidcAppConfig, ?timeout) |
OidcCoherenceValidator.evaluate cfg presetMeta |
OidcCoherenceValidator.evaluate cfg |
PresetMetadata record (Name / IssuerForm / AutoAddedScopes / ExpectsDecodableAccessToken / Notes) |
Deleted. Same data derived from PresetKind via OidcAppConfig.PresetKind.label / .issuerForm / .autoAddedScopes / .expectsDecodableAccessToken / .notes. |
Non-breaking
OidcUIConfig(inToolUp.Platform) is preserved unchanged. It remains the client-tier shape thatOidcAuthUI.OidcShellandOidcClient.handleCallbackconsume internally. Consumers writingOidcAppConfignever see it — the SDK projects viaOidcAppConfig.toClientConfig.- Existing handler signatures (
OidcAuthUI.OidcShell cfg,OidcClient.beginSignIn cfg, etc.) still takeOidcUIConfig. A subsequent change folds them ontoOidcAppConfigdirectly; until then the projection bridges.
The OidcAppConfig record
type OidcAppConfig = {
Issuer: string // base for OIDC discovery
Audience: string // access-token `aud` the server binds against
ClientId: string // app-registration client id
Scopes: string list // includes preset-auto-added entries
RedirectUri: string // registered callback URL
PostLogoutRedirectUri: string option
ValidateIdToken: bool option // None = off; Some true = full pipeline
Preset: PresetKind option // provenance for the validator + tracer
}
Audience is a new field at 0.4.0. Preset constructors default it to ClientId (the workforce-Entra / External-ID case). Auth0 deployments using a configured API audience override it after construction:
let cfg =
{ OidcPresets.auth0 "my-tenant.auth0.com" clientId redirectUri with
Audience = "https://api.yourapp.com" } // matches Auth0 dashboard "Audience" field
Worked migration — workforce Entra consumer
Pre-0.4.0 (with 0.3.x-oidc-presets.md advisory adoption):
let oidcCfg, meta =
OidcPresets.entraWorkforce entraTenantId entraClientId redirectUri
ClientConfig.compose
{| ... AuthUI = OidcAuthUI oidcCfg ... |}
0.4.0:
let oidcCfg : OidcAppConfig =
OidcPresets.entraWorkforce entraTenantId entraClientId redirectUri
// metadata is on cfg.Preset; OidcAppConfig.PresetKind.notes EntraWorkforce gives the hints
ClientConfig.compose
{| ... AuthUI = OidcAuthUI (OidcAppConfig.toClientConfig oidcCfg) ... |}
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ projection until OidcAuthUI takes OidcAppConfig directly
The single-line change is dropping the _ from the tuple destructure — let oidcCfg, _ = ... becomes let oidcCfg = .... The projection at the OidcAuthUI call site is a one-cycle bridge: a follow-up flips OidcAuthUI's parameter type to OidcAppConfig and the projection disappears.
Worked migration — OidcCoherenceValidator consumer
// 0.3.x
let v = OidcCoherenceValidator(oidcUIConfig, Some presetMeta)
ServerApp.withConfigValidator v ...
// 0.4.0
let v = OidcCoherenceValidator(oidcAppConfig) // preset on the config itself
ServerApp.withConfigValidator v ...
The validator's rule set is unchanged (same 11 rules — emptiness, OIDC-spec, HTTPS, preset-vs-issuer paste-mistakes, preset-AutoAddedScopes dropped, preset provenance). The data shape is denser.
Worked migration — PresetMetadata introspection
Consumers reading metadata fields directly (rare):
// 0.3.x
let _, meta = OidcPresets.entraWorkforce tid cid uri
printfn "applied: %s — %s" meta.Name meta.IssuerForm
for note in meta.Notes do printfn " - %s" note
// 0.4.0
let cfg = OidcPresets.entraWorkforce tid cid uri
match cfg.Preset with
| Some kind ->
printfn "applied: %s — %s" (PresetKind.label kind) (PresetKind.issuerForm kind)
for note in PresetKind.notes kind do printfn " - %s" note
| None -> ()
Verification
dotnet build—OidcAppConfigis a new type the compiler will flag at every mismatched call site. The 0.4.0 break is intentionally loud.- Test pack —
OidcPresetsTests(34 cases) +OidcCoherenceValidatorTests(30 cases) are reshaped to constructOidcAppConfigvalues. If the consumer's own tests constructOidcUIConfig * PresetMetadatatuples, they break the same way. - Smoke-test sign-in — confirm
[auth] <corr> begin-sign-inappears in the browser console withTOOLUP_AUTH_TRACEenabled, followed bytoken-exchange-okandtransition:established. The transitions all wire through the same handler surface as 0.3.x.
Rollback
Pin <ToolUpSdkVersion> back to 0.3.8 in Directory.Packages.props. The new ToolUp.AuthProviders.Oidc.Shared package is opt-in via transitive reference — pinning the SDK below 0.4.0 keeps consumers off it.
Coordinated change at 0.4.0
0.4.0 ships:
- Phase 69a re-application — forge SDK depends on
ToolUp.Remoting0.1.15+ (transitively). External consumers reach for ToolUp.Remoting the same way they reach for any other publicToolUp.*package family member. - OidcAppConfig unification (this migration).
- EntraExternalId companion deprecation (separate migration doc, in progress).
- Continued deprecation warnings (
FS0044) forFableJsonConverter— replaced byToolUp.Remoting.Json.SystemTextJson.FableConverters.create()per Phase 69 design. Non-blocking; consumer migration to the new converter is tracked separately.
Companion-side notes
EntraExternalIdClient (ToolUp.AuthProviders.EntraExternalId.Client) is marked deprecated at 0.4.0 in favour of OidcPresets.entraExternalId + OidcPresets.entraExternalIdWithDomain. The companion code stays compiling for one minor cycle (consumer migration window); removal lands at 0.Y.0. See the separate 0.4.0-entra-external-id-deprecation.md migration doc.