Skip to content

OAuthCallbackError ​

Thrown by getAuthCode() when the authorization server redirects back with an error instead of a code (RFC 6749 §4.1.2.1).

Definition ​

ts
class OAuthCallbackError extends Error {
  readonly name: "OAuthCallbackError";
  readonly error: string; // e.g. "access_denied"
  readonly description?: string; // error_description, untrusted provider text
  readonly uri?: string; // error_uri, untrusted provider text
  readonly params: URLSearchParams; // full callback query
}

message is Authorization failed: "<error>", with the code JSON-escaped. Provider text (description, uri) never goes into message, so logging the error can't inject lines or control characters; show it deliberately if you trust the provider.

Usage ​

ts
import { getAuthCode, OAuthCallbackError } from "oauth-callback";

try {
  const { code } = await getAuthCode(build);
} catch (error) {
  if (error instanceof OAuthCallbackError) {
    if (error.error === "access_denied") {
      console.log("Sign-in cancelled.");
    } else {
      console.error(error.message); // escaped code, no provider text
    }
  } else {
    throw error;
  }
}

The callback is only accepted when its state matches the flow, so the error comes from the redirect you started. Still, treat description and uri as untrusted: don't render them as HTML and don't follow uri automatically. If your provider supports RFC 9207, check error.params.get("iss") against the expected issuer before trusting the error details.

Common error codes ​

errorMeaning
access_deniedThe user or server denied the request
invalid_requestMissing, invalid or duplicated parameter
unauthorized_clientThe client may not use this grant
unsupported_response_typeThe server doesn't support response_type=code
invalid_scopeUnknown or malformed scope
server_errorUnexpected server condition
temporarily_unavailableServer overloaded or down for maintenance

OpenID Connect adds codes such as login_required, consent_required and interaction_required.

Other failures ​

getAuthCode() signals everything else with standard errors:

FailureError
TimeoutDOMException with name === "TimeoutError"
AbortYour signal's reason
Invalid inputTypeError or RangeError, before the browser opens
Launcher failureWhatever your launch threw or rejected with
Bind failureThe listener error, e.g. EADDRINUSE
ts
try {
  await getAuthCode(build, { signal });
} catch (error) {
  if (error instanceof OAuthCallbackError) {
    // provider error
  } else if (error instanceof DOMException && error.name === "TimeoutError") {
    // no callback within `timeout`
  } else if (signal.aborted) {
    // cancelled: error === signal.reason
  } else {
    throw error;
  }
}

MCP ​

browserAuth() doesn't throw OAuthCallbackError. It passes the callback to the MCP SDK, which checks iss and reports errors with its own error types (e.g. the SDK's OAuthError).