{"record":{"id":"8d8b711dae91bcc4","repo":"immich-app/immich","slug":"invalid-password","errorCode":null,"errorMessage":"Invalid password","messagePattern":"Invalid password","errorType":"http","errorClass":"UnauthorizedException","httpStatus":401,"severity":"warning","filePath":"server/src/services/shared-link.service.ts","lineNumber":40,"sourceCode":"      .getAll({ userId: auth.user.id, id, albumId })\n\n      .then((links) => links.map((link) => mapSharedLink(link, { stripAssetMetadata: false })));\n  }\n\n  async login(auth: AuthDto, dto: SharedLinkLoginDto) {\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) {\n      throw new BadRequestException('Shared link is not password protected');\n    }\n\n    if (password !== dto.password) {\n      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');","sourceCodeStart":22,"sourceCodeEnd":58,"githubUrl":"https://github.com/immich-app/immich/blob/199723261c6ffa897fec8ccdaea6359e39c37cc3/server/src/services/shared-link.service.ts#L22-L58","documentation":"SharedLinkService.login() compares the submitted password to the stored shared-link password with a strict equality (password !== dto.password). A mismatch throws UnauthorizedException 'Invalid password' (shared-link.service.ts:40, HTTP 401). Note the comparison is plain string equality, not constant-time.","triggerScenarios":"Submitting the wrong password for a password-protected shared link on the login endpoint — typo, wrong case, or an outdated password after the owner changed it.","commonSituations":"User mistyping the password, copy-paste with trailing whitespace, or the link owner rotated the password while a viewer had a stale one.","solutions":["Re-enter the password carefully, matching case and without surrounding whitespace.","If forgotten, ask the link owner to share the current password or to reset it via PATCH /shared-links/:id.","On the client, trim() the input only if the owner's stored value is also trimmed (otherwise do not trim)."],"exampleFix":"// before\nawait login({ password: 'secret ' }); // trailing space\n// after\nawait login({ password: 'secret' });","handlingStrategy":"try-catch","validationCode":"null","typeGuard":"null","tryCatchPattern":"try {\n  await sharedLinkApi.login({ password });\n} catch (e) {\n  if (e instanceof UnauthorizedException && /invalid password/i.test(e.message)) {\n    showFieldError('password', 'Incorrect password. Try again.');\n  } else throw e;\n}","preventionTips":["Show a retry prompt on wrong password rather than aborting.","If repeatedly wrong, the owner may have changed it — offer to request access."],"tags":["shared-link","password","auth"],"backgroundTag":null,"analyzedSha":"199723261c6ffa897fec8ccdaea6359e39c37cc3","analyzedAt":"2026-08-12T04:54:27.085Z","schemaVersion":2},"datasetVersion":"2026-08-12T13:17:24.610Z"}