bitwarden/server
Identity
Active contributors: auth team.
Purpose
src/Identity/ is the OAuth 2.0 / OpenID Connect token endpoint for Bitwarden. Every client (web vault, browser ext, mobile, desktop, CLI, directory connector) authenticates here first to obtain an access + refresh token; the resulting JWT is then accepted by every other service. The Identity host is built on Duende IdentityServer with Bitwarden-authored extension grants, custom token providers, and ASP.NET Core Identity for the user store.
Directory layout
src/Identity/
├── Program.cs / Startup.cs
├── Controllers/
│ ├── AccountsController.cs # Pre-token helpers (prelogin, register-finalize, register-anonymous)
│ ├── SsoController.cs # /sso/PreValidate, /sso/ExternalChallenge
│ └── ...
├── IdentityServer/ # Identity-host-specific IdentityServer config (the meaty part lives in src/Core/Auth/IdentityServer)
├── Models/ # Request/response models for the controllers
├── Utilities/ # Identity-specific extensions (e.g. ServiceCollectionExtensions for AddCustomIdentityServerServices)
├── Views/ # SSO callback / error views (Razor)
├── appsettings.*.json
└── Dockerfile / build.sh / entrypoint.shThe bulk of the IdentityServer customisation lives in the shared library src/Core/Auth/IdentityServer/:
src/Core/Auth/IdentityServer/
├── ServiceCollectionExtensions.cs # AddCustomIdentityServerServices
├── ProfileService.cs # Builds the access-token claims for a user
├── ApiClient.cs # Server-to-server / org / installation / SCIM clients
├── DynamicClientStore.cs # Org-mounted clients (e.g. SSO callback)
├── StaticClientStore.cs # The fixed Bitwarden first-party clients
├── ApiResources.cs # The "api", "api.organization", "api.installation", etc. resources
├── AuthorizationCodeStore.cs # Persisted-grant store backed by IDistributedCache
├── PersistedGrantStore.cs # Same but for refresh tokens / device codes
├── DynamicClientStore.cs # Pulls per-org clients from the DB
├── ClientProviders/ # Resolves clients by organisation, installation, or SCIM token
├── RequestValidators/ # Custom resource-owner / extension grant validators
│ ├── ResourceOwnerPasswordValidator.cs
│ ├── WebAuthnLoginGrantValidator.cs
│ ├── SsoExtensionGrantValidator.cs
│ ├── AuthorizationCodeGrantValidator.cs
│ └── ...
├── UserDecryptionOptionsBuilder.cs # Computes the `userDecryptionOptions` payload returned with the token
└── VaultCorsPolicyService.csASP.NET Core Identity glue lives in src/Core/Auth/Identity/:
UserStore.cs— bridgesIUserRepositoryto ASP.NET Core Identity.RoleStore.cs— minimal role store (Bitwarden uses claims, not roles, but the abstraction is required).Policies.cs—Policies.Application,Policies.Web,Policies.Push, ... — shared with the API.TokenProviders/— every two-factor provider (TOTP, Email, Duo, YubiKey, WebAuthn, Remember).
Key abstractions
| Type | Purpose |
|---|---|
Startup (src/Identity/Startup.cs) |
Registers data protection, distributed cache, IdentityServer, ASP.NET Identity, Bitwarden services, the sso external OIDC handler. |
AddCustomIdentityServerServices (src/Core/Auth/IdentityServer/ServiceCollectionExtensions.cs) |
Adds IdentityServer with Bitwarden's grant types, persisted-grant store, signing credentials, and dynamic client store. |
ResourceOwnerPasswordValidator |
Master-password grant: validates email + master-password hash via IUserService.CheckPasswordAsync, then defers to 2FA. |
BaseRequestValidator (in RequestValidators/) |
Centralised post-credential-check logic: device tracking, 2FA enforcement, captcha gating, security stamp checks, login policy enforcement. |
WebAuthnLoginGrantValidator |
Login With Passkey extension grant; uses the passkey PRF blob to decrypt the user key. |
SsoExtensionGrantValidator |
Validates a one-shot sso token issued by Sso and exchanges it for an access token. |
AuthorizationCodeGrantValidator |
Standard OIDC code-flow validator. |
ProfileService |
Decides which claims land in the access token (user id, email, name, premium, security stamp, organization claims, etc.). |
UserDecryptionOptionsBuilder |
Adds userDecryptionOptions to the token response so clients know how to derive the user key (master-password, device-trust, key-connector, …). |
Policies.cs (src/Core/Auth/Identity/Policies.cs) |
The set of authorization policies used across all services. |
How it works
sequenceDiagram
participant C as Client
participant Id as Identity
participant DB
participant TFA as 2FA Provider
C->>Id: POST /connect/token (grant_type=password,<br/>email, masterPasswordHash, captchaResponse?)
Id->>DB: ResourceOwnerPasswordValidator → IUserService.CheckPasswordAsync
Id->>Id: BaseRequestValidator: device trust, captcha, login policy
alt 2FA required
Id-->>C: 400 invalid_grant + TwoFactorProviders
C->>TFA: complete TOTP/Email/Duo/WebAuthn
C->>Id: POST /connect/token (..., twoFactorToken, twoFactorProvider)
Id->>Id: TwoFactorAuthenticationValidator
end
Id->>Id: ProfileService → claims
Id->>Id: UserDecryptionOptionsBuilder
Id-->>C: access_token + refresh_token (JWT) + userDecryptionOptionsOther grant types follow the same shape but plug in different validators:
webauthn: passwordless via FIDO2 PRF.auth_request: login-with-device (a logged-in device approves).sso: one-shot SSO bridge (used bybitwarden_license/Sso).client_credentials: org / installation API keys.refresh_token: rotates the refresh token on each use.
Two-factor providers
Implementations live in src/Core/Auth/Identity/TokenProviders/:
AuthenticatorTokenProvider— TOTP (Microsoft Authenticator, Authy, Google Authenticator).EmailTokenProvider— six-digit code via mail.DuoTokenProvider/OrganizationDuoTokenProvider— Duo Security (personal + org-issued).WebAuthnTokenProvider— FIDO2 second factor.YubicoOtpTokenProvider— YubiKey OTP.RememberTokenProvider— "remember this device for 30 days" cookie.
TwoFactorAuthenticationValidator (src/Core/Auth/IdentityServer/RequestValidators/TwoFactorAuthenticationValidator.cs) decides which provider is required given the user's enrolment + organization policy.
SSO bridge
The Identity host registers an external OpenID Connect handler called sso:
.AddOpenIdConnect("sso", "Single Sign On", options =>
{
options.Authority = globalSettings.BaseServiceUri.InternalSso;
options.ClientId = "oidc-identity";
options.ClientSecret = globalSettings.OidcIdentityClientKey;
...
});bitwarden_license/Sso is both an OIDC IdP (this handler talks to it) and a SAML / OIDC SP (it talks to the customer's IdP). The SsoExtensionGrantValidator consumes a one-shot token to convert that flow into a Bitwarden access token. See features/sso-saml.
Persisted grants and tokens
IdentityServer's persisted-grant store is backed by IDistributedCache (Redis in cloud, in-memory in self-host) via PersistedGrantStore and AuthorizationCodeStore. Refresh-token rotation is enabled by default; the SecurityStamp on the user invalidates all outstanding tokens when bumped.
Cookie-based grants for the SSO redirect flow use a custom DistributedCacheTicketStore (src/Core/Auth/Identity/DistributedCacheTicketStore.cs) so cookies stay small.
Self-host quirks
Startup.Configureaddsapp.UsePathBase("/identity")whenglobalSettings.SelfHostedis true; the bundled nginx config routes/identity/*to this app.- Rate limiting is disabled in self-host.
- The dev cert path differs (
src/Identity/identity_server_dev.pfxis generated bydev/create_certificates_*.sh).
Entry points for modification
- Adding a new grant type → add an
IExtensionGrantValidatorinsrc/Core/Auth/IdentityServer/RequestValidators/, register it inAddCustomIdentityServerServices. - Adding a new 2FA provider → implement
IUserTwoFactorTokenProvider<User>insrc/Core/Auth/Identity/TokenProviders/, add an enum value toTwoFactorProviderType, surface inTwoFactorAuthenticationValidator. - Changing what claims appear in the JWT → edit
ProfileService.csand theUserDecryptionOptionsBuilder. - Adding a new client (e.g. for a new partner integration) → add it to
StaticClientStoreor mint org-bound API keys via the AdminConsole. See features/auth.
Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.