{"record":{"id":"bb59842557400fc3","repo":"cube-js/cube","slug":"invalid-token","errorCode":null,"errorMessage":"Invalid token","messagePattern":"Invalid token","errorType":"http","errorClass":"CubejsHandlerError","httpStatus":403,"severity":"error","filePath":"packages/cubejs-api-gateway/src/gateway.ts","lineNumber":2710,"sourceCode":"            403,\n            'Forbidden',\n            `Unable to verify, JWK with kid: \"${decoded.header.kid}\" not found`\n          );\n        }\n\n        return verifyToken(auth, jwk);\n      };\n    }\n\n    return async (req, auth) => {\n      if (auth) {\n        try {\n          req.securityContext = await checkAuthFn(auth);\n          req.signedWithPlaygroundAuthSecret =\n            Boolean(internalOptions?.isPlaygroundCheckAuth) && hasDevTokenScope(req.securityContext);\n        } catch (e: any) {\n          if (this.enforceSecurityChecks) {\n            throw new CubejsHandlerError(403, 'Forbidden', 'Invalid token', e);\n          }\n        }\n      } else if (this.enforceSecurityChecks) {\n        // @todo Move it to 401 or 400\n        throw new CubejsHandlerError(403, 'Forbidden', 'Authorization header isn\\'t set');\n      }\n\n      return {\n        securityContext: req.securityContext\n      };\n    };\n  }\n\n  protected createCheckAuthFn(options: ApiGatewayOptions): PreparedCheckAuthFn {\n    const mainCheckAuthFn = options.checkAuth\n      ? this.wrapCheckAuth(options.checkAuth)\n      : this.createDefaultCheckAuth(options.jwt);\n","sourceCodeStart":2692,"sourceCodeEnd":2728,"githubUrl":"https://github.com/cube-js/cube/blob/7d981676b36392fec34088b9afab6bdcad40207c/packages/cubejs-api-gateway/src/gateway.ts#L2692-L2728","documentation":"This is the generic wrapper: when the configured checkAuth function (default JWT verification or a user-supplied `checkAuth`) throws for a presented token and the gateway runs with security checks enforced (production mode, no dev/prod toggle disabling them), Cube rethrows it as a 403 'Invalid token'. The original error is attached as `cause`, so the underlying reason (bad signature, expiry, missing kid, etc.) is inside it.","triggerScenarios":"Any Authorization header whose token fails the checkAuth function — expired JWT, wrong signing key, malformed token, custom checkAuth throwing — while `enforceSecurityChecks` is true (i.e. not running with CUBEJS_DEV_MODE-style disabled checks).","commonSituations":"Expired tokens after session timeout; mismatched JWT secret after rotating CUBEJS_API_SECRET or moving environments; clock skew between token issuer and Cube server; custom checkAuth rejecting valid users due to a bug; forgetting that in dev mode the same token succeeds but production enforces checks.","solutions":["Inspect the error's `cause` to get the real reason (e.g. 'jwt expired', 'invalid signature') and fix that underlying issue.","Re-generate the token with the correct current signing secret and algorithm configured for the Cube instance.","Verify client clock and token `exp`/`nbf` claims; issue tokens with a reasonable validity window.","If using a custom checkAuth, add logging/fix its exception path so valid tokens don't throw.","For local development only, run with dev mode enabled where enforceSecurityChecks is false (never in production)."],"exampleFix":"// before\nconst token = jwt.sign(payload, OLD_SECRET, { expiresIn: '1s' });\n// after\nconst token = jwt.sign(payload, process.env.CUBEJS_API_SECRET, { expiresIn: '1h' });","handlingStrategy":"try-catch","validationCode":"function tokenLooksValid(token, secret) {\n  try { jwt.verify(token, secret); return true; } catch { return false; }\n}\nif (!tokenLooksValid(myToken, process.env.CUBEJS_API_SECRET)) {\n  myToken = refreshToken(); // before calling Cube\n}","typeGuard":"function isCubeForbiddenError(e) {\n  return !!e && typeof e.status === 'number' && e.status === 403 && typeof e.message === 'string';\n}","tryCatchPattern":"try {\n  const rs = await cubeApi.load(query);\n} catch (e) {\n  if (isCubeForbiddenError(e) && e.message === 'Invalid token') {\n    const reason = e.cause?.message; // real cause: 'jwt expired', 'invalid signature', etc.\n    await refreshSession();\n    return cubeApi.load(query); // single retry after token refresh\n  }\n  throw e;\n}","preventionTips":["Log e.cause on 403s to surface the real verification failure","Use short-lived tokens with an automatic refresh flow in clients","Keep signing secrets in sync across environments via one source (env/secret manager)","Monitor clock skew between token issuer and Cube server (NTP)"],"tags":["auth","jwt","forbidden","token-expired"],"backgroundTag":"jwt-verification-failed","analyzedSha":"7d981676b36392fec34088b9afab6bdcad40207c","analyzedAt":"2026-09-02T03:45:10.400Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T15:18:49.778Z"}