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

# OAuth authentication

> Configure OAuth authentication for your MCP servers on Alpic

To add OAuth authentication to your MCP server, you need to have an Identity Provider (IdP) that manages your users identities.

Depending on your IdP, you can be in one of the 4 following cases:

<CardGroup cols={2}>
  <Card title="Your IdP supports OAuth 2.1 + DCR" href="#using-an-oauth-2-1-compatible-identity-providers-idp">
    Your IdP is OAuth 2.1 compatible and implements Dynamic Client Registration (DCR)
  </Card>

  <Card title="Your IdP supports OAuth 2.0 but no DCR" href="#using-an-oauth-2-0-compatible-identity-provider-idp-with-no-dcr">
    Your IdP is OAuth 2.0 compatible but doesn't implement DCR
  </Card>

  <Card title="Your IdP doesn't support OAuth metadata" href="#using-and-idp-without-oauth-support">
    Your IdP doesn't provide OAuth metadata endpoints
  </Card>

  <Card title="No IdP" href="#building-from-scratch">
    You don't have an IdP yet as you are starting from scratch
  </Card>
</CardGroup>

### Using an OAuth 2.1 compatible Identity Providers (IdP)

If you're already using an OAuth 2.1 identity provider with Dynamic Client Registration (DCR), you simply need to configure your MCP server to advertise your IdP through OAuth metadata endpoints.

The easiest way to do so is to rely on existing SDK helpers to provide such configuration on your server:

<CodeGroup>
  ```ts MCP Typescript SDK theme={null}
  import { requireBearerAuth } from "@modelcontextprotocol/sdk/server/auth/middleware/bearerAuth.js";
  import { mcpAuthMetadataRouter } from "@modelcontextprotocol/sdk/server/auth/router.js";

  //...

  app.use(
    mcpAuthMetadataRouter({
      oauthMetadata: {
        authorization_endpoint: "https://my-idp.com/oauth2/authorize",
        token_endpoint: "https://my-idp.com/oauth2/token",
        registration_endpoint: "https://my-idp.com/oauth2/register",
        response_types_supported: ["code"],
        code_challenge_methods_supported: ["S256"],
        token_endpoint_auth_methods_supported: ["client_secret_post"],
        issuer: "https://my-idp.com",
      },
      resourceServerUrl: new URL("http://localhost:3000"),
    }),
  );

  const authMiddleware = requireBearerAuth({
    verifier: {
      verifyAccessToken: async (token) => {
        // Generic jwtDecode for JWT auth token or any IDP custom verification method

        return { token, clientId: "clientId", scopes: [], expiresAt: Date.now() / 1000 + 600 };
      },
    },
  });

  app.post("/mcp", authMiddleware, async (req: Request, res: Response) => {});
  app.get("/mcp", authMiddleware, async (req: Request, res: Response) => {});
  app.delete("/mcp", authMiddleware, async (req: Request, res: Response) => {});

  app.listen(3000);
  ```

  ```python FastMCP theme={null}
  from typing import Any
  from fastmcp.server.auth import AccessToken, RemoteAuthProvider, TokenVerifier
  from mcp.server.auth.handlers.metadata import MetadataHandler
  from mcp.server.auth.routes import cors_middleware
  from mcp.shared.auth import OAuthMetadata
  from starlette.routing import Route
  from pydantic import AnyHttpUrl

  class CustomTokenVerifier(TokenVerifier):
      """Token verifier for the custom auth provider."""

      async def verify_token(self, token: str) -> AccessToken | None:
          """Generic jwtDecode for JWT auth token or any IDP custom verification method"""
          return AccessToken(token=token, client_id="clientId", scopes=[])

  class CustomAuthProvider(RemoteAuthProvider):
      """Authentication provider for MCP servers that act both as an authorization server and a resource server.
      """

      base_url: AnyHttpUrl

      def __init__(
          self,
          base_url: AnyHttpUrl | str,
      ):
          """Initialize the custom auth provider.

          Args:
              token_verifier: TokenVerifier instance for token validation
              base_url: The base URL of this server
          """
          super().__init__(
              token_verifier=CustomTokenVerifier(),
              base_url=base_url,
              authorization_servers=[base_url],
          )

      def get_routes(self, mcp_path: str | None = None, mcp_endpoint: Any | None = None) -> list[Route]:
          routes = super().get_routes(mcp_path, mcp_endpoint)

          routes.append(Route(
              "/.well-known/oauth-authorization-server",
              endpoint=cors_middleware(
                  MetadataHandler(
                      OAuthMetadata(
                          issuer=AnyHttpUrl("http://localhost:8000"),
                          authorization_endpoint=AnyHttpUrl("https://my-idp.com/oauth2/authorize"),
                          token_endpoint=AnyHttpUrl("https://my-idp.com/oauth2/token"),
                          registration_endpoint=AnyHttpUrl("https://my-idp.com/oauth2/register"),
                          response_types_supported=["code"],
                          code_challenge_methods_supported=["S256"],
                          token_endpoint_auth_methods_supported=["client_secret_post"],
                      )
                  ).handle,
                  ["GET", "OPTIONS"],
              ),
              methods=["GET", "OPTIONS"],
          ))

          return routes


  auth = CustomAuthProvider(base_url="http://localhost:8000")
  mcp = FastMCP("My MCP Server", auth=auth)
  ```
</CodeGroup>

<Info>
  Configuring your MCP server to use an existing Identity Provider (IdP) with OAuth requires knowledge of a the
  different oauth endpoint URLs. We have curated a list of endpoints for the most used IdPs
  [here](/secure/auth/oauth-providers).
</Info>

After deploying a new version of your MCP server on Alpic with such a configuration, you should see your server as **Protected** in the **Settings** tab of your project page. This will confirm that your MCP server is protected by OAuth.

<Tip>
  We made Alpic to be fully compatible with your local server code. If your authentication code runs locally, it should
  work on Alpic. In the example above, you should keep the localhost URL for the resource server when your deploy to
  Alpic.
</Tip>

<Info>
  If you want to use your deployed MCP server URL as the OAuth server instead of `localhost`, the `ALPIC_HOST`
  environment variable was conceived for this use case. It's automatically set at both build and runtime, allowing Alpic
  to treat your deployed host as an internal host for OAuth metadata discovery. Learn more about [system environment
  variables](/build-deploy/env-variables).
</Info>

### Using an OAuth 2.0 compatible Identity Provider (IdP) with no DCR

If you're not using an OAuth 2.1 IdP with DCR, you can rely on [**Alpic's Dynamic Client Registration proxy**](./dcr-proxy) to handle the complexity associated with multiple OAuth clients registering to use your IdP.

In order to leverage Alpic's DCR proxy feature, you should configure your MCP server to advertise your IdP through OAuth metadata endpoints without mentioning a `registration_endpoint` property.

The easiest way to do so is to rely on existing SDK helpers to provide such configuration on your server:

<CodeGroup>
  ```ts MCP Typescript SDK theme={null}
  import { requireBearerAuth } from "@modelcontextprotocol/sdk/server/auth/middleware/bearerAuth.js";
  import { mcpAuthMetadataRouter } from "@modelcontextprotocol/sdk/server/auth/router.js";

  //...

  app.use(
    mcpAuthMetadataRouter({
      oauthMetadata: {
        authorization_endpoint: "https://my-idp.com/oauth2/authorize",
        token_endpoint: "https://my-idp.com/oauth2/token",
        response_types_supported: ["code"],
        code_challenge_methods_supported: ["S256"],
        token_endpoint_auth_methods_supported: ["client_secret_post"],
        issuer: "https://my-idp.com",
      },
      resourceServerUrl: new URL("http://localhost:3000"),
    }),
  );

  const authMiddleware = requireBearerAuth({
    verifier: {
      verifyAccessToken: async (token) => {
        // Generic jwtDecode for JWT auth token or any IDP custom verification method

        return { token, clientId: "clientId", scopes: [], expiresAt: Date.now() / 1000 + 600 };
      },
    },
  });

  app.post("/mcp", authMiddleware, async (req: Request, res: Response) => {});
  app.get("/mcp", authMiddleware, async (req: Request, res: Response) => {});
  app.delete("/mcp", authMiddleware, async (req: Request, res: Response) => {});

  app.listen(3000);
  ```

  ```python FastMCP theme={null}
  from typing import Any
  from fastmcp.server.auth import AccessToken, RemoteAuthProvider, TokenVerifier
  from mcp.server.auth.handlers.metadata import MetadataHandler
  from mcp.server.auth.routes import cors_middleware
  from mcp.shared.auth import OAuthMetadata
  from starlette.routing import Route
  from pydantic import AnyHttpUrl

  class CustomTokenVerifier(TokenVerifier):
      """Token verifier for the custom auth provider."""

      async def verify_token(self, token: str) -> AccessToken | None:
          """Generic jwtDecode for JWT auth token or any IDP custom verification method"""
          return AccessToken(token=token, client_id="clientId", scopes=[])

  class CustomAuthProvider(RemoteAuthProvider):
      """Authentication provider for MCP servers that act both as an authorization server and a resource server.
      """

      base_url: AnyHttpUrl

      def __init__(
          self,
          base_url: AnyHttpUrl | str,
      ):
          """Initialize the custom auth provider.

          Args:
              token_verifier: TokenVerifier instance for token validation
              base_url: The base URL of this server
          """
          super().__init__(
              token_verifier=CustomTokenVerifier(),
              base_url=base_url,
              authorization_servers=[base_url],
          )

      def get_routes(self, mcp_path: str | None = None, mcp_endpoint: Any | None = None) -> list[Route]:
          routes = super().get_routes(mcp_path, mcp_endpoint)

          routes.append(Route(
              "/.well-known/oauth-authorization-server",
              endpoint=cors_middleware(
                  MetadataHandler(
                      OAuthMetadata(
                          issuer=AnyHttpUrl("http://localhost:8000"),
                          authorization_endpoint=AnyHttpUrl("https://my-idp.com/oauth2/authorize"),
                          token_endpoint=AnyHttpUrl("https://my-idp.com/oauth2/token"),
                          response_types_supported=["code"],
                          code_challenge_methods_supported=["S256"],
                          token_endpoint_auth_methods_supported=["client_secret_post"],
                      )
                  ).handle,
                  ["GET", "OPTIONS"],
              ),
              methods=["GET", "OPTIONS"],
          ))

          return routes

  auth = CustomAuthProvider(base_url="http://localhost:8000")
  mcp = FastMCP("My MCP Server", auth=auth)
  ```
</CodeGroup>

After deploying a new version of your MCP server on Alpic with such a configuration, check that your server is detected as **Protected** in the **Settings** tab of your project page.

If that's the case, you will see an option to **activate Alpic DCR**.

In order to activate DCR, you should create a single OAuth client on your IdP. This client will be the one proxied by Alpic for every request users make to identify on your MCP server. Make sure to choose a generic enough name in case it's displayed during the authentication process.

The callback URL to use for this client is specified by Alpic when activating DCR.
Please fill-in the activation form with newly created OAuth **client ID**, **client secret** and **scopes** to complete the DCR proxy setup.

### Using and IdP without OAuth support

In the case you have an internal identity management system, or are using an identity management system that doesn't support Oauth, you can use providers that take care of Oauth and DCR flow for you while keeping your identity system. Here are some providers supporting the MCP use-case:

* [Stytch Standalone Apps](https://stytch.com/docs/guides/connected-apps/overview)
* [WorkOS Connect](https://workos.com/docs/authkit/connect/standalone)

Once you have configured your MCP Oauth 2.1 workflow with that provider, you can use the [examples above](#using-an-oauth-21-compatible-identity-providers-idp) to integrate it into your MCP server.

### Building from scratch

If you don't have any identity system, you can build yours, or rely on one of the many Identiy Managment Systems out there.

You can check our OAuth [providers list](/secure/auth/oauth-providers#identity-management-platforms) to see which of them is supporting OAuth 2.1 with DCR or not.
