{"record":{"id":"a10e5ca1eeb13a11","repo":"mastra-ai/mastra","slug":"token-exchange-failed-error-a10e5c","errorCode":null,"errorMessage":"Token exchange failed: ${error}","messagePattern":"Token exchange failed: (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"auth/okta/src/auth-provider.ts","lineNumber":438,"sourceCode":"    }\n\n    // Exchange code for tokens using client_secret (confidential client)\n    const tokenResponse = await fetch(`${this.endpointBase}/v1/token`, {\n      method: 'POST',\n      headers: {\n        'Content-Type': 'application/x-www-form-urlencoded',\n        Authorization: `Basic ${btoa(`${this.clientId}:${this.clientSecret}`)}`,\n      },\n      body: new URLSearchParams({\n        grant_type: 'authorization_code',\n        code,\n        redirect_uri: stored.redirectUri,\n      }),\n    });\n\n    if (!tokenResponse.ok) {\n      const error = await tokenResponse.text();\n      throw new Error(`Token exchange failed: ${error}`);\n    }\n\n    const tokens = (await tokenResponse.json()) as {\n      access_token: string;\n      id_token: string;\n      refresh_token?: string;\n      expires_in: number;\n      token_type: string;\n    };\n\n    // Verify and decode ID token\n    const { payload: idTokenPayload } = await jwtVerify(tokens.id_token, this.jwks, {\n      issuer: this.issuer,\n      audience: this.clientId,\n    });\n    const user = mapOktaClaimsToUser(idTokenPayload);\n\n    // Create encrypted session cookie.","sourceCodeStart":420,"sourceCodeEnd":456,"githubUrl":"https://github.com/mastra-ai/mastra/blob/75dd419e613fe9c39f846ffc500716141b74fda6/auth/okta/src/auth-provider.ts#L420-L456","documentation":"Thrown by handleCallback when the HTTP POST to Okta's /v1/token endpoint (exchanging the authorization code for tokens using client_secret) returns a non-OK response. The error message embeds Okta's raw error body, typically invalid_grant or invalid_client.","triggerScenarios":"Token exchange fails because: the authorization code was already used or expired (invalid_grant), redirect_uri doesn't exactly match the one used in the authorize request, clientId/clientSecret are wrong (invalid_client), or the Okta app/endpoint is misconfigured or unreachable.","commonSituations":"Callback replayed after refresh (code single-use); environment mismatch where callback uses a different redirect URI than the authorize request; rotated/incorrect client secret; Okta outage returning 5xx; dev/prod Okta domain mixups.","solutions":["Read the embedded error body: invalid_grant means restart the login flow (code is single-use/expired); invalid_client means fix clientId/clientSecret.","Ensure the redirect_uri sent to the token endpoint exactly matches the authorize request and the Okta app whitelist.","Verify OKTA_CLIENT_SECRET and OKTA_CLIENT_ID are current and for the correct Okta domain/app.","Check Okta system logs (admin console) for the failed token request details; retry after transient 5xx/outage."],"exampleFix":"// before (mismatched redirect between authorize and token exchange)\nredirectUri: 'https://localhost:3000/callback'\n// after (use the exact same, whitelisted URI in both steps and Okta app settings)\nredirectUri: process.env.OKTA_REDIRECT_URI // e.g. https://app.example.com/api/auth/sso/okta/callback","handlingStrategy":"try-catch","validationCode":"// before starting the flow, ensure redirect URI matches the one the callback will send\nif (authorizeRedirectUri !== process.env.OKTA_REDIRECT_URI) {\n  throw new Error('redirect_uri mismatch between authorize and token exchange');\n}","typeGuard":null,"tryCatchPattern":"try {\n  await provider.handleCallback(code, stateId);\n} catch (e) {\n  if (e instanceof Error && e.message.startsWith('Token exchange failed:')) {\n    if (e.message.includes('invalid_grant')) {\n      // restart SSO flow — code is single-use or expired\n    } else if (e.message.includes('invalid_client')) {\n      // check OKTA_CLIENT_ID / OKTA_CLIENT_SECRET / domain\n    }\n  } else throw e;\n}","preventionTips":["Always send the identical redirect_uri in authorize and token requests.","Rotate client secrets carefully and redeploy immediately after rotation.","Handle browser refresh on callback: redirect to login instead of replaying the code.","Monitor Okta system log for token endpoint failures."],"tags":["okta","sso","oauth","token-exchange","network"],"backgroundTag":"token-exchange-failed","analyzedSha":"75dd419e613fe9c39f846ffc500716141b74fda6","analyzedAt":"2026-08-30T00:15:31.844Z","schemaVersion":2},"datasetVersion":"2026-08-30T03:17:51.788Z"}