> ## Documentation Index
> Fetch the complete documentation index at: https://docs.mcp-use.com/llms.txt
> Use this file to discover all available pages before exploring further.

# OAuth Proxy

> Bridge providers that don't support Dynamic Client Registration  -  Google, GitHub, Okta, Azure AD, and others  -  to your MCP server

Use `oauthProxy` when your identity provider doesn't support Dynamic Client Registration (DCR). You register a single application in the provider's dashboard, hand the `clientId` / `clientSecret` to the MCP server, and the server mediates the OAuth flow so every MCP client can authenticate without registering itself upstream.

<Note>
  If your provider supports DCR (Auth0, Better Auth, Keycloak, WorkOS, Supabase, or any provider that advertises a `registration_endpoint`), prefer a [remote auth provider](/typescript/server/authentication/index#remote-auth-built-in) instead  -  clients register directly with the upstream and the server only verifies tokens.
</Note>

## When to use OAuth Proxy

Reach for `oauthProxy` when all of the following are true:

* The provider requires you to register an app in a dashboard and returns a fixed `clientId` / `clientSecret`.
* The provider does **not** expose a `registration_endpoint` in its OAuth metadata.
* You're fine holding the client secret on the server and mediating token exchange.

Common proxy targets: **Google, GitHub, Okta, Azure AD (Microsoft Entra ID), Auth0 Regular Web Apps** (non-DCR), and most enterprise SSO deployments.

## How it works

1. Your server exposes a `/register` endpoint that returns the configured `clientId`.
2. The MCP client runs PKCE authorization against the upstream using that `clientId`.
3. At token exchange, your server injects the `clientId` and `clientSecret` before forwarding to the upstream.
4. On each `/mcp/*` request, your server verifies the bearer token via the `verifyToken` function you provided.

Your server holds the client credentials and mediates every token exchange. Tokens are passed through  -  the proxy does not mint its own.

## Quick start (Google)

```typescript theme={null}
import { MCPServer, oauthProxy, jwksVerifier } from "mcp-use/server";

const server = new MCPServer({
  name: "my-server",
  version: "1.0.0",
  oauth: oauthProxy({
    authEndpoint: "https://accounts.google.com/o/oauth2/v2/auth",
    tokenEndpoint: "https://oauth2.googleapis.com/token",
    issuer: "https://accounts.google.com",
    clientId: process.env.GOOGLE_CLIENT_ID!,
    clientSecret: process.env.GOOGLE_CLIENT_SECRET!,
    scopes: ["openid", "email", "profile"],
    extraAuthorizeParams: { access_type: "offline" },
    verifyToken: jwksVerifier({
      jwksUrl: "https://www.googleapis.com/oauth2/v3/certs",
      issuer: "https://accounts.google.com",
      audience: process.env.GOOGLE_CLIENT_ID!,
    }),
  }),
});

await server.listen(3000);
```

## Configuration options

```typescript theme={null}
oauthProxy({
  // Required: upstream OAuth endpoints
  authEndpoint: "https://accounts.google.com/o/oauth2/v2/auth",
  tokenEndpoint: "https://oauth2.googleapis.com/token",
  issuer: "https://accounts.google.com",

  // Required: pre-registered client credentials
  clientId: process.env.CLIENT_ID!,
  clientSecret: process.env.CLIENT_SECRET, // optional for public clients

  // Required: token verifier  -  resolves to { payload } or throws
  verifyToken: jwksVerifier({
    jwksUrl: "https://www.googleapis.com/oauth2/v3/certs",
    issuer: "https://accounts.google.com",
    audience: process.env.CLIENT_ID!,
  }),

  // Optional: scopes to request (default: ["openid", "email", "profile"])
  scopes: ["openid", "email", "profile"],

  // Optional: grant types (default: ["authorization_code", "refresh_token"])
  grantTypes: ["authorization_code", "refresh_token"],

  // Optional: extra authorize-request params (e.g. audience, access_type, prompt)
  extraAuthorizeParams: { access_type: "offline", prompt: "consent" },

  // Optional: custom user info extractor
  // Default pulls sub/email/name/picture and parses `scope` into scopes[].
  getUserInfo(payload) {
    return {
      userId: payload.sub as string,
      email: payload.email as string | undefined,
      name: payload.name as string | undefined,
    };
  },
});
```

### `jwksVerifier` helper

For standard JWT + JWKS providers, `jwksVerifier` handles signature verification, issuer checking, and optional audience validation against a remote JWKS endpoint:

```typescript theme={null}
verifyToken: jwksVerifier({
  jwksUrl: "https://www.googleapis.com/oauth2/v3/certs",
  issuer: "https://accounts.google.com",
  audience: process.env.GOOGLE_CLIENT_ID!, // optional
});
```

For non-JWT providers (e.g. GitHub opaque tokens), write a custom `verifyToken` function  -  see the [GitHub example](#github-opaque-tokens) below.

<Note>
  Any `verifyToken` function  -  including the one returned by `jwksVerifier`  -  **must resolve to `{ payload: Record<string, unknown> }`** or throw on an invalid token. The proxy surfaces `payload` to `getUserInfo` and to `ctx.auth`.
</Note>

## Provider examples

### Google

```typescript theme={null}
oauth: oauthProxy({
  authEndpoint: "https://accounts.google.com/o/oauth2/v2/auth",
  tokenEndpoint: "https://oauth2.googleapis.com/token",
  issuer: "https://accounts.google.com",
  clientId: process.env.GOOGLE_CLIENT_ID!,
  clientSecret: process.env.GOOGLE_CLIENT_SECRET!,
  scopes: ["openid", "email", "profile"],
  extraAuthorizeParams: { access_type: "offline" },
  verifyToken: jwksVerifier({
    jwksUrl: "https://www.googleapis.com/oauth2/v3/certs",
    issuer: "https://accounts.google.com",
    audience: process.env.GOOGLE_CLIENT_ID!,
  }),
});
```

### Okta

```typescript theme={null}
const oktaDomain = process.env.OKTA_DOMAIN!;

oauth: oauthProxy({
  authEndpoint: `${oktaDomain}/oauth2/default/v1/authorize`,
  tokenEndpoint: `${oktaDomain}/oauth2/default/v1/token`,
  issuer: `${oktaDomain}/oauth2/default`,
  clientId: process.env.OKTA_CLIENT_ID!,
  clientSecret: process.env.OKTA_CLIENT_SECRET,
  scopes: ["openid", "email", "profile"],
  verifyToken: jwksVerifier({
    jwksUrl: `${oktaDomain}/oauth2/default/v1/keys`,
    issuer: `${oktaDomain}/oauth2/default`,
  }),
});
```

### Azure AD (Microsoft Entra ID)

```typescript theme={null}
const tenantId = process.env.AZURE_TENANT_ID!;
const base = `https://login.microsoftonline.com/${tenantId}/v2.0`;

oauth: oauthProxy({
  authEndpoint: `${base}/oauth2/v2.0/authorize`,
  tokenEndpoint: `${base}/oauth2/v2.0/token`,
  issuer: base,
  clientId: process.env.AZURE_CLIENT_ID!,
  clientSecret: process.env.AZURE_CLIENT_SECRET,
  scopes: ["openid", "profile", "email"],
  verifyToken: jwksVerifier({
    jwksUrl: "https://login.microsoftonline.com/common/discovery/v2.0/keys",
    issuer: base,
    audience: process.env.AZURE_CLIENT_ID!,
  }),
});
```

### Auth0 Regular Web App (non-DCR)

Use the proxy when your Auth0 tenant doesn't have the Early Access DCR feature enabled:

```typescript theme={null}
const domain = process.env.AUTH0_DOMAIN!;
const audience = process.env.AUTH0_AUDIENCE!;

oauth: oauthProxy({
  authEndpoint: `https://${domain}/authorize`,
  tokenEndpoint: `https://${domain}/oauth/token`,
  issuer: `https://${domain}/`,
  clientId: process.env.AUTH0_CLIENT_ID!,
  clientSecret: process.env.AUTH0_CLIENT_SECRET,
  scopes: ["openid", "email", "profile"],
  extraAuthorizeParams: { audience },
  verifyToken: jwksVerifier({
    jwksUrl: `https://${domain}/.well-known/jwks.json`,
    issuer: `https://${domain}/`,
    audience,
  }),
});
```

See the [runnable `auth0-proxy` example](https://github.com/mcp-use/mcp-use/tree/main/libraries/typescript/packages/mcp-use/examples/server/oauth/auth0-proxy) for a full working setup.

### GitHub (opaque tokens)

GitHub issues non-JWT opaque tokens, so `jwksVerifier` doesn't apply. Write a custom `verifyToken` that validates by calling the GitHub API:

```typescript theme={null}
oauth: oauthProxy({
  authEndpoint: "https://github.com/login/oauth/authorize",
  tokenEndpoint: "https://github.com/login/oauth/access_token",
  issuer: "https://github.com",
  clientId: process.env.GITHUB_CLIENT_ID!,
  clientSecret: process.env.GITHUB_CLIENT_SECRET!,
  scopes: ["read:user", "user:email"],

  async verifyToken(token) {
    const res = await fetch("https://api.github.com/user", {
      headers: {
        Authorization: `Bearer ${token}`,
        "User-Agent": "my-mcp-server",
      },
    });
    if (!res.ok) throw new Error("Invalid GitHub token");
    const user = await res.json();
    return { payload: { sub: String(user.id), ...user } };
  },

  getUserInfo(payload) {
    return {
      userId: payload.sub as string,
      username: payload.login as string | undefined,
      name: payload.name as string | undefined,
      email: payload.email as string | undefined,
      picture: payload.avatar_url as string | undefined,
    };
  },
});
```

## Accessing user info in tools

```typescript theme={null}
server.tool(
  {
    name: "get-user-info",
    description: "Get authenticated user info",
  },
  async (_args, ctx) => ({
    userId: ctx.auth.user.userId,
    email: ctx.auth.user.email,
    name: ctx.auth.user.name,
    scopes: ctx.auth.scopes,
  }),
);
```

The upstream access token is available as `ctx.auth.accessToken`  -  use it to call provider APIs on behalf of the user:

```typescript theme={null}
server.tool(
  {
    name: "get-google-profile",
    description: "Fetch the full profile from Google's userinfo endpoint",
  },
  async (_args, ctx) => {
    const res = await fetch("https://openidconnect.googleapis.com/v1/userinfo", {
      headers: { Authorization: `Bearer ${ctx.auth.accessToken}` },
    });
    return { content: [{ type: "text", text: JSON.stringify(await res.json()) }] };
  },
);
```

## Resources

* [Runnable `auth0-proxy` example](https://github.com/mcp-use/mcp-use/tree/main/libraries/typescript/packages/mcp-use/examples/server/oauth/auth0-proxy)
* [jose library](https://github.com/panva/jose)  -  JWT / JWKS verification
* [OAuth 2.1 Specification](https://oauth.net/2.1/)
* [PKCE (RFC 7636)](https://datatracker.ietf.org/doc/html/rfc7636)

## Next Steps

* [Custom Provider (DCR)](/typescript/server/authentication/providers/custom)  -  Use a provider that supports Dynamic Client Registration
* [User Context](/typescript/server/authentication/user-context)  -  Access user information in tools
