{"record":{"id":"8e8bf68293124c01","repo":"Mintplex-Labs/anything-llm","slug":"token-expired","errorCode":null,"errorMessage":"Token expired.","messagePattern":"Token expired\\.","errorType":"exception","errorClass":null,"httpStatus":401,"severity":"error","filePath":"server/models/temporaryAuthToken.js","lineNumber":83,"sourceCode":"   * to be set in the browser localStorage for authentication.\n   * @param {string} publicToken - the token to validate against\n   * @returns {Promise<{sessionToken: string|null, token: import(\"@prisma/client\").temporary_auth_tokens & {user: import(\"@prisma/client\").users} | null, error: string | null}>}\n   */\n  validate: async function (publicToken = \"\") {\n    /** @type {import(\"@prisma/client\").temporary_auth_tokens & {user: import(\"@prisma/client\").users} | undefined | null} **/\n    let token;\n\n    try {\n      if (!publicToken)\n        throw new Error(\n          \"Public token is required to validate a temporary auth token.\"\n        );\n      token = await prisma.temporary_auth_tokens.findUnique({\n        where: { token: String(publicToken) },\n        include: { user: true },\n      });\n      if (!token) throw new Error(\"Invalid token.\");\n      if (token.expiresAt < new Date()) throw new Error(\"Token expired.\");\n      if (token.user.suspended) throw new Error(\"User account suspended.\");\n\n      // Create a new session token for the user valid for 30 days\n      const sessionToken = makeJWT(\n        { id: token.user.id, username: token.user.username },\n        process.env.JWT_EXPIRY\n      );\n\n      return { sessionToken, token, error: null };\n    } catch (error) {\n      console.error(\"FAILED TO VALIDATE TEMPORARY AUTH TOKEN.\", error.message);\n      return { sessionToken: null, token: null, error: error.message };\n    } finally {\n      // Delete the token after it has been used under all circumstances if it was retrieved\n      if (token)\n        await prisma.temporary_auth_tokens.delete({ where: { id: token.id } });\n    }\n  },","sourceCodeStart":65,"sourceCodeEnd":101,"githubUrl":"https://github.com/Mintplex-Labs/anything-llm/blob/a145d4d87d086bdb31d50f9bf9cd9c46d311780c/server/models/temporaryAuthToken.js#L65-L101","documentation":"validate() found the token row but its expiresAt is earlier than the current time (compared against new Date()), so the token is expired and no session JWT is minted. Temporary auth tokens are intentionally short-lived.","triggerScenarios":"Opening a magic link after the expiry window elapsed; server clock set ahead of the token-creation clock; tokens created long ago and reused; delayed email delivery pushing the click past expiry.","commonSituations":"Users leaving login links in their inbox overnight; scheduled emails sent with tokens generated earlier in the pipeline; container clocks drifting after host suspension.","solutions":["Issue a new temporary auth token and use it promptly","Regenerate the token close to the moment of delivery, not long before","Verify server time is correct (NTP) so expiresAt comparisons are accurate","Surface a clear 'link expired, request a new one' UX instead of a raw error"],"exampleFix":"// before\n// generating a token far in advance of the email actually being sent\nconst link = buildLink(token); // token may expire before user clicks\n\n// after\n// generate at send time with a comfortable window\nconst { token } = await TemporaryAuthToken.create(user.id, hoursFromNow(24));\nconst link = buildLink(token);","handlingStrategy":"try-catch","validationCode":"null // the expiry state lives server-side in temporary_auth_tokens.expiresAt;\n// callers cannot reliably pre-check it - handle via the returned error field","typeGuard":null,"tryCatchPattern":"const { sessionToken, error } = await TemporaryAuthToken.validate(publicToken);\nif (error === 'Token expired.') {\n  // transparently re-issue and resend the magic link, then stop this attempt\n  const fresh = await issueNewTemporaryAuthToken(user.id);\n  return res.status(401).json({ error: 'Link expired. A new one has been sent.', reissued: true });\n}","preventionTips":["Generate tokens at send time with a window comfortable for real delivery and reading","Keep server clocks NTP-synced so expiresAt comparisons are trustworthy","Design the UX around expiry: 'request a new link' beats showing a raw error"],"tags":["auth","temporary-token","expiry"],"backgroundTag":"auth-token-expired","analyzedSha":"a145d4d87d086bdb31d50f9bf9cd9c46d311780c","analyzedAt":"2026-08-18T10:02:21.017Z","contentChangedAt":"2026-08-18T10:02:21.017Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}