bitwarden/server
IdentityServer & auth pipeline
Active contributors: auth team.
Purpose
Bitwarden's authentication and OAuth/OIDC stack is built on Duende IdentityServer plus ASP.NET Core Identity, with a substantial layer of Bitwarden-authored extension grants, custom validators, and stores. The wiring is split between src/Identity/ (the host) and src/Core/Auth/IdentityServer/ (the reusable pieces that other services also need).
Directory layout
src/Core/Auth/IdentityServer/
├── ServiceCollectionExtensions.cs # AddCustomIdentityServerServices: signing keys, stores, validators, profile service
├── ProfileService.cs # Builds the JWT claim set
├── UserDecryptionOptionsBuilder.cs # Adds userDecryptionOptions to the token response
├── ApiResources.cs / ApiClient.cs / StaticClientStore.cs / DynamicClientStore.cs
├── PersistedGrantStore.cs # Refresh tokens / device codes (IDistributedCache backed)
├── AuthorizationCodeStore.cs # OIDC auth-code store
├── ClientProviders/ # Resolve clients for installations / SCIM tokens / orgs
├── RequestValidators/ # 1 file per grant type (password, sso, webauthn, auth_request, refresh, ApiClient)
├── VaultCorsPolicyService.cs # Per-CORS-origin policy for IdentityServer's discovery endpoints
└── Constants/
src/Core/Auth/Identity/
├── Policies.cs # Cross-service authz policy names
├── Claims.cs # JWT claim type constants
├── UserStore.cs / RoleStore.cs # ASP.NET Core Identity stores backed by IUserRepository
├── CustomIdentityServiceCollectionExtensions.cs # AddCustomIdentityServices
├── DistributedCache* / TokenRetrieval.cs # OIDC cookie-store + bearer-token helpers
└── TokenProviders/ # 2FA: TOTP, Email, Duo, WebAuthn, YubiKey, RememberKey abstractions
| Type | Purpose |
|---|---|
AddCustomIdentityServerServices(env, globalSettings) |
One-shot service registration for every service that needs IdentityServer (just Identity today). |
BaseRequestValidator (in RequestValidators/) |
Shared post-credential validator: device tracking, 2FA enforcement, captcha gating, security-stamp checks, login policy enforcement. Inherited by every grant validator. |
ResourceOwnerPasswordValidator |
Master-password grant: validates email + master-password hash via IUserService.CheckPasswordAsync. |
WebAuthnLoginGrantValidator |
Login With Passkey extension grant; uses the passkey PRF blob. |
SsoExtensionGrantValidator |
Validates the one-shot SsoTokenable issued by the Sso host. |
AuthRequestExtensionGrantValidator |
Login-with-device flow: a logged-in device approves a pending auth request. |
ProfileService |
Adds Bitwarden claims (user id, email, name, premium, security stamp, organization claims) to the access token. |
UserDecryptionOptionsBuilder |
Computes userDecryptionOptions so clients know whether to derive the user key from the master password, a trusted device, key connector, or a combination. |
Policies |
Application, Web, Push, Organization, Installation, Licensing, Secrets, Send — all the authz policy names used elsewhere. |
Claims |
Constant claim-type strings used in policies and authorization handlers. |
How it works
graph TD
A[Client POST /connect/token] --> B[IdentityServer pipeline]
B --> C{grant_type?}
C -->|password| D[ResourceOwnerPasswordValidator]
C -->|webauthn| E[WebAuthnLoginGrantValidator]
C -->|auth_request| F[AuthRequestExtensionGrantValidator]
C -->|sso| G[SsoExtensionGrantValidator]
C -->|client_credentials| H[ApiClient validators]
C -->|refresh_token| I[Persisted-grant store]
D --> Z[BaseRequestValidator: 2FA + captcha + policies]
E --> Z
F --> Z
G --> Z
Z --> P[ProfileService → claims]
P --> U[UserDecryptionOptionsBuilder]
U --> R[Token response]Persisted-grant storage
PersistedGrantStore and AuthorizationCodeStore use ASP.NET Core's IDistributedCache rather than the IdentityServer SQL store. In cloud this is Redis; in self-host it falls back to an in-memory cache. Refresh-token rotation is on by default (RefreshTokenUsage = OneTimeOnly).
Cookie-based auth (used during the SSO redirect flow) uses DistributedCacheTicketStore in src/Core/Auth/Identity/ so cookie payload size stays small.
ASP.NET Core Identity
UserStore.cs and RoleStore.cs adapt Bitwarden's User entity + IUserRepository to the ASP.NET Core Identity abstractions. LowerInvariantLookupNormalizer ensures emails are case-insensitive lookup keys.
Two-factor providers are registered via AddTokenProvider<T> calls in CustomIdentityServiceCollectionExtensions. Each implements IUserTwoFactorTokenProvider<User>. The TwoFactorAuthenticationValidator decides which provider is required given the user's enrolment plus organisation policy (RequireTwoFactorPolicy).
Authorization policies
Five JWT scopes are common: api, api.organization, api.installation, api.secrets, api.send.access, api.licensing, api.push. Plus the SSO and SCIM tokens use bespoke scopes. The matching policy definitions live in src/Api/Startup.cs (AddPolicy(Policies.X, ...)) and replicate across services via AddIdentityAuthenticationServices.
Integration points
Identityhost — the only service that issues tokens.- All other services — consume tokens via
AddIdentityAuthenticationServices(src/SharedWeb/Utilities/ServiceCollectionExtensions.cs), which configures JWT bearer auth pointing at the Identity host's discovery endpoint. Ssohost — issuesSsoTokenables consumed bySsoExtensionGrantValidator.Adminhost — does not run IdentityServer; instead uses passwordless email-link auth and signs its own session cookies.
Entry points for modification
- New grant type →
IExtensionGrantValidatorinsrc/Core/Auth/IdentityServer/RequestValidators/, register inAddCustomIdentityServerServices. - New 2FA provider → implement
IUserTwoFactorTokenProvider<User>insrc/Core/Auth/Identity/TokenProviders/, addTwoFactorProviderTypeenum value, surface inTwoFactorAuthenticationValidator. - Change JWT claims → edit
ProfileService. - Tighten / loosen a policy → edit the
AddPolicy(...)call inApi.Startup(and any other host that re-declares it).
Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.