> ## 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.

# User Context

> Use verified OAuth identity inside MCP server callbacks.

On a server configured with `oauth`, tool, resource, and prompt callbacks receive verified authentication data on `ctx.auth`. The provider determines the static type of `ctx.auth.user`; there is no generic `UserInfo` shape.

## Auth shape

Every provider exposes this top-level shape:

```typescript theme={null}
type OAuthAuth<TUser> = {
  user: TUser;
  payload: Record<string, unknown>;
  accessToken: string;
  scopes: string[];
  permissions: string[];
  clientId?: string;
  expiresAt: number;
  resource?: URL;
};
```

* `user` is the provider's normalized user type.
* `payload` contains the verified token claims or introspection data.
* `accessToken` is the verified bearer token. Forward it only when calling an upstream API on behalf of the user.
* `scopes` are copied from the auth information returned by the token verifier.
* `permissions` are produced by the provider's mapping.
* `clientId` is present when the token has a client identifier.
* `expiresAt` is a Unix timestamp in seconds.
* `resource` is the protected resource validated by the verifier.

## Use provider-specific fields

All built-in users have `id`. Optional profile and organization fields vary:

| Provider | Selected normalized fields                                                                                      |
| -------- | --------------------------------------------------------------------------------------------------------------- |
| Auth0    | `id`, `email`, `name`, `nickname`, `picture`, `emailVerified`, `updatedAt`, `roles`                             |
| Clerk    | `id`, `email`, `name`, `username`, `organizationId`, `organizationRole`, `organizationSlug`, `roles`            |
| Keycloak | `id`, `email`, `name`, `preferredUsername`, `givenName`, `familyName`, `roles`, `realmAccess`, `resourceAccess` |
| Supabase | `id`, `email`, `name`, `fullName`, `username`, `avatarUrl`, `role`, `aal`, `amr`, `sessionId`                   |
| WorkOS   | `id`, `email`, `name`, `preferredUsername`, `firstName`, `lastName`, `roles`, `organizationId`, `sessionId`     |

```typescript theme={null}
server.tool(
  {
    name: "list_documents",
    description: "List documents for the caller's Clerk organization.",
  },
  async (_args, ctx) => {
    const organizationId = ctx.auth.user.organizationId;
    if (!organizationId) {
      return {
        isError: true,
        content: [{ type: "text", text: "Organization context required" }],
      };
    }

    const documents = await db.documents.findMany({
      where: { organizationId },
    });
    return {
      content: [{ type: "text", text: JSON.stringify(documents) }],
    };
  },
);
```

Use normalized fields for application logic. Read `payload` only when you deliberately need a provider claim that is not mapped.

## Check scopes and permissions

Scopes describe OAuth grants. Permissions describe provider-mapped application authorization.

```typescript theme={null}
import { z } from "zod";

server.tool(
  {
    name: "delete_document",
    description: "Delete a document when the caller has permission.",
    inputSchema: z.object({ documentId: z.string() }),
  },
  async ({ documentId }, ctx) => {
    if (!ctx.auth.permissions.includes("documents:delete")) {
      return {
        isError: true,
        content: [
          {
            type: "text",
            text: "Forbidden: documents:delete permission required",
          },
        ],
      };
    }

    await db.documents.delete({ id: documentId });
    return { content: [{ type: "text", text: "Document deleted" }] };
  },
);
```

Provider mappings differ: Auth0 and WorkOS map their permission claim, Clerk maps organization permissions, Keycloak flattens resource roles as `client:role`, and Supabase maps AAL to `aal:<level>`.

## Map custom claims

For another authorization server, `oauthCustomProvider` separates verification from application mapping:

```typescript theme={null}
import { MCPServer } from "mcp-use";
import {
  oauthCustomProvider,
  type OAuthAuthInfo,
  type OAuthMetadata,
} from "mcp-use/oauth";

type AppUser = {
  id: string;
  email?: string;
  organizationId?: string;
};

const demoAccessToken = process.env.DEMO_ACCESS_TOKEN;
if (!demoAccessToken) throw new Error("DEMO_ACCESS_TOKEN is required");

const oauthMetadata = {
  issuer: "https://auth.example.com",
  authorization_endpoint: "https://auth.example.com/oauth/authorize",
  token_endpoint: "https://auth.example.com/oauth/token",
  registration_endpoint: "https://auth.example.com/oauth/register",
  response_types_supported: ["code"],
  code_challenge_methods_supported: ["S256"],
} satisfies OAuthMetadata;

const server = new MCPServer({
  name: "custom-auth-server",
  version: "1.0.0",
  oauth: oauthCustomProvider<AppUser>({
    oauthMetadata,
    createTokenVerifier: (resource) => ({
      async verifyAccessToken(token) {
        if (token !== demoAccessToken) {
          throw new Error("Invalid access token");
        }

        return {
          token,
          clientId: "demo-client",
          scopes: ["profile"],
          expiresAt: Math.floor(Date.now() / 1000) + 3600,
          resource,
          extra: {
            payload: {
              sub: "user-123",
              email: "user@example.com",
              organization_id: "org-123",
              permissions: ["documents:read"],
            },
          },
        };
      },
    }),
    mapAuthInfo(authInfo: OAuthAuthInfo) {
      const payload = authInfo.extra?.payload as Record<string, unknown>;
      return {
        user: {
          id: String(payload.sub),
          email: typeof payload.email === "string" ? payload.email : undefined,
          organizationId:
            typeof payload.organization_id === "string"
              ? payload.organization_id
              : undefined,
        },
        payload,
        permissions: Array.isArray(payload.permissions)
          ? payload.permissions.filter(
              (value): value is string => typeof value === "string",
            )
          : [],
      };
    },
  }),
});
```

This fixed-token verifier keeps the example small and is only suitable for local testing. In production, validate the token signature, issuer, expiry, and MCP resource, or introspect an opaque token with the authorization server.

`createTokenVerifier` receives the canonical MCP resource. `mapAuthInfo` runs only after SDK auth information has been verified and must return `{ user, payload, permissions }`. It does not rewrite `ctx.auth.scopes`; those come from the verifier's verified auth info.

## Next steps

<CardGroup cols={2}>
  <Card title="Authentication" icon="shield-check" href="/v2/typescript/server/authentication/index">
    Choose and configure an OAuth provider.
  </Card>

  <Card title="Custom Provider" icon="code" href="/v2/typescript/server/authentication/providers/custom">
    Implement resource-bound token verification and claim mapping.
  </Card>

  <Card title="Middleware" icon="layers" href="/v2/typescript/server/middleware">
    Apply shared authorization checks.
  </Card>
</CardGroup>
