This page documents Immich's authentication and authorization systems, covering how users authenticate (prove their identity) and how the system authorizes access to resources. For information about multi-tenant content sharing and access control, see page 4.3.
Immich implements a multi-layered authentication system supporting three primary methods:
| Authentication Method | Use Case | Token Type | Key Classes |
|---|---|---|---|
| Email/Password | Web and mobile users | JWT (Bearer + Cookie) | AuthService, CryptoRepository |
| OAuth 2.0 / OIDC | External identity providers | JWT (Bearer + Cookie) | OAuthRepository, AuthService |
| API Keys | CLI tools and integrations | API Key (x-api-key header) | ApiKeyRepository |
Every API request passes through AuthGuard (registered as NestJS APP_GUARD in ApiModule). The guard delegates to AuthService.authenticate(), which resolves the caller's identity and populates an AuthDto used for downstream authorization checks. The maintenance worker uses a separate MaintenanceAuthGuard.
Sources: server/src/app.module.ts17-49 server/src/middleware/auth.guard.ts
AuthGuard is registered as APP_GUARD in ApiModule and runs on every HTTP request. It reads credentials from three possible locations and calls AuthService.authenticate():
Authentication pipeline diagram
The resulting AuthDto carries the resolved identity through the request. Services call requireAccess() or checkAccess() (from src/utils/access) using an AccessRepository to enforce fine-grained per-resource permissions.
WebSocket connections are also authenticated: BaseModule.onModuleInit() calls websocketRepository.setAuthFn() with a closure that calls AuthService.authenticate() using the socket handshake headers.
Sources: server/src/app.module.ts17-49 server/src/app.module.ts80-86 server/src/services/base.service.ts210-214 server/test/utils.ts117-119
Password authentication is the default method for user accounts. The system uses bcrypt for password hashing and issues JWT tokens upon successful authentication.
Password Change Flow:
The system supports password changes with optional session invalidation. When a user changes their password, they can choose to invalidate all existing sessions except the current one.
Sources: server/src/services/auth.service.ts server/src/repositories/crypto.repository.ts
OAuth authentication allows users to authenticate using external identity providers (Google, Authentik, Keycloak, etc.). The implementation uses the openid-client library with PKCE (Proof Key for Code Exchange) for enhanced security.
OAuth Configuration:
OAuth is configured via the oauth key in SystemConfig. The full set of fields (from server/src/config.ts):
| Config Field | Type | Default | Description |
|---|---|---|---|
enabled | boolean | false | Enable OAuth login |
issuerUrl | string | '' | OIDC discovery endpoint |
clientId | string | '' | OAuth client identifier |
clientSecret | string | '' | OAuth client secret |
scope | string | 'openid email profile' | Requested scopes |
signingAlgorithm | string | 'RS256' | ID token signing algorithm |
profileSigningAlgorithm | string | 'none' | UserInfo signing algorithm |
tokenEndpointAuthMethod | OAuthTokenEndpointAuthMethod | client_secret_post | Token endpoint auth method |
timeout | number | 30000 | Request timeout (ms) |
autoRegister | boolean | true | Auto-create user on first OAuth login |
autoLaunch | boolean | false | Skip login page, go directly to OAuth |
buttonText | string | 'Login with OAuth' | Custom button label |
mobileOverrideEnabled | boolean | false | Use custom mobile redirect URI |
mobileRedirectUri | string | '' | Custom mobile redirect URI |
defaultStorageQuota | number | null | null | Default quota for OAuth users |
storageLabelClaim | string | 'preferred_username' | JWT claim for storage label |
storageQuotaClaim | string | 'immich_quota' | JWT claim for storage quota |
roleClaim | string | 'immich_role' | JWT claim for admin role |
Token Endpoint Authentication Methods (OAuthTokenEndpointAuthMethod):
The OAuthRepository supports three authentication methods:
none: No authentication (public client)client_secret_post: Send client_id and client_secret in POST bodyclient_secret_basic: Send credentials in HTTP Basic Auth headerLinking OAuth Accounts:
Users can link their existing password-based account to an OAuth provider using the /oauth/link endpoint, allowing them to authenticate with either method.
Sources: server/src/config.ts98-117 server/src/repositories/oauth.repository.ts
API keys provide programmatic access to Immich for CLI tools and integrations. Each API key has an associated set of permissions that limit what operations it can perform.
API key authentication flow
API Key Management Endpoints:
POST /api-keys - Create a new API key with specified permissionsGET /api-keys - List all API keys for current userGET /api-keys/{id} - Get specific API key detailsGET /api-keys/me - Get the current API key (when authenticated with an API key)PUT /api-keys/{id} - Update API key name or permissionsDELETE /api-keys/{id} - Delete an API keyAPI Key Creation:
Usage:
API keys are sent in the x-api-key HTTP header:
x-api-key: <secret-key>
Sources: server/src/repositories/api-key.repository.ts mobile/openapi/lib/model/permission.dart1-50
Mobile clients have additional authentication features for improved security and user experience.
Users can set up a PIN code for quick access to the mobile app without entering their full password. PIN codes are stored securely on the device and validated server-side.
Endpoints:
POST /auth/pin-code - Set up a new PIN codePUT /auth/pin-code - Change existing PIN codeDELETE /auth/pin-code - Reset/remove PIN codePOST /auth/session/unlock - Unlock session with PIN or passwordPin Code Operations:
Mobile sessions can be locked for security when the app is backgrounded or after a timeout. Locked sessions require PIN or password to unlock.
Endpoints:
POST /auth/session/lock - Lock the current sessionPOST /auth/session/unlock - Unlock with PIN or password (SessionUnlockDto)Sources: server/src/services/auth.service.ts server/src/controllers/auth.controller.ts
Sessions track authenticated clients and their state. The system stores session information in PostgreSQL and uses it for token validation, device tracking, and security features.
Session Response Fields:
| Field | Type | Description |
|---|---|---|
id | UUID | Session identifier |
userId | UUID | User who owns the session |
deviceType | string | Type of device (web, iOS, Android, CLI) |
deviceOS | string | Operating system name |
appVersion | string | Immich app version |
createdAt | timestamp | When session was created |
updatedAt | timestamp | Last activity timestamp |
expiresAt | timestamp | When session expires (optional) |
isPendingSyncReset | boolean | Mobile sync needs reset |
current | boolean | Is this the current session |
Session Management Endpoints:
POST /sessions - Create a new session (login)GET /sessions - List all sessions for current userPUT /sessions/{id} - Update session (e.g., device info)DELETE /sessions/{id} - Delete a specific sessionDELETE /sessions - Delete all sessions except currentPOST /sessions/{id}/lock - Lock a session (mobile)Admin Session Management:
GET /admin/users/{id}/sessions - List sessions for any user (admin only)Sources: server/src/repositories/session.repository.ts server/src/services/session.service.ts server/src/controllers/session.controller.ts
Authorization in Immich operates at two levels:
AuthGuard checks whether the caller has the required role (admin vs. standard user) using NestJS metadata decorators applied at the controller level.requireAccess() or checkAccess() (from src/utils/access) with an AccessRequest to verify that the authenticated user has the right to act on the specific resource (e.g., owns the asset, is a member of the album).Authorization layer diagram
AccessRequest structure:
An AccessRequest specifies the permission being requested (a Permission enum value), an auth context, and the ids of the resources being accessed. requireAccess resolves which IDs the caller is permitted to access and throws ForbiddenException for any that are not.
Common Permission Patterns (from Permission enum):
| Pattern | Examples |
|---|---|
{resource}.read | activity.read, album.read, asset.read |
{resource}.create | activity.create, album.create, asset.create |
{resource}.update | album.update, asset.update |
{resource}.delete | activity.delete, album.delete, asset.delete |
adminUser.* | adminUser.read, adminUser.create, adminUser.update, adminUser.delete |
adminSession.* | adminSession.read, adminSession.delete |
Sources: server/src/services/base.service.ts61-62 server/src/services/base.service.ts210-214 server/src/repositories/access.repository.ts mobile/openapi/lib/model/permission.dart14-50
Immich supports three credential schemes:
Security scheme summary
| Scheme | Header / Location | Credential |
|---|---|---|
bearer | Authorization: Bearer <jwt> | JWT signed by Immich |
cookie | Cookie: immich_access_token=<jwt> | JWT signed by Immich |
api_key | x-api-key: <secret> | Raw API key secret |
| shared link | ?key=<key> query param | Shared link key |
Sources: server/src/services/auth.service.ts server/src/middleware/auth.guard.ts
Immich uses a flat two-role model: every user has an isAdmin boolean flag.
User role hierarchy
Admin-Only Controllers:
| Controller | Purpose |
|---|---|
UserAdminController | CRUD for all user accounts |
AuthAdminController | Unlink OAuth for any user |
SystemConfigController | Read/write SystemConfig |
QueueController | Manage background job queues |
MaintenanceController | Toggle maintenance mode |
NotificationAdminController | Send test emails |
Sources: server/src/controllers/index.ts7-42 server/src/services/index.ts8-9
Beyond basic user roles, Immich implements fine-grained access control for shared resources.
Albums support role-based sharing with three access levels:
Album User Management:
Album User Fields:
Sources: server/src/repositories/album.repository.ts server/src/repositories/album-user.repository.ts server/src/services/album.service.ts
Partner sharing creates a trusted relationship where two users can access each other's entire libraries.
Partner Management:
POST /partners - Create a partner relationship (requires acceptance)GET /partners - List current partnersPUT /partners/{id} - Update partner settingsDELETE /partners/{id} - Remove a partnerPartner Permissions:
Partners have access to each other's:
Partners cannot:
Sources: server/src/repositories/partner.repository.ts server/src/services/partner.service.ts
Shared links allow external access to albums or individual assets without authentication. Links can be password-protected and have expiration dates.
Shared Link Endpoints:
POST /shared-links - Create a shared linkGET /shared-links - List all shared linksGET /shared-links/{id} - Get specific shared link detailsGET /shared-links/me - Get link info when accessing via linkPUT /shared-links/{id}/assets - Add assets to linkDELETE /shared-links/{id}/assets - Remove assets from linkPATCH /shared-links/{id} - Update link settingsDELETE /shared-links/{id} - Delete a shared linkShared Link Configuration:
Sources: server/src/repositories/shared-link.repository.ts server/src/services/shared-link.service.ts
When Immich starts in maintenance mode (controlled by the MaintenanceModeState stored in SystemMetadata[SystemMetadataKey.MaintenanceMode]), the regular ApiModule and AuthGuard are replaced by MaintenanceModule with its own MaintenanceAuthGuard.
Maintenance mode boot sequence
The maintenance worker authenticates requests using a JWT signed with a per-session ephemeral secret stored alongside the MaintenanceModeState. The MaintenanceWorkerService (in server/src/maintenance/maintenance-worker.service.ts) uses jwtVerify from the jose library and reads credentials from either the Authorization header or the immich_access_token cookie.
The MaintenanceModeState type encodes this:
MaintenanceModeState =
| { isMaintenanceMode: true; secret: string; action?: SetMaintenanceModeDto }
| { isMaintenanceMode: false }
Sources: server/src/main.ts32-65 server/src/app.module.ts105-137 server/src/maintenance/maintenance-worker.service.ts server/src/maintenance/maintenance-auth.guard.ts server/src/types.ts474-476
OAuth authentication uses PKCE to prevent authorization code interception attacks. This is especially important for mobile and single-page applications that cannot securely store a client secret.
PKCE Flow:
code_verifiercode_challenge = BASE64URL(SHA256(code_verifier))code_challenge with authorization requestcode_verifier for tokenscode_challenge == BASE64URL(SHA256(code_verifier))The OAuthRepository.authorize() method automatically generates PKCE parameters:
Sources: server/src/repositories/oauth.repository.ts server/src/config.ts98-117
JWT Token Structure:
JWT tokens are signed using the jose library. The token payload encodes the session identity:
{
sub: userId, // User ID (UUID)
iat: timestamp, // Issued at
exp: timestamp, // Expiration (if set)
}
AuthService.authenticate() validates the JWT signature and then looks up the session record in SessionRepository to confirm the session is still active.
Token Storage:
immich_access_token cookie (set server-side)flutter_secure_storage (Keychain on iOS, KeyStore on Android)Token Transmission:
Tokens can be sent via:
Authorization: Bearer <token> header (preferred)immich_access_token cookie (web browser)x-api-key header (API keys, not a JWT)Sources: server/src/services/auth.service.ts server/src/repositories/session.repository.ts
Password Hashing:
Passwords are hashed using bcrypt via CryptoRepository.hashBcrypt(). The salt rounds constant is SALT_ROUNDS (defined in src/constants). Password comparison uses CryptoRepository.compareBcrypt().
The BaseService.createUser() helper hashes the password before writing to UserRepository:
payload.password = await this.cryptoRepository.hashBcrypt(payload.password, SALT_ROUNDS);
Password Policies:
shouldChangePassword flag on the user record forces a password change on next loginUserAdminControllerSources: server/src/services/base.service.ts218-240 server/src/repositories/crypto.repository.ts server/src/constants.ts
Session Invalidation:
Sessions can be invalidated through multiple mechanisms:
POST /auth/logout deletes sessionDELETE /sessions/{id} removes specific sessionDELETE /sessions removes all except currentexpiresAt timestampDevice Tracking:
Each session tracks device information for security auditing:
Sources: server/src/repositories/session.repository.ts server/src/services/session.service.ts
All permissions are defined in the Permission class in mobile/openapi/lib/model/permission.dart (auto-generated from the OpenAPI spec). The following is a representative subset:
| Permission value | Category | Description |
|---|---|---|
all | — | Grants all permissions (super-access) |
activity.read | Activity | View album activities |
activity.create | Activity | Create likes/comments |
activity.delete | Activity | Delete activities |
activity.statistics | Activity | View activity stats |
album.read | Album | View albums |
album.create | Album | Create albums |
album.update | Album | Modify album metadata/assets |
album.delete | Album | Delete albums |
album.addAsset | Album | Add assets to album |
album.removeAsset | Album | Remove assets from album |
album.share | Album | Add/change album users |
asset.read | Asset | View assets |
asset.create | Asset | Upload assets |
asset.update | Asset | Modify asset metadata |
asset.delete | Asset | Delete assets |
asset.view | Asset | Access asset binary |
asset.download | Asset | Download assets |
asset.upload | Asset | Upload via shared link |
apiKey.read | API Key | View API keys |
apiKey.create | API Key | Create API keys |
apiKey.update | API Key | Modify API keys |
apiKey.delete | API Key | Delete API keys |
library.read | Library | View external libraries |
library.create | Library | Create libraries |
library.update | Library | Modify libraries |
library.delete | Library | Delete libraries |
library.statistics | Library | View library stats |
person.read | Person | View recognized people |
person.create | Person | Create person records |
person.update | Person | Modify person names/merge |
person.delete | Person | Delete person records |
person.merge | Person | Merge person records |
person.reassign | Person | Reassign faces |
sharedLink.read | Shared Link | View shared links |
sharedLink.create | Shared Link | Create shared links |
sharedLink.update | Shared Link | Modify shared links |
sharedLink.delete | Shared Link | Delete shared links |
tag.read | Tag | View tags |
tag.create | Tag | Create tags |
tag.update | Tag | Modify tags |
tag.delete | Tag | Delete tags |
tag.asset | Tag | Tag/untag assets |
adminUser.read | Admin | View any user (admin) |
adminUser.create | Admin | Create users (admin) |
adminUser.update | Admin | Modify users (admin) |
adminUser.delete | Admin | Delete users (admin) |
adminSession.read | Admin | View any user's sessions |
adminSession.delete | Admin | Delete any user's session |
adminAuth.unlinkAll | Admin | Unlink all OAuth for user |
adminLibrary.* | Admin | Manage all libraries |
adminNotification.read | Admin | View all notifications |
adminNotification.create | Admin | Send test notifications |
adminSystemConfig.* | Admin | Read/write system config |
adminTag.* | Admin | Manage all tags |
adminUser.forcePw | Admin | Force password reset |
timeline.read | Timeline | Read timeline data |
stack.read | Stack | View asset stacks |
stack.create | Stack | Create stacks |
stack.update | Stack | Modify stacks |
stack.delete | Stack | Delete stacks |
Sources: mobile/openapi/lib/model/permission.dart server/src/repositories/access.repository.ts
Mobile apps have a specialized authentication flow that supports both OAuth and password authentication, with additional features for offline access and biometric authentication.
Mobile-Specific Security:
flutter_secure_storage (Keychain on iOS, KeyStore on Android)Sources: mobile/pubspec.yaml36 open-api/immich-openapi-specs.json631-642
Refresh this wiki
This wiki was recently refreshed. Please wait 2 days to refresh again.