{"record":{"id":"8a8df573a488ff2c","repo":"nextauthjs/next-auth","slug":"oauth-provider-returned-an-error-responsejson-e","errorCode":null,"errorMessage":"OAuth Provider returned an error: ${responseJson.error}","messagePattern":"OAuth Provider returned an error: (.+?)","errorType":"exception","errorClass":"OAuthCallbackError","httpStatus":null,"severity":"error","filePath":"packages/core/src/lib/actions/callback/oauth/callback.ts","lineNumber":204,"sourceCode":"  const requireIdToken = isOIDCProvider(provider)\n\n  if (provider[conformInternal]) {\n    switch (provider.id) {\n      case \"microsoft-entra-id\":\n      case \"azure-ad\": {\n        /**\n         * These providers return errors in the response body and\n         * need the authorization server metadata to be re-processed\n         * based on the `id_token`'s `tid` claim.\n         * @see: https://learn.microsoft.com/en-us/entra/identity-platform/v2-oauth2-auth-code-flow#error-response-1\n         */\n        const responseJson = await codeGrantResponse.clone().json()\n        if (responseJson.error) {\n          const cause = {\n            providerId: provider.id,\n            ...responseJson,\n          }\n          throw new OAuthCallbackError(\n            `OAuth Provider returned an error: ${responseJson.error}`,\n            cause\n          )\n        }\n        const { tid } = decodeJwt(responseJson.id_token)\n        if (typeof tid === \"string\") {\n          const tenantRe = /microsoftonline\\.com\\/(\\w+)\\/v2\\.0/\n          const tenantId = as.issuer?.match(tenantRe)?.[1] ?? \"common\"\n          const issuer = new URL(as.issuer.replace(tenantId, tid))\n          const discoveryResponse = await o.discoveryRequest(issuer, {\n            [o.customFetch]: provider[customFetch],\n          })\n          as = await o.processDiscoveryResponse(issuer, discoveryResponse)\n        }\n        break\n      }\n      default:\n        break","sourceCodeStart":186,"sourceCodeEnd":222,"githubUrl":"https://github.com/nextauthjs/next-auth/blob/a1a16a5a7780488c7449feece410033f445d0b31/packages/core/src/lib/actions/callback/oauth/callback.ts#L186-L222","documentation":"handleOAuth throws OAuthCallbackError when the OAuth token endpoint's code-grant response JSON contains an `error` field, meaning the provider rejected the authorization-code exchange. The provider's error payload (and providerId) is attached as `cause` for diagnostics. This surfaces provider-side rejections such as invalid codes or bad client credentials.","triggerScenarios":"During the sign-in callback, POSTing the authorization code to the provider's token endpoint returns 200/4xx JSON like {\"error\":\"invalid_grant\"} or {\"error\":\"invalid_client\"}; handleOAuth detects responseJson.error and throws.","commonSituations":"Authorization code already consumed (user refreshed the callback URL or double-submitted the callback); code expired; mismatched client_id/client_secret between issuer config and env vars; provider requires PKCE or client auth method (client_secret_post vs basic) the config doesn't use; clock skew invalidating tokens.","solutions":["Read error.cause (logged by Auth.js) to see the provider's exact error code and fix accordingly.","Ensure AUTH_*_CLIENT_ID / AUTH_*_CLIENT_SECRET (or provider config) exactly match the values registered at the provider.","Avoid reusing the callback URL: each authorization code is single-use; restart the sign-in flow instead of refreshing the callback page.","Verify provider settings: redirect URI, PKCE requirement, and token_endpoint_auth_method match the library's provider definition.","Check for clock skew between your server and the provider if errors are intermittent (expired codes)."],"exampleFix":"// before: client auth mismatch causes invalid_client\nAuth0Provider({ clientId: process.env.AUTH_AUTH0_ID })\n// after: supply secret correctly and set the right auth method\nAuth0Provider({\n  clientId: process.env.AUTH_AUTH0_ID,\n  clientSecret: process.env.AUTH_AUTH0_SECRET,\n  authorization: { params: { scope: \"openid email profile\" } },\n})","handlingStrategy":"try-catch","validationCode":"const id = process.env.AUTH_AUTH0_ID, secret = process.env.AUTH_AUTH0_SECRET\nif (!id || !secret) throw new Error(\"Missing OAuth client credentials\")","typeGuard":"function hasProviderError(r: unknown): r is { error: string } {\n  return typeof r === \"object\" && r !== null && \"error\" in r\n}","tryCatchPattern":"try {\n  await signIn(\"provider\", { redirectTo: \"/\" })\n} catch (e) {\n  if (e instanceof OAuthCallbackError) {\n    console.error(\"Provider rejected token exchange:\", e.cause)\n  }\n}","preventionTips":["Log error.cause to capture the provider's error code on every OAuth failure","Keep client credentials in env and validate presence at startup","Never reuse authorization codes; restart the flow instead of refreshing callbacks","Match PKCE and token auth method settings with your provider app configuration"],"tags":["oauth","token-exchange","provider-error","callback"],"backgroundTag":"oauth-token-exchange-rejected","analyzedSha":"a1a16a5a7780488c7449feece410033f445d0b31","analyzedAt":"2026-08-28T21:52:38.200Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}