# How `user.service.ts` Manages User Accounts, Roles, and Permissions in Immich

> Discover how Immich user.service.ts manages user accounts, roles, and permissions. Learn about access control, admin restrictions, and self-service profile updates to secure your data.

- Repository: [Immich/immich](https://github.com/immich-app/immich)
- Tags: internals
- Published: 2026-02-27

---

**The `UserService` enforces access control through an `isAdmin` flag and authentication context, restricting user enumeration to administrators unless public mode is enabled, while strictly limiting profile updates to self-service operations only.**

Immich's server-side `UserService` serves as the central authority for user-related business logic and permission enforcement. Located at [`server/src/services/user.service.ts`](https://github.com/immich-app/immich/blob/main/server/src/services/user.service.ts), this service coordinates database access, authentication checks, and role-based access control (RBAC) to ensure users can only interact with their own data or perform administrative actions when explicitly authorized.

## Authentication-Aware Data Access

Every public method in `UserService` receives an `AuthDto` object containing the authenticated user's context. This pattern ensures that all data retrieval operations respect the caller's permissions before returning results.

### The Admin Flag Check

The service relies on the `isAdmin` boolean property stored on the `User` entity to gate access to sensitive operations. In the `search` method (lines 23-34), `UserService` checks `auth.user.isAdmin` to determine whether the caller may enumerate all user accounts:

```typescript
// server/src/services/user.service.ts
async search(auth: AuthDto) {
  // Only admins can list all users unless public mode is enabled
  if (!auth.user.isAdmin && !this.config.server.publicUsers) {
    return [auth.user]; // Return only self
  }
  return this.userRepository.getAll();
}

```

This flag is populated during user creation in `AuthService` from the JWT claim `roleClaim` (typically `"immich_role"`) and persists in the database via [`server/src/repositories/user.repository.ts`](https://github.com/immich-app/immich/blob/main/server/src/repositories/user.repository.ts).

### Public User Mode Configuration

When `config.server.publicUsers` is enabled (configured in [`server/src/dtos/system-config.dto.ts`](https://github.com/immich-app/immich/blob/main/server/src/dtos/system-config.dto.ts)), the service relaxes administrative restrictions. This allows any authenticated user to retrieve the full user list, which supports public gallery instances where user discovery is expected behavior. The check occurs alongside the admin validation in the `search` method, creating a logical OR condition for access.

## Self-Service Profile Management

Immich strictly enforces that users can only modify their own profiles through the `updateMe` method, preventing privilege escalation attacks where one user might attempt to alter another's credentials or metadata.

### Enforcing Ownership in updateMe

The `updateMe` method (lines 46-69) uses the authentication context to bind every update operation to the requesting user's ID:

```typescript
@Patch('me')
async updateMe(@Auth() auth: AuthDto, @Body() dto: UserUpdateMeDto) {
  // Guarantees users can only edit their own record
  return this.userService.updateMe(auth, dto);
}

```

Internally, the service fetches the user record using `auth.user.id`, ensuring that even if a malicious client sends a different user ID in the payload, the operation targets only the authenticated account. This design prevents role changes through profile updates, as the method only accepts fields like email, name, and password—not the `isAdmin` flag.

### Password Hashing and Security Flags

When users update their passwords through `updateMe`, the service delegates hashing to `cryptoRepository.hashBcrypt` before persisting changes. The method also automatically clears the `shouldChangePassword` flag upon successful password modification, which is used to force password resets after administrative actions or security events.

## Privileged Operations and Role Enforcement

Methods exposing sensitive data or administrative capabilities implicitly rely on the admin flag baked into the `User` entity rather than implementing separate permission checks.

### License Management and Admin Restrictions

Operations like `getMe`, `getLicense`, and `setLicense` respect the admin flag when determining what data to return. While the service itself never mutates roles—role assignment occurs exclusively during authentication flows in `AuthService`—it strictly honors the `isAdmin` flag when deciding whether to expose license information or allow license modifications.

### Background Jobs for User Cleanup

Privileged deletion logic runs through scheduled jobs `handleUserDeleteCheck` and `handleUserDelete`. These methods execute only for users marked with a deletion flag and respect the `deleteDelay` configuration from `SystemConfig`. By isolating permanent deletion to background jobs, the service ensures that destructive operations occur under controlled conditions separate from immediate API request handling.

## Separation of Concerns: Server vs. Web Layer

Immich maintains two distinct [`user.service.ts`](https://github.com/immich-app/immich/blob/main/user.service.ts) implementations to separate business logic from UI concerns. The server-side implementation ([`server/src/services/user.service.ts`](https://github.com/immich-app/immich/blob/main/server/src/services/user.service.ts)) contains the actual permission checks, database interactions, and cryptographic operations described above.

In contrast, the web layer ([`web/src/lib/services/user.service.ts`](https://github.com/immich-app/immich/blob/main/web/src/lib/services/user.service.ts)) provides thin wrappers around the autogenerated SDK (`@immich/sdk`) for UI-specific actions:

```typescript
// web/src/lib/services/user.service.ts
import { lockAuthSession } from '@immich/sdk';
import { eventManager } from '$lib/managers/event-manager.svelte';

export const lockSession = async () => {
  await lockAuthSession();               // Calls backend API
  eventManager.emit('SessionLocked');    // Notifies UI components
};

```

This architecture ensures that all security-critical logic resides on the server, while the web client handles only presentation-layer events like session locking, PIN resets, and password change workflows.

## Summary

- **`UserService` enforces RBAC** through the `isAdmin` flag checked in methods like `search`, with optional relaxation via `publicUsers` configuration.
- **Self-service updates are strictly bounded** by `auth.user.id` in `updateMe`, preventing users from modifying other accounts or elevating privileges.
- **Password security** uses bcrypt hashing via `cryptoRepository.hashBcrypt` and manages the `shouldChangePassword` flag automatically.
- **Privileged cleanup operations** run as background jobs (`handleUserDeleteCheck`) with configurable delays, separate from interactive API flows.
- **Role assignment** occurs in `AuthService` during OAuth/JWT processing, not in `UserService`, which only reads and respects the admin flag.

## Frequently Asked Questions

### What is the difference between the server and web UserService in Immich?

The server-side `UserService` ([`server/src/services/user.service.ts`](https://github.com/immich-app/immich/blob/main/server/src/services/user.service.ts)) contains the core business logic, database access, and permission enforcement for user accounts. The web `UserService` ([`web/src/lib/services/user.service.ts`](https://github.com/immich-app/immich/blob/main/web/src/lib/services/user.service.ts)) acts as a thin client layer that calls the autogenerated SDK for UI-specific actions like locking sessions or resetting PINs, containing no actual permission logic.

### How does Immich determine if a user is an administrator?

Admin status is determined by the `isAdmin` boolean on the `User` entity. During authentication in `AuthService`, the system extracts the `roleClaim` from the OAuth provider or JWT token (typically `"immich_role"`) and sets this flag when creating or updating the user record. `UserService` reads this flag but never modifies it.

### Can regular users view other user accounts in Immich?

By default, only administrators can enumerate all users through the `search` method. However, if the server administrator enables `config.server.publicUsers` in the system configuration, any authenticated user can view the full user list. Without this setting, non-admin users receive only their own user record from the API.

### Where is the admin role actually assigned to new users?

The admin role is assigned during the authentication flow in [`server/src/services/auth.service.ts`](https://github.com/immich-app/immich/blob/main/server/src/services/auth.service.ts), not in `UserService`. When a user logs in via OAuth or JWT, `AuthService` maps the identity provider's role claim to the `isAdmin` flag and persists it through `UserRepository`. `UserService` subsequently uses this flag for all permission checks but does not have authority to grant or revoke admin privileges.