immich-app/immich · error · UnauthorizedException
Password login has been disabled
Error message
Password login has been disabled
What it means
AuthService.login first reads the server config and rejects password-based sign-in with this UnauthorizedException (401) when the passwordLogin.enabled setting is false. This is a deliberate server policy, not a credential problem — typically set when the instance is OAuth/OIDC-only.
Solutions
- Log in via the configured OAuth/OIDC flow instead of POST /api/auth/login.
- Re-enable password login in Administration > Settings > Authentication (or set server config passwordLogin.enabled=true) if password access is intended.
- For programmatic access, create an API key and use the x-api-key header instead of password auth.
- Check the server config (GET /api/server/config) to confirm passwordLogin.enabled before attempting password auth.
Example fix
// before
await immichApi.login({ email, password }); // 401: password login disabled
// after
const cfg = await immichApi.getServerConfig();
if (!cfg.passwordLoginEnabled) {
client.setApiKey(process.env.IMMICH_API_KEY); // use API key instead
} Defensive patterns
Strategy: fallback
Validate before calling
const cfg = await api.getServerConfig();
if (!cfg.passwordLoginEnabled) console.warn('Password login disabled; use OAuth or API key'); Try / catch
try { await api.login({ email, password }); } catch (e) { if (e.status === 401) startOAuthFlow(); else throw e; } Prevention
- Check server config for passwordLoginEnabled before offering email/password forms
- Prefer API keys for programmatic access
- Keep IdP client registrations in sync with Immich auth settings
When it happens
Trigger: POST /api/auth/login on an Immich server whose config has passwordLogin.enabled === false (admin disabled password login in Server Settings, or config via env/immich.json omits/enables only OAuth).
Common situations: Self-hosters switching to SSO (OAuth) and disabling password login, then old clients/CLIs/scripts still authenticating with email+password; fresh setups where password login was disabled by template config; users who never set a password because accounts were provisioned via OAuth.
Related errors
- Incorrect email or password
- Received backchannel logout request but OAuth is not enabled
- Error backchannel logout: token validation failed
- Invalid logout token: no claims found
- Not in maintenance mode
AI-assisted analysis of immich-app/immich@f48d4b3321 (2026-09-15).
Data as JSON: /api/errors/044be41e0cfe17c6.
Report an issue: GitHub.
Appendix: source
Thrown at server/src/services/auth.service.ts:63
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 f48d4b3321)