{"record":{"id":"fd831b24f36b0875","repo":"anomalyco/sst","slug":"missing-token-parameter","errorCode":null,"errorMessage":"Missing token parameter","messagePattern":"Missing token parameter","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"sdk/js/src/auth/adapter/link.ts","lineNumber":29,"sourceCode":"        .setExpirationTime(\"10m\")\n        .sign(await ctx.signing.privateKey());\n\n      const url = new URL(new URL(c.req.url).origin);\n      url.pathname = `/${ctx.name}/callback`;\n      for (const key of url.searchParams.keys()) {\n        url.searchParams.delete(key);\n      }\n      url.searchParams.set(\"token\", token);\n      const resp = ctx.forward(\n        c,\n        await config.onLink(url.toString(), c.req.query()),\n      );\n      return resp;\n    });\n\n    routes.get(\"/callback\", async (c) => {\n      const token = c.req.query(\"token\");\n      if (!token) throw new Error(\"Missing token parameter\");\n      const verified = await jwtVerify(token, await ctx.signing.publicKey());\n      const resp = await ctx.success(c, { claims: verified.payload as any });\n      return resp;\n    });\n  } satisfies Adapter<{ claims: Record<string, string> }>;\n}\n","sourceCodeStart":11,"sourceCodeEnd":36,"githubUrl":"https://github.com/anomalyco/sst/blob/a0bd20f762883e72a35caccb4896c42ce5b3f707/sdk/js/src/auth/adapter/link.ts#L11-L36","documentation":"The LinkAdapter's /callback route reads a signed JWT from the `token` query parameter. The token is generated by the /authorize route and passed to your `onLink` handler (e.g. embedded in a magic link you email the user). If the callback URL is hit without a `token` query param, the adapter throws this error instead of trying to verify undefined with jose.","triggerScenarios":"A GET request to `/<auth-name>/callback` with no `token` query parameter. Typical causes: the user's link provider stripped the query string, the user visited the callback URL directly, the `onLink` implementation truncated the URL, or a redirect dropped `url.searchParams`.","commonSituations":"Email providers (e.g. Outlook SafeLinks, some spam scanners) rewriting links and dropping query strings; a custom `onLink` that re-parses and rebuilds the URL losing searchParams; users clicking a partially copied link; bookmarking/expiring pages so only the base callback URL is visited.","solutions":["Ensure your `onLink` handler delivers the full `link` URL verbatim, including the `token` query parameter.","Check whether your email/link provider rewrites or truncates URLs; use their mechanisms for preserving query strings.","Confirm the user is reaching the callback via the link generated by /authorize, not by navigating to the callback URL directly.","Add a redirect or friendly error page for missing-token callbacks by catching this error in your auth handler."],"exampleFix":"// before: onLink rebuilds the URL and loses the token\nasync (link) => Response.redirect(link.split('?')[0], 302)\n// after: keep the full link including ?token=...\nasync (link) => Response.redirect(link, 302)","handlingStrategy":"validation","validationCode":"const url = new URL(request.url);\nif (!url.searchParams.get(\"token\")) {\n  return new Response(\"Redirecting...\", { status: 302, headers: { Location: \"/login\" } });\n}","typeGuard":"function hasToken(req: Request): req is Request & { token: string } {\n  return new URL(req.url).searchParams.has(\"token\");\n}","tryCatchPattern":null,"preventionTips":["Never rewrite or strip query strings from the link produced by /authorize in onLink.","Test the magic-link flow with email providers known to rewrite URLs.","Handle missing-token callbacks with a friendly redirect instead of a 500."],"tags":["auth","oauth","missing-parameter","jwt"],"backgroundTag":"missing-query-parameter","analyzedSha":"a0bd20f762883e72a35caccb4896c42ce5b3f707","analyzedAt":"2026-08-30T11:26:00.383Z","schemaVersion":2},"datasetVersion":"2026-08-30T13:17:10.514Z"}