{"record":{"id":"837a032591a3970c","repo":"immich-app/immich","slug":"oauth-is-not-enabled","errorCode":null,"errorMessage":"OAuth is not enabled","messagePattern":"OAuth is not enabled","errorType":"http","errorClass":"BadRequestException","httpStatus":400,"severity":"warning","filePath":"server/src/services/auth.service.ts","lineNumber":274,"sourceCode":"      return this.validateSession(session, headers);\n    }\n\n    if (apiKey) {\n      return this.validateApiKey(apiKey);\n    }\n\n    throw new UnauthorizedException('Authentication required');\n  }\n\n  getMobileRedirect(url: string) {\n    return `${MOBILE_REDIRECT}?${url.split('?', 2)[1] || ''}`;\n  }\n\n  async authorize(dto: OAuthConfigDto) {\n    const { oauth } = await this.getConfig({ withCache: false });\n\n    if (!oauth.enabled) {\n      throw new BadRequestException('OAuth is not enabled');\n    }\n\n    return await this.oauthRepository.authorize(\n      oauth,\n      this.resolveRedirectUri(oauth, dto.redirectUri),\n      dto.state,\n      dto.codeChallenge,\n    );\n  }\n\n  async callback(dto: OAuthCallbackDto, headers: IncomingHttpHeaders, loginDetails: LoginDetails) {\n    const { oauth } = await this.getConfig({ withCache: false });\n    if (!oauth.enabled) {\n      throw new BadRequestException('OAuth is not enabled');\n    }\n\n    const expectedState = dto.state ?? this.getCookieOauthState(headers);\n    if (!expectedState?.length) {","sourceCodeStart":256,"sourceCodeEnd":292,"githubUrl":"https://github.com/immich-app/immich/blob/199723261c6ffa897fec8ccdaea6359e39c37cc3/server/src/services/auth.service.ts#L256-L292","documentation":"BadRequestException (HTTP 400) thrown by AuthService.authorize when system config `oauth.enabled` is false. The authorize endpoint (POST /oauth/authorize) is the OAuth entry point; calling it without enabling OAuth in Administration > Settings returns this. Config is re-read without cache so a recent admin toggle is honored immediately.","triggerScenarios":"POST /oauth/authorize with body {redirectUri} before an admin has toggled OAuth enabled in server settings. The web login screen may show an OAuth button based on stale client config while the server has it disabled.","commonSituations":"Admin disabled OAuth after the web app cached the enabled state; OAuth config import/migration reset the flag; multi-instance deployment where the user hits a node with a different config.","solutions":["As admin, enable OAuth in Administration > Settings > OAuth and configure the issuer/clientId.","Reload the web app so it fetches the fresh server config and hides the OAuth button.","Verify the config persisted by re-opening Settings after save.","If using env overrides, confirm OAUTH_ENABLED is not forced off."],"exampleFix":"// before\nawait axios.post('/oauth/authorize', { redirectUri: 'https://app/callback' });\n\n// after\n// admin enables OAuth in UI first, then:\nawait axios.post('/oauth/authorize', { redirectUri: 'https://app/callback' });","handlingStrategy":"validation","validationCode":"async function oauthEnabled(): Promise<boolean> {\n  const { data } = await axios.get('/server/features');\n  return data.config?.oauth?.enabled === true || data.oauth?.enabled === true;\n}","typeGuard":"function isOauthEnabled(features: { oauth?: { enabled?: boolean } }): boolean {\n  return features.oauth?.enabled === true;\n}","tryCatchPattern":"try {\n  await axios.post('/oauth/authorize', { redirectUri });\n} catch (e) {\n  if (e.response?.data?.message === 'OAuth is not enabled') {\n    hideOauthButton();\n  } else throw e;\n}","preventionTips":["Drive the OAuth button visibility from a fresh server features fetch.","Refresh server config after admin settings changes.","Surface a clear 'OAuth disabled' message in the UI instead of a generic error."],"tags":["auth","oauth","config","nestjs","immich"],"backgroundTag":null,"analyzedSha":"199723261c6ffa897fec8ccdaea6359e39c37cc3","analyzedAt":"2026-08-12T04:54:27.085Z","schemaVersion":2},"datasetVersion":"2026-08-12T13:17:24.610Z"}