immich-app/immich · error · UnauthorizedException

Password login has been disabled

Error message

Password login has been disabled

What it means

Thrown by AuthService.login (POST /auth/login) when the server config has passwordLogin.enabled === false. The administrator has disabled password authentication (typically an OAuth-only deployment), so any password login attempt is rejected up front with 401 Unauthorized before credentials are even checked.

Source

Thrown at server/src/services/auth.service.ts:62

export type ValidateRequest = {
  headers: IncomingHttpHeaders;
  queryParams: Record<string, string>;
  metadata: {
    sharedLinkRoute: boolean;
    adminRoute: boolean;
    /** `false` explicitly means no permission is required, which otherwise defaults to `all` */
    permission?: Permission | false;
    uri: string;
  };
};

@Injectable()
export class AuthService extends BaseService {
  async login(dto: LoginCredentialDto, details: LoginDetails) {
    const config = await this.getConfig({ withCache: false });
    if (!config.passwordLogin.enabled) {
      throw new UnauthorizedException('Password login has been disabled');
    }

    const user = await this.userRepository.getByEmail(dto.email, { withPassword: true });
    // Always run bcrypt so response time is constant regardless of whether the email
    // is registered, preventing timing-based user enumeration.
    const isAuthenticated = this.cryptoRepository.compareBcrypt(dto.password, user?.password ?? LOGIN_DUMMY_HASH);

    if (!user || !user.password || !isAuthenticated) {
      this.logger.warn(`Failed login attempt for user ${dto.email} from ip address ${details.clientIp}`);
      throw new UnauthorizedException('Incorrect email or password');
    }

    return this.createLoginResponse(user, details);
  }

  async logout(auth: AuthDto, authType: AuthType): Promise<LogoutResponseDto> {
    let oauthBearerToken: string | undefined;
    if (auth.session) {

View on GitHub (pinned to 199723261c)

Solutions

  1. Switch the client to OAuth login flow (GET /oauth/authorize etc.).
  2. If password login is intended, set passwordLogin.enabled=true in the server system config and restart.
  3. Check the /server-feature-flags or admin system settings to confirm which auth methods are enabled.

Example fix

// before
await api.post('/auth/login', { email, password });

// after
const cfg = await api.get('/oauth/config');
if (cfg.data.enabled) {
  window.location = cfg.data.url; // redirect to IdP
} else {
  await api.post('/auth/login', { email, password });
}
Defensive patterns

Strategy: validation

Validate before calling

// Discover enabled auth methods before attempting password login.
const { data: oauth } = await api.get('/oauth/config');
if (oauth.enabled) {
  throw new Error('Password login disabled; use OAuth.');
}
await api.post('/auth/login', { email, password });

Try / catch

try {
  await api.post('/auth/login', { email, password });
} catch (e) {
  if (e.response?.status === 401 && /disabled/i.test(e.response?.data?.message)) {
    redirectToOAuth(); // password login is off
  } else throw e;
}

Prevention

When it happens

Trigger: POST /auth/login on a server whose passwordLogin.enabled config is false; an IdP-only deployment where someone still tries email/password; admin flipped the flag and clients haven't adapted.

Common situations: OAuth-only Immich instance; misconfigured deployment expecting password login; legacy client/script still using password auth after a policy change.

Related errors


AI-assisted analysis of immich-app/immich@199723261c (2026-08-12). Data as JSON: /api/errors/044be41e0cfe17c6. Report an issue: GitHub.