{"record":{"id":"ffbacf913d799b9c","repo":"nextauthjs/next-auth","slug":"discovery-request-responded-with-an-invalid-issuer","errorCode":null,"errorMessage":"Discovery request responded with an invalid issuer. expected: ${issuer}","messagePattern":"Discovery request responded with an invalid issuer\\. expected: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/core/src/lib/actions/signin/authorization-url.ts","lineNumber":38,"sourceCode":"  let as: o.AuthorizationServer | undefined\n\n  // Falls back to authjs.dev if the user only passed params\n  if (!url || url.host === \"authjs.dev\") {\n    // If url is undefined, we assume that issuer is always defined\n    // We check this in assert.ts\n\n    const issuer = new URL(provider.issuer!)\n    const discoveryResponse = await o.discoveryRequest(issuer, {\n      [o.customFetch]: provider[customFetch],\n      // TODO: move away from allowing insecure HTTP requests\n      [o.allowInsecureRequests]: true,\n    })\n    const as = await o\n      .processDiscoveryResponse(issuer, discoveryResponse)\n      .catch((error) => {\n        if (!(error instanceof TypeError) || error.message !== \"Invalid URL\")\n          throw error\n        throw new TypeError(\n          `Discovery request responded with an invalid issuer. expected: ${issuer}`\n        )\n      })\n\n    if (!as.authorization_endpoint) {\n      throw new TypeError(\n        \"Authorization server did not provide an authorization endpoint.\"\n      )\n    }\n\n    url = new URL(as.authorization_endpoint)\n  }\n\n  const authParams = url.searchParams\n\n  let redirect_uri: string = provider.callbackUrl\n  let data: string | undefined\n  if (!options.isOnRedirectProxy && provider.redirectProxyUrl) {","sourceCodeStart":20,"sourceCodeEnd":56,"githubUrl":"https://github.com/nextauthjs/next-auth/blob/a1a16a5a7780488c7449feece410033f445d0b31/packages/core/src/lib/actions/signin/authorization-url.ts#L20-L56","documentation":"During OIDC discovery, the openid-client library validates that the issuer returned in the discovery document exactly matches the configured issuer URL. Auth.js catches the 'Invalid URL' TypeError from that validation and rethrows it with the expected issuer, meaning the provider's well-known endpoint returned a different issuer value.","triggerScenarios":"Configuring provider.issuer with a URL whose scheme/host/port/path differs (even a trailing slash) from the 'issuer' claim in the provider's /.well-known/openid-configuration response; discovery document served from a mirror or proxy that rewrites the issuer.","commonSituations":"Issuer mismatch like http vs https, missing/extra trailing slash, localhost vs 127.0.0.1; providers behind API gateways that return an internal URL as issuer; Keycloak realms where the realm name in the issuer differs from the discovery URL; Azure AD vs Azure AD Graph issuer casing (login.microsoftonline.com vs sts.windows.net).","solutions":["Compare the 'issuer' field in {provider.issuer}/.well-known/openid-configuration with your configured issuer and make them byte-identical (scheme, host, port, path, no trailing slash)","Pin wellKnown/authorization/token endpoints manually (skip discovery) if the provider cannot return a matching issuer: set authorization: { url }, token, userinfo, jwks_endpoint directly","Check for proxy/gateway rewriting and use the issuer exactly as the provider advertises it","For Azure AD, use https://login.microsoftonline.com/{tenant}/v2.0 as issuer (matching the discovery document), not the sts.windows.net legacy issuer"],"exampleFix":"// before\nissuer: \"https://accounts.example.com\", // discovery returns https://accounts.example.com/oidc\n// after\nissuer: \"https://accounts.example.com/oidc\"; // matches the issuer claim exactly","handlingStrategy":"validation","validationCode":"const doc = await fetch(`${issuer}/.well-known/openid-configuration`).then(r => r.json());\nif (doc.issuer !== issuer) throw new Error(`Issuer mismatch: configured ${issuer}, provider returns ${doc.issuer}`);","typeGuard":"function matchesIssuer(doc: { issuer?: string }, issuer: string): doc is { issuer: string } {\n  return doc.issuer === issuer;\n}","tryCatchPattern":"try {\n  await signIn('oidc');\n} catch (e) {\n  if (/invalid issuer/.test(String(e))) {\n    // compare discovery doc issuer with configured issuer; adjust issuer or set endpoints manually\n  }\n}","preventionTips":["Copy the issuer verbatim from the provider's well-known document (watch trailing slashes, http vs https)","For Azure AD use https://login.microsoftonline.com/{tenant}/v2.0, not legacy sts.windows.net","Pin explicit endpoints (authorization/token/userinfo) when a proxy rewrites the issuer","Test discovery with curl during setup before wiring the provider"],"tags":["oauth","oidc","discovery","configuration"],"backgroundTag":"oidc-issuer-mismatch","analyzedSha":"a1a16a5a7780488c7449feece410033f445d0b31","analyzedAt":"2026-08-28T21:52:38.200Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}