Follow-up to #407 (PR #459), which lands the OIDC relying party with in-memory adapters only.
What is owed
store::OidcAuthorizationStore — Valkey adapter. The pending authorization (state → nonce, PKCE verifier, redirect URI, issued_at) with the store's fixed ten-minute TTL and a destructive consume (a GETDEL, or GET + DEL in one MULTI). Register it on ValkeyStores and drop the Option from store::conformance::Harness::oidc_authorizations() together with the run_all skip — both exist only so a container-backed harness written before this adapter keeps compiling.
auth::oidc::FederatedAccounts — Postgres adapter, over the same account table as AccountDirectory/AccountRegistry/AccountProfiles. resolve_or_create is one statement: look up (issuer, subject); if absent and the asserted address already has a row, AddressTaken; otherwise insert an account row with a null credential and the federated link. The adapter contract: an account row whose credential is null makes AccountDirectory::authenticate and authenticate_user return Refused (after the timing-equalized miss), never Granted — "no password for OIDC accounts" made structural. Add that as a suite row beside the port.
- The development profile.
auth::oidc::InMemoryFederatedAccounts holds its own rows, separate from auth::accounts_memory::InMemoryAccounts, because the two live in different files and the lane that wrote it could not edit the latter. Consequences under serve --memory today: an OIDC identity whose address matches a password account is not refused with 409 error.auth.oidc_address_taken, and an OIDC account has no profile row (GET /v1/auth/profile answers 404 for it). Fold the federated rows into InMemoryAccounts (an Option<String> credential, a (issuer, subject) index) so the development profile has the same shape as Postgres.
Until then
boot::assemble refuses OIDC_ISSUER under the durable backends by name (BootError::AdapterUnavailable { key: "OIDC_ISSUER", .. }), so a durable profile never mixes an in-memory ceremony double (design/filesystem/server.md).
Follow-up to #407 (PR #459), which lands the OIDC relying party with in-memory adapters only.
What is owed
store::OidcAuthorizationStore— Valkey adapter. The pending authorization (state→ nonce, PKCE verifier, redirect URI, issued_at) with the store's fixed ten-minute TTL and a destructiveconsume(aGETDEL, orGET+DELin oneMULTI). Register it onValkeyStoresand drop theOptionfromstore::conformance::Harness::oidc_authorizations()together with therun_allskip — both exist only so a container-backed harness written before this adapter keeps compiling.auth::oidc::FederatedAccounts— Postgres adapter, over the same account table asAccountDirectory/AccountRegistry/AccountProfiles.resolve_or_createis one statement: look up(issuer, subject); if absent and the asserted address already has a row,AddressTaken; otherwise insert an account row with a null credential and the federated link. The adapter contract: an account row whose credential is null makesAccountDirectory::authenticateandauthenticate_userreturnRefused(after the timing-equalized miss), neverGranted— "no password for OIDC accounts" made structural. Add that as a suite row beside the port.auth::oidc::InMemoryFederatedAccountsholds its own rows, separate fromauth::accounts_memory::InMemoryAccounts, because the two live in different files and the lane that wrote it could not edit the latter. Consequences underserve --memorytoday: an OIDC identity whose address matches a password account is not refused with409 error.auth.oidc_address_taken, and an OIDC account has no profile row (GET /v1/auth/profileanswers 404 for it). Fold the federated rows intoInMemoryAccounts(anOption<String>credential, a(issuer, subject)index) so the development profile has the same shape as Postgres.Until then
boot::assemblerefusesOIDC_ISSUERunder the durable backends by name (BootError::AdapterUnavailable { key: "OIDC_ISSUER", .. }), so a durable profile never mixes an in-memory ceremony double (design/filesystem/server.md).