Authentication

Custom

Wire any OAuth authorization server with custom() or jwtOAuthProvider().

Use a custom provider when your identity provider is not covered by a built-in plugin, you need token introspection instead of JWT verification, or claim mapping differs from the defaults.

Choose an integration style

  • JWT providers — use jwtOAuthProvider() with issuer, JWKS, and mapUser
  • Non-standard verification — use custom() with createTokenVerifier

Set environment variables

Tokens must target your MCP server. Set MCP_URL to your public MCP endpoint. See Installation → Environment variables.

Configure bitmcp

JWT identity providers:

bitmcp.config.ts
import { defineConfig } from "bitmcp";
import { jwtOAuthProvider } from "bitmcp/oauth";

export default defineConfig({
  auth: jwtOAuthProvider({
    name: "my-provider",
    resolveIssuer: () => "https://auth.example.com",
    jwksUrl: (issuer) => new URL(`${issuer}/.well-known/jwks.json`),
    oauthMetadata: (issuer) => ({
      issuer,
      authorization_endpoint: `${issuer}/oauth/authorize`,
      token_endpoint: `${issuer}/oauth/token`,
      registration_endpoint: `${issuer}/oauth/register`,
      response_types_supported: ["code"],
      grant_types_supported: ["authorization_code", "refresh_token"],
      code_challenge_methods_supported: ["S256"],
    }),
    mapUser: (payload) => ({
      id: String(payload.sub),
      email: typeof payload.email === "string" ? payload.email : undefined,
    }),
    audience: "https://mcp.example.com/mcp",
  }),
});

Full control with custom():

bitmcp.config.ts
import { custom, createJwtVerifier } from "bitmcp/oauth";

auth: custom({
  createTokenVerifier: (resource) =>
    createJwtVerifier({
      issuer: "https://auth.example.com",
      jwksUrl: new URL("https://auth.example.com/.well-known/jwks.json"),
      resource,
      audience: resource.href,
    }),
  oauthMetadata: {
    issuer: "https://auth.example.com",
    authorization_endpoint: "https://auth.example.com/oauth/authorize",
    token_endpoint: "https://auth.example.com/oauth/token",
    response_types_supported: ["code"],
  },
  mapUser: (authInfo) => ({
    id: String(authInfo.extra?.payload?.sub ?? authInfo.clientId),
  }),
});

Verify

  1. Start the server with pnpm dev or pnpm start
  2. Connect from an OAuth-capable MCP client
  3. Complete login through your provider
  4. Call a tool and confirm ctx.user is populated
  5. Call /mcp without a token and confirm 401

OAuth requires HTTP. app.stdio() throws when auth is set.

Options

custom(options: {
  createTokenVerifier: (resource: URL) => OAuthTokenVerifier;
  oauthMetadata: OAuthMetadata;
  mapUser: (authInfo: AuthInfo) => BitmcpUser;
  resource?: URL | string;
  requiredScopes?: string[];
  scopesSupported?: string[];
  resourceName?: string;
})

Options passed to a factory override environment variables. Shared vars: Installation → Environment variables.