{"record":{"id":"4cd3d5bb0bc1f800","repo":"mastra-ai/mastra","slug":"google-id-token-has-expired","errorCode":null,"errorMessage":"Google ID token has expired","messagePattern":"Google ID token has expired","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"auth/google/src/auth-provider.ts","lineNumber":364,"sourceCode":"    return this.hostedDomain;\n  }\n\n  getClientId(): string {\n    return this.clientId;\n  }\n\n  private async verifyIdToken(token: string, nonce?: string): Promise<GoogleUser> {\n    const { payload } = await jwtVerify(token, this.jwks, {\n      issuer: GOOGLE_ISSUERS,\n      audience: this.clientId,\n    });\n\n    if (nonce && payload.nonce !== nonce) {\n      throw new Error('Invalid Google ID token nonce');\n    }\n\n    if (hasExpired(payload)) {\n      throw new Error('Google ID token has expired');\n    }\n\n    const user = mapGoogleClaimsToUser(payload);\n    if (!user.googleId) {\n      throw new Error('Google ID token is missing subject');\n    }\n\n    if (!this.isHostedDomainAllowed(user.hostedDomain)) {\n      throw new Error('Google user is not in an allowed hosted domain');\n    }\n\n    return user;\n  }\n\n  private isHostedDomainAllowed(hostedDomain: string | undefined): boolean {\n    if (this.allowedDomains.length === 0) return true;\n    const domain = normalizeDomain(hostedDomain);\n    if (!domain) return false;","sourceCodeStart":346,"sourceCodeEnd":382,"githubUrl":"https://github.com/mastra-ai/mastra/blob/75dd419e613fe9c39f846ffc500716141b74fda6/auth/google/src/auth-provider.ts#L346-L382","documentation":"After signature verification, verifyIdToken additionally checks the token's expiry (hasExpired on the JWT payload) and rejects tokens that are no longer valid. This is a defense-in-depth check on top of jwtVerify so expired Google ID tokens are never mapped to a user.","triggerScenarios":"Calling verifyIdToken with a token whose exp claim is in the past — e.g. a cached ID token reused after its ~1 hour lifetime, or a clock-skewed server evaluating a still-valid token as expired.","commonSituations":"Storing the ID token and re-verifying it on later requests instead of using refresh tokens; replaying old tokens in integration tests; server clocks drifting significantly from real time.","solutions":["Obtain a fresh ID token by re-running the OAuth login flow (or use refresh tokens per Google's guidance) instead of re-verifying an old one.","Verify the token promptly after receiving it in the callback; don't persist and re-verify later.","Check server clock sync (NTP) if the token appears expired immediately after issuance.","In tests, mint tokens with an exp far enough in the future for the test duration."],"exampleFix":"// before (re-verifying cached token)\nconst user = await provider.verifyIdToken(cache.get('idToken'), nonce);\n\n// after (fresh token per flow)\nconst user = await provider.verifyIdToken(callbackToken, nonce);","handlingStrategy":"try-catch","validationCode":"const { payload } = decodeJwt(token);\nif (typeof payload.exp === 'number' && payload.exp * 1000 < Date.now()) {\n  // token already expired: trigger re-auth instead of calling verifyIdToken\n}","typeGuard":null,"tryCatchPattern":"try {\n  const user = await provider.verifyIdToken(token, nonce);\n} catch (err) {\n  if (err instanceof Error && err.message === 'Google ID token has expired') {\n    // send the user back through the OAuth flow for a fresh token\n    return res.redirect(await provider.getLoginUrl(redirectUri, state));\n  }\n  throw err;\n}","preventionTips":["Verify the ID token immediately in the OAuth callback; don't cache and re-verify later.","Use refresh tokens for long-lived access instead of reusing ID tokens.","Keep server clocks NTP-synced to avoid false expiry.","In tests, set token exp comfortably in the future relative to test runtime."],"tags":["oauth","jwt","expired","token-validation","google"],"backgroundTag":"jwt-token-expired","analyzedSha":"75dd419e613fe9c39f846ffc500716141b74fda6","analyzedAt":"2026-08-30T00:15:31.844Z","schemaVersion":2},"datasetVersion":"2026-08-30T03:17:51.788Z"}