{"record":{"id":"cd4a8c2a38e3b999","repo":"immich-app/immich","slug":"password-required","errorCode":null,"errorMessage":"Password required","messagePattern":"Password required","errorType":"http","errorClass":"UnauthorizedException","httpStatus":401,"severity":"warning","filePath":"server/src/services/shared-link.service.ts","lineNumber":58,"sourceCode":"      throw new UnauthorizedException('Invalid password');\n    }\n\n    return {\n      sharedLink: mapSharedLink(sharedLink, { stripAssetMetadata: !sharedLink.showExif }),\n      token: this.asToken({ id, password }),\n    };\n  }\n\n  async getMine(auth: AuthDto, authTokens: string[]) {\n    if (!auth.sharedLink) {\n      throw new ForbiddenException();\n    }\n\n    const sharedLink = await this.findOrFail(auth.user.id, auth.sharedLink.id);\n    const { id, password } = sharedLink;\n\n    if (password && !authTokens.includes(this.asToken({ id, password }))) {\n      throw new UnauthorizedException('Password required');\n    }\n\n    return mapSharedLink(sharedLink, { stripAssetMetadata: !sharedLink.showExif });\n  }\n\n  async get(auth: AuthDto, id: string): Promise<SharedLinkResponseDto> {\n    const sharedLink = await this.findOrFail(auth.user.id, id);\n    return mapSharedLink(sharedLink, { stripAssetMetadata: false });\n  }\n\n  async create(auth: AuthDto, dto: SharedLinkCreateDto): Promise<SharedLinkResponseDto> {\n    switch (dto.type) {\n      case SharedLinkType.Album: {\n        if (!dto.albumId) {\n          throw new BadRequestException('Invalid albumId');\n        }\n        await this.requireAccess({ auth, permission: Permission.AlbumShare, ids: [dto.albumId] });\n        break;","sourceCodeStart":40,"sourceCodeEnd":76,"githubUrl":"https://github.com/immich-app/immich/blob/199723261c6ffa897fec8ccdaea6359e39c37cc3/server/src/services/shared-link.service.ts#L40-L76","documentation":"In SharedLinkService.getMine(), when the resolved shared link HAS a password, the caller must present a valid auth token computed from {id, password}. If none of the request's authTokens match asToken({id, password}), UnauthorizedException 'Password required' is thrown (shared-link.service.ts:58, HTTP 401).","triggerScenarios":"Accessing the shared-link viewer (getMine) for a password-protected link without first completing the login flow that mints the shared-link auth cookie/token. The token is only issued by a successful login().","commonSituations":"Loading the share URL directly without the password cookie set, the auth cookie expiring or being cleared, or the password having changed since the token was issued.","solutions":["Complete the shared-link login() flow first; the returned token is what authorizes getMine.","Ensure the shared-link auth token/cookie is sent with the getMine request.","If the owner changed the password, re-run login to obtain a fresh token."],"exampleFix":"// before - skip login\nfetch('/shared-links/me', { headers: { 'x-immich-share-key': key } });\n// after - login first, then read\nconst { token } = await sharedLinkApi.login({ password });\nfetch('/shared-links/me', { headers: { 'x-immich-share-key': key, 'x-immich-share-token': token } });","handlingStrategy":"validation","validationCode":"// Ensure a shared-link auth token exists before calling getMine.\nlet token = getShareAuthToken();\nif (!token) {\n  const res = await sharedLinkApi.login({ password });\n  token = res.token;\n  storeShareAuthToken(token);\n}\nawait sharedLinkApi.getMine({ shareKey, token });","typeGuard":"const hasShareAuthToken = (tokens: string[] | undefined): boolean =>\n  Array.isArray(tokens) && tokens.length > 0;","tryCatchPattern":"try {\n  await sharedLinkApi.getMine();\n} catch (e) {\n  if (e instanceof UnauthorizedException && /password required/i.test(e.message)) {\n    // run login flow, then retry getMine\n    const { token } = await sharedLinkApi.login({ password });\n    return retryGetMineWith(token);\n  } else throw e;\n}","preventionTips":["Complete login() and persist the returned token before any getMine call.","Re-run login when the owner changes the password (token becomes invalid)."],"tags":["shared-link","password","auth","token"],"backgroundTag":null,"analyzedSha":"199723261c6ffa897fec8ccdaea6359e39c37cc3","analyzedAt":"2026-08-12T04:54:27.085Z","schemaVersion":2},"datasetVersion":"2026-08-12T13:17:24.610Z"}