{"record":{"id":"01c794533d2d2865","repo":"hcengineering/platform","slug":"forbidden","errorCode":"Forbidden","errorMessage":"platform.status.Forbidden","messagePattern":"platform\\.status\\.Forbidden","errorType":"error_code","errorClass":"PlatformError","httpStatus":null,"severity":"error","filePath":"server/account/src/operations.ts","lineNumber":1646,"sourceCode":"  ctx: MeasureContext,\n  db: AccountDB,\n  branding: Branding | null,\n  token: string,\n  params: { password: string }\n): Promise<LoginInfo> {\n  const { password } = params\n\n  if (password == null || password === '') {\n    throw new PlatformError(new Status(Severity.ERROR, platform.status.BadRequest, {}))\n  }\n\n  const { account, extra } = decodeTokenVerbose(ctx, token)\n  ctx.info('Restoring password', { account, extra })\n\n  const email = extra?.restoreEmail\n  if (email === undefined) {\n    ctx.error('Email not provided for restoration', { account, extra })\n    throw new PlatformError(new Status(Severity.ERROR, platform.status.Forbidden, {}))\n  }\n\n  const emailSocialId = await getEmailSocialId(db, email)\n\n  if (emailSocialId == null) {\n    ctx.error('Email social id not found', { email })\n    throw new PlatformError(\n      new Status(Severity.ERROR, platform.status.SocialIdNotFound, { value: email, type: SocialIdType.EMAIL })\n    )\n  }\n\n  await setPassword(ctx, db, branding, account, password)\n\n  if (emailSocialId.verifiedOn == null) {\n    await db.socialId.update({ key: emailSocialId.key }, { verifiedOn: Date.now() })\n  }\n\n  return await login(ctx, db, branding, token, { email, password })","sourceCodeStart":1628,"sourceCodeEnd":1664,"githubUrl":"https://github.com/hcengineering/platform/blob/63e28dc96483967b2fc21c881b3f1023c1de7718/server/account/src/operations.ts#L1628-L1664","documentation":"Thrown by restorePassword when the decoded restore token has no restoreEmail in its extra payload. Without the email bound into the token, the service refuses to restore the password, since it cannot validate which identity is being restored.","triggerScenarios":"Using a restore token that was issued without restoreEmail in extra, or a token of a different kind (not a restore token) passed to restorePassword.","commonSituations":"Tokens generated by older service versions before restoreEmail was added to extra; reusing a changePassword or workspace token for restore; hand-crafted or corrupted tokens.","solutions":["Request a fresh restore token via the standard restore request flow so restoreEmail is embedded.","Use decodeTokenVerbose locally to check extra.restoreEmail before calling the endpoint.","Upgrade/align service versions so token issuance includes restoreEmail.","Verify you are passing the restore token, not another token type."],"exampleFix":"// before: reuse old token\nconst ok = await accountClient.restorePassword(ctx, oldToken, { password })\n// after: request a new restore token containing the email\nconst info = await accountClient.requestRestore(ctx, email)\nawait accountClient.restorePassword(ctx, info.token, { password })","handlingStrategy":"validation","validationCode":"const { extra } = decodeTokenVerbose(ctx, token)\nif (extra?.restoreEmail === undefined) {\n  throw new Error('Token is not a restore token (missing restoreEmail)')\n}","typeGuard":"function isRestoreToken(extra: Record<string, any> | undefined): extra is { restoreEmail: string } & Record<string, any> {\n  return typeof extra?.restoreEmail === 'string' && extra.restoreEmail.length > 0\n}","tryCatchPattern":"try {\n  await accountClient.restorePassword(ctx, token, { password })\n} catch (err) {\n  if (err instanceof PlatformError && err.status.code === platform.status.Forbidden) {\n    await accountClient.requestRestore(ctx, email) // re-issue fresh restore token\n  } else throw err\n}","preventionTips":["Always issue restore tokens through the official restore request flow.","Label token kinds and verify kind before use.","Keep token payload schema consistent across service versions."],"tags":["token","password-restore","forbidden"],"backgroundTag":"invalid-restore-token","analyzedSha":"63e28dc96483967b2fc21c881b3f1023c1de7718","analyzedAt":"2026-08-29T15:21:27.377Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}