{"record":{"id":"fb601b5a3cdf878f","repo":"affaan-m/ECC","slug":"invalid-token","errorCode":null,"errorMessage":"Invalid token","messagePattern":"Invalid token","errorType":"http","errorClass":null,"httpStatus":401,"severity":"error","filePath":"skills/backend-patterns/SKILL.md","lineNumber":119,"sourceCode":"\n### Middleware Pattern\n\n```typescript\n// Request/response processing pipeline\nexport function withAuth(handler: NextApiHandler): NextApiHandler {\n  return async (req, res) => {\n    const token = req.headers.authorization?.replace('Bearer ', '')\n\n    if (!token) {\n      return res.status(401).json({ error: 'Unauthorized' })\n    }\n\n    try {\n      const user = await verifyToken(token)\n      req.user = user\n      return handler(req, res)\n    } catch (error) {\n      return res.status(401).json({ error: 'Invalid token' })\n    }\n  }\n}\n\n// Usage\nexport default withAuth(async (req, res) => {\n  // Handler has access to req.user\n})\n```\n\n## Database Patterns\n\n### Query Optimization\n\n```typescript\n// PASS: GOOD: Select only needed columns\nconst { data } = await supabase\n  .from('markets')","sourceCodeStart":101,"sourceCodeEnd":137,"githubUrl":"https://github.com/affaan-m/ECC/blob/d8409a4b0813771235555e32e3d8046a73988bfa/skills/backend-patterns/SKILL.md#L101-L137","documentation":"The second failure mode of withAuth: a bearer token was supplied, but verifyToken(token) threw, so the middleware responds 401 { error: 'Invalid token' }. verifyToken (jsonwebtoken's jwt.verify) throws when the signature does not match the secret, the token is expired (exp), malformed/truncated, or uses an unexpected algorithm — the middleware collapses all of these into one message.","triggerScenarios":"Expired JWT (exp in the past) sent after a long-lived session; token signed with a different JWT_SECRET than the verifier uses (dev vs prod secret, or two services with different secrets); token truncated or corrupted during copy-paste (missing segment, embedded newline); token signed with alg the verifier rejects.","commonSituations":"Redeploy changed or failed to load JWT_SECRET so old tokens no longer verify; secret rotated without keeping the old key for a grace period; server clock skew making fresh tokens appear expired; token copied out of a log with surrounding quotes/whitespace.","solutions":["Get a fresh token (re-login / refresh) and retry — expiry is the most common cause","Verify the signer and verifier use the same JWT_SECRET env value (a service restarted with a missing secret will fail every token)","Decode the token client-side (jwt-decode or jwt.io) and inspect exp, alg, and issuer to see what verification would reject","If secrets were rotated, keep the previous key accepted for a transition window; if clocks drift, sync server time (NTP)"],"exampleFix":"// before - every request 401s after token expiry\nconst res = await fetch('/api/protected', { headers: { Authorization: `Bearer ${token}` } })\nif (res.status === 401) throw new Error('broken')\n\n// after - refresh once on 401 and retry\nlet res = await fetch('/api/protected', { headers: { Authorization: `Bearer ${token}` } })\nif (res.status === 401) {\n  token = await refreshSession()\n  res = await fetch('/api/protected', { headers: { Authorization: `Bearer ${token}` } })\n}","handlingStrategy":"retry","validationCode":"// Client: cheap pre-flight — refuse to send tokens that are already expired or malformed\nfunction decodeJwt(token: string): { exp?: number } | null {\n  try {\n    const payload = JSON.parse(atob(token.split('.')[1].replace(/-/g, '+').replace(/_/g, '/')))\n    return typeof payload === 'object' ? payload : null\n  } catch { return null }\n}\nconst isUsableToken = (token: string) =>\n  token.split('.').length === 3 && (decodeJwt(token)?.exp ?? 0) * 1000 > Date.now() + 30_000","typeGuard":"const isExpiredToken = (token: string): boolean => {\n  const p = decodeJwt(token)\n  return p === null || (p.exp !== undefined && p.exp * 1000 <= Date.now())\n}","tryCatchPattern":"// Refresh-once pattern: one retry after token refresh, then give up (prevents refresh loops)\nlet res = await request(token)\nif (res.status === 401) {\n  token = await refreshSession()\n  res = await request(token)\n  if (res.status === 401) await logout() // genuinely invalid — stop retrying\n}","preventionTips":["Refresh tokens proactively before exp rather than waiting for a 401","Ensure every service verifying JWTs shares the same secret/keyset (check env wiring per deployment)","After secret rotation, accept the previous key for a grace window","Never copy tokens through logs or shells that can add quotes/whitespace — corruption causes 'Invalid token'"],"tags":["auth","http-401","jwt","token-expired","middleware"],"backgroundTag":"jwt-token-invalid","analyzedSha":"d8409a4b0813771235555e32e3d8046a73988bfa","analyzedAt":"2026-08-26T12:15:34.022Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}