The Identity service provides authentication and authorization for Bitwarden Server using OAuth 2.0 and OpenID Connect protocols, built on Duende IdentityServer.
Overview
The Identity service handles:
- User Authentication: Password-based login, SSO integration
- OAuth 2.0 Flows: Authorization code, client credentials, resource owner password
- OpenID Connect: Identity token issuance and validation
- Token Management: Access tokens, refresh tokens, identity tokens
- SSO Integration: External authentication via SSO service
- Device Authorization: Device-specific authentication flows
Architecture
OAuth 2.0 Flows
Authorization Code Flow
Used by web and mobile clients for user authentication:
Authorization Request
Client redirects to /connect/authorize with client_id and redirect_uri
User Authentication
User authenticates with username/password or SSO
Authorization Code
Identity service redirects back with authorization code
Token Exchange
Client exchanges code for access token at /connect/token
Client Credentials Flow
Used for server-to-server authentication (Public API, SCIM):
curl -X POST "https://identity.bitwarden.com/connect/token" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=client_credentials" \
-d "client_id=organization.{guid}" \
-d "client_secret={secret}" \
-d "scope=api.organization"
Resource Owner Password Flow
Used by CLI and desktop clients:
curl -X POST "https://identity.bitwarden.com/connect/token" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=password" \
-d "username={email}" \
-d "password={password}" \
-d "scope=api offline_access" \
-d "client_id=cli" \
-d "deviceType=8" \
-d "deviceIdentifier={device_id}" \
-d "deviceName={device_name}"
Configuration
Application Settings
From src/Identity/Startup.cs:33:
public void ConfigureServices(IServiceCollection services)
{
// Settings
var globalSettings = services.AddGlobalSettingsServices(Configuration, Environment);
// Data Protection
services.AddCustomDataProtectionServices(Environment, globalSettings);
// Repositories
services.AddDatabaseRepositories(globalSettings);
// Caching
services.AddMemoryCache();
services.AddDistributedCache(globalSettings);
// Disable claim mapping
JwtSecurityTokenHandler.DefaultMapInboundClaims = false;
// Authentication
services.AddDistributedIdentityServices()
.AddAuthentication()
.AddCookie(AuthenticationSchemes.BitwardenExternalCookieAuthenticationScheme)
.AddOpenIdConnect("sso", "Single Sign On", options =>
{
options.Authority = globalSettings.BaseServiceUri.InternalSso;
options.ClientId = "oidc-identity";
options.ClientSecret = globalSettings.OidcIdentityClientKey;
options.ResponseType = "code";
options.SaveTokens = false;
options.GetClaimsFromUserInfoEndpoint = true;
});
// IdentityServer
services.AddCustomIdentityServerServices(Environment, globalSettings);
// Identity
services.AddCustomIdentityServices(globalSettings);
}
IdentityServer Configuration
The service uses Duende IdentityServer for OAuth/OIDC implementation:
IdentityServer is configured with signing certificates for token validation.
Certificate Configuration
{
"globalSettings": {
"identityServer": {
"certificateThumbprint": "ABC123..."
}
}
}
Supported Scopes
The Identity service issues tokens with the following scopes:
| Scope | Description | Used By |
|---|
api | Full API access | Web, Mobile, Desktop |
offline_access | Refresh token | All clients |
api.organization | Public organization API | Integrations |
api.push | Push notification registration | Mobile |
api.licensing | License validation | Self-hosted |
api.scim | SCIM provisioning | Enterprise SSO |
api.secrets | Secrets Manager | SM clients |
internal | Internal service access | Notifications, Events |
Client Types
Bitwarden defines several OAuth clients:
// User-facing clients
web - Web vault
browser - Browser extension
desktop - Desktop application
mobile - Mobile apps
cli - Command-line interface
// Service clients
organization.{guid} - Organization API access
installation.{guid} - Installation access
connector - Directory Connector
Authentication Schemes
From src/Identity/Startup.cs:98:
SSO Integration
The Identity service integrates with the SSO service for enterprise authentication:
.AddOpenIdConnect("sso", "Single Sign On", options =>
{
options.Authority = globalSettings.BaseServiceUri.InternalSso;
options.ClientId = "oidc-identity";
options.ClientSecret = globalSettings.OidcIdentityClientKey;
options.SignInScheme = AuthenticationSchemes.BitwardenExternalCookieAuthenticationScheme;
options.ResponseType = "code";
options.Events = new OpenIdConnectEvents
{
OnRedirectToIdentityProvider = context =>
{
// Pass domain_hint and organizationId to SSO service
context.ProtocolMessage.DomainHint = context.Properties.Items["domain_hint"];
context.ProtocolMessage.Parameters.Add("organizationId",
context.Properties.Items["organizationId"]);
return Task.FromResult(0);
}
};
});
Controllers
The Identity service exposes several endpoints:
SSO Controller
Handles SSO login flows:
/sso/authorize - Initiate SSO login
/sso/login - Process SSO callback
/sso/prevalidate - Validate SSO configuration
Accounts Controller
User account operations:
/accounts/prelogin - Get KDF iteration count
/accounts/register - User registration endpoint
IdentityServer Endpoints
Standard OAuth/OIDC endpoints:
/connect/authorize - Authorization endpoint
/connect/token - Token endpoint
/connect/userinfo - UserInfo endpoint
/connect/introspect - Token introspection
/connect/revoke - Token revocation
/.well-known/openid-configuration - Discovery document
Token Structure
Access Token Claims
Access tokens include the following claims:
{
"nbf": 1234567890,
"exp": 1234571490,
"iss": "https://identity.bitwarden.com",
"client_id": "web",
"sub": "{user_id}",
"auth_time": 1234567890,
"idp": "bitwarden",
"premium": "false",
"email": "[email protected]",
"email_verified": "true",
"name": "User Name",
"orgowner": "{org_id}",
"device": "{device_id}",
"scope": ["api", "offline_access"],
"amr": ["Application"]
}
Refresh Tokens
Refresh tokens are long-lived and must be stored securely by clients.
Refresh tokens enable clients to obtain new access tokens without re-authentication:
curl -X POST "https://identity.bitwarden.com/connect/token" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=refresh_token" \
-d "client_id=web" \
-d "refresh_token={refresh_token}"
Two-Factor Authentication
The Identity service integrates 2FA into the authentication flow:
Primary Authentication
User provides username and password
2FA Challenge
Identity service responds with TwoFactorRequired error and available providers
2FA Verification
Client resubmits with 2FA code and provider
Token Issuance
Identity service issues tokens upon successful 2FA verification
Supported 2FA providers:
- Authenticator apps (TOTP)
- Email
- Duo Security
- YubiKey
- FIDO2 WebAuthn
Rate Limiting
The Identity service implements rate limiting to prevent brute force attacks:
{
"IpRateLimitOptions": {
"GeneralRules": [
{
"Endpoint": "post:/connect/token",
"Period": "1m",
"Limit": 10
},
{
"Endpoint": "post:/accounts/prelogin",
"Period": "1m",
"Limit": 10
}
]
}
}
Middleware Pipeline
From src/Identity/Startup.cs:170:
public void Configure(IApplicationBuilder app)
{
// Security headers
app.UseMiddleware<SecurityHeadersMiddleware>();
// Path base for self-hosted
if (globalSettings.SelfHosted)
{
app.UsePathBase("/identity");
app.UseForwardedHeaders(globalSettings);
}
// Rate limiting (cloud only)
if (!globalSettings.SelfHosted)
{
app.UseMiddleware<CustomIpRateLimitMiddleware>();
}
// Localization
app.UseCoreLocalization();
// Static files (login pages, CSS, JS)
app.UseStaticFiles();
// Routing
app.UseRouting();
// CORS
app.UseCors(policy => policy
.SetIsOriginAllowed(o => CoreHelpers.IsCorsOriginAllowed(o, globalSettings))
.AllowAnyMethod()
.AllowAnyHeader()
.AllowCredentials());
// Current context
app.UseMiddleware<CurrentContextMiddleware>();
// IdentityServer
app.UseIdentityServer();
// Controllers
app.UseEndpoints(endpoints => endpoints.MapDefaultControllerRoute());
}
Deployment
Environment Variables
GLOBALSETTINGS__SELFHOSTED=true
GLOBALSETTINGS__SQLSERVER__CONNECTIONSTRING=<connection>
GLOBALSETTINGS__IDENTITYSERVER__CERTIFICATETHUMBPRINT=<thumbprint>
GLOBALSETTINGS__OIDCIDENTITYCLIENTKEY=<secret>
GLOBALSETTINGS__BASESERVICEURI__INTERNALSSO=<sso_url>
Docker
docker run -d \
--name bitwarden-identity \
-p 5001:5000 \
-e GLOBALSETTINGS__SelfHosted=true \
-e GLOBALSETTINGS__SqlServer__ConnectionString="<connection>" \
bitwarden/identity:latest
Self-Hosted Configuration
In self-hosted deployments, the Identity service runs at /identity path.
location /identity {
proxy_pass http://identity:5000/identity;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
Discovery Document
The Identity service exposes an OpenID Connect discovery document:
curl https://identity.bitwarden.com/.well-known/openid-configuration
Response includes:
- Authorization endpoint
- Token endpoint
- Supported grant types
- Supported scopes
- Signing keys (JWKS)
Background Services
From src/Identity/Startup.cs:157:
if (CoreHelpers.SettingHasValue(globalSettings.ServiceBus.ConnectionString))
{
services.AddHostedService<Core.HostedServices.ApplicationCacheHostedService>();
}
The cache service synchronizes user permissions and organization data across instances.