anomalyco/sst · error · Error

Missing token parameter

Error message

Missing token parameter

What it means

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.

Source

Thrown at sdk/js/src/auth/adapter/link.ts:29

        .setExpirationTime("10m")
        .sign(await ctx.signing.privateKey());

      const url = new URL(new URL(c.req.url).origin);
      url.pathname = `/${ctx.name}/callback`;
      for (const key of url.searchParams.keys()) {
        url.searchParams.delete(key);
      }
      url.searchParams.set("token", token);
      const resp = ctx.forward(
        c,
        await config.onLink(url.toString(), c.req.query()),
      );
      return resp;
    });

    routes.get("/callback", async (c) => {
      const token = c.req.query("token");
      if (!token) throw new Error("Missing token parameter");
      const verified = await jwtVerify(token, await ctx.signing.publicKey());
      const resp = await ctx.success(c, { claims: verified.payload as any });
      return resp;
    });
  } satisfies Adapter<{ claims: Record<string, string> }>;
}

View on GitHub (pinned to a0bd20f762)

Solutions

  1. Ensure your `onLink` handler delivers the full `link` URL verbatim, including the `token` query parameter.
  2. Check whether your email/link provider rewrites or truncates URLs; use their mechanisms for preserving query strings.
  3. Confirm the user is reaching the callback via the link generated by /authorize, not by navigating to the callback URL directly.
  4. Add a redirect or friendly error page for missing-token callbacks by catching this error in your auth handler.

Example fix

// before: onLink rebuilds the URL and loses the token
async (link) => Response.redirect(link.split('?')[0], 302)
// after: keep the full link including ?token=...
async (link) => Response.redirect(link, 302)
Defensive patterns

Strategy: validation

Validate before calling

const url = new URL(request.url);
if (!url.searchParams.get("token")) {
  return new Response("Redirecting...", { status: 302, headers: { Location: "/login" } });
}

Type guard

function hasToken(req: Request): req is Request & { token: string } {
  return new URL(req.url).searchParams.has("token");
}

Prevention

When it happens

Trigger: 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`.

Common situations: 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.

Related errors


AI-assisted analysis of anomalyco/sst@a0bd20f762 (2026-08-30). Data as JSON: /api/errors/fd831b24f36b0875. Report an issue: GitHub.