Note 2026-10-26 (STA-1264), additive:
IdpAdmin.listAuthMethods(userId) answers a user’s enrolled
authentication methods as RFC 8176 amr codes (the ZITADEL driver maps its method types; the
level each code reaches stays IdentityConfig.assuranceByMethod), for the security page’s MFA
enrolment list. ApiKeyRecord gains lastUsedAt and ApiKeyStore gains recordUse(id, at) with
a no-op default; ApiKeyVerifierService writes it after a passing verification when it is older
than IdentityConfig.apiKeyLastUsedSeconds (60), and a failed write never refuses the key.Note 2026-10-26 (STA-1263), additive:
LoginPolicy gains externalProviderIds (the provider-side
ids of the market pack’s sign-in providers, through IdentityConfig.externalProviders) and
secondFactors (IdpSecondFactor: totp, u2f, otp_email, otp_sms). When a list is given,
setLoginPolicy leaves the organisation offering exactly those (the ZITADEL driver adds the
missing and removes the others, regional providers as system-owned); a list left out is not
touched. This is decision 4 applied to sign-in methods per market (OS identity A3).Context
Nothing in Stackra created a ZITADEL user or machine user, ended a session, ran the OAuth device flow or set an organisation’s login policy at run time, so device credentials, machine principals and revocations could not reach the provider. API keys were stored as hashes, but the gateway had no verification read.Decision
IdpAdminport in@stackra/identity/contracts:createUser,createMachineUser(with its client secret, shown once),deactivateUser,removeUser,endSession(a gone session is fine),endSessions(user),startDeviceAuthorizationandpollDeviceToken(RFC 8628), andsetLoginPolicy.ZitadelIdpAdminDriveruses the v2 user and session services, the management API for machine users, secrets and the login policy, and the OAuth device endpoints. Managed or bring-your-own is configuration:IdpAdminConfig.urlis the platform’s ZITADEL or the tenant’s own, and the token is theIDENTITY_IDP_ADMIN_TOKENsecret. A 409 isidp_user_exists; a timeout, network failure, 429 or 5xx isidp_admin_unavailable(retryable).MemoryIdpAdminDriverserves tests.- API-key verification by hash.
hashApiKeyis SHA-256 of the key (unsalted; the key holds 178 random bits).ApiKeyVerifierService.verify(key, origin)refuses a malformed key with no lookup, reads the record through theApiKeyStoreport by hash, compares in constant time, and refuses a revoked or expired key, a publishable key from an unbound origin and a secret key from a browser, all as oneapi_key_rejected. - Login policies are data. The values come from tenant settings and market-pack defaults and are pushed through the port; nothing in code picks them.
Consequences
- The identity service binds the ZITADEL driver and implements
ApiKeyStoreover its key table. - Devices get credentials through
createMachineUseror the device flow (device_credential.issued).
Enforcement
packages/security/identity/src/core/drivers/__tests__/zitadel-idp-admin.driver.test.ts,
packages/security/identity/src/core/services/__tests__/api-key-verifier.service.test.ts.