diff --git a/docs/integrations/supabase.mdx b/docs/integrations/supabase.mdx
index cbaa48920f..b98c9cb7d1 100644
--- a/docs/integrations/supabase.mdx
+++ b/docs/integrations/supabase.mdx
@@ -17,7 +17,7 @@ This guide shows you how to secure your FastMCP server using **Supabase Auth**.
### Prerequisites
Before you begin, you will need:
-1. A **[Supabase Account](https://supabase.com/)** with a project
+1. A **[Supabase Account](https://supabase.com/)** with a project or a self-hosted **Supabase Auth** instance
2. Your FastMCP server's URL (can be localhost for development, e.g., `http://localhost:8000`)
### Step 1: Get Supabase Project URL
@@ -37,7 +37,8 @@ from fastmcp.server.auth.providers.supabase import SupabaseProvider
# Configure Supabase Auth
auth = SupabaseProvider(
project_url="https://abc123.supabase.co",
- base_url="http://localhost:8000"
+ base_url="http://localhost:8000",
+ auth_route="/my/auth/route" # if self-hosting and using custom routes
)
mcp = FastMCP("Supabase Protected Server", auth=auth)
@@ -117,6 +118,10 @@ Your Supabase project URL (e.g., `https://abc123.supabase.co`)
Public URL of your FastMCP server (e.g., `https://your-server.com` or `http://localhost:8000` for development)
+
+Your Supabase auth route (e.g., `/auth/v1`)
+
+
Comma-, space-, or JSON-separated list of required OAuth scopes (e.g., `openid email` or `["openid", "email"]`)
diff --git a/src/fastmcp/server/auth/providers/supabase.py b/src/fastmcp/server/auth/providers/supabase.py
index 418831d484..50bb3be08c 100644
--- a/src/fastmcp/server/auth/providers/supabase.py
+++ b/src/fastmcp/server/auth/providers/supabase.py
@@ -34,6 +34,7 @@ class SupabaseProviderSettings(BaseSettings):
project_url: AnyHttpUrl
base_url: AnyHttpUrl
+ auth_route: str = "/auth/v1"
algorithm: Literal["HS256", "RS256", "ES256"] = "ES256"
required_scopes: list[str] | None = None
@@ -59,8 +60,8 @@ class SupabaseProvider(RemoteAuthProvider):
- Asymmetric keys (RS256/ES256) are recommended for production
2. JWT Verification:
- - FastMCP verifies JWTs using the JWKS endpoint at {project_url}/auth/v1/.well-known/jwks.json
- - JWTs are issued by {project_url}/auth/v1
+ - FastMCP verifies JWTs using the JWKS endpoint at {project_url}{auth_route}/.well-known/jwks.json
+ - JWTs are issued by {project_url}{auth_route}
- Tokens are cached for up to 10 minutes by Supabase's edge servers
- Algorithm must match your Supabase Auth configuration
@@ -93,6 +94,7 @@ def __init__(
*,
project_url: AnyHttpUrl | str | NotSetT = NotSet,
base_url: AnyHttpUrl | str | NotSetT = NotSet,
+ auth_route: str | NotSetT = NotSet,
algorithm: Literal["HS256", "RS256", "ES256"] | NotSetT = NotSet,
required_scopes: list[str] | NotSetT | None = NotSet,
token_verifier: TokenVerifier | None = None,
@@ -102,6 +104,7 @@ def __init__(
Args:
project_url: Your Supabase project URL (e.g., "https://abc123.supabase.co")
base_url: Public URL of this FastMCP server
+ auth_route: Supabase Auth route. Defaults to "/auth/v1".
algorithm: JWT signing algorithm (HS256, RS256, or ES256). Must match your
Supabase Auth configuration. Defaults to ES256.
required_scopes: Optional list of scopes to require for all requests.
@@ -115,6 +118,7 @@ def __init__(
for k, v in {
"project_url": project_url,
"base_url": base_url,
+ "auth_route": auth_route,
"algorithm": algorithm,
"required_scopes": required_scopes,
}.items()
@@ -124,12 +128,13 @@ def __init__(
self.project_url = str(settings.project_url).rstrip("/")
self.base_url = AnyHttpUrl(str(settings.base_url).rstrip("/"))
+ self.auth_route = settings.auth_route.strip("/")
# Create default JWT verifier if none provided
if token_verifier is None:
token_verifier = JWTVerifier(
- jwks_uri=f"{self.project_url}/auth/v1/.well-known/jwks.json",
- issuer=f"{self.project_url}/auth/v1",
+ jwks_uri=f"{self.project_url}/{self.auth_route}/.well-known/jwks.json",
+ issuer=f"{self.project_url}/{self.auth_route}",
algorithm=settings.algorithm,
required_scopes=settings.required_scopes,
)
@@ -137,7 +142,7 @@ def __init__(
# Initialize RemoteAuthProvider with Supabase as the authorization server
super().__init__(
token_verifier=token_verifier,
- authorization_servers=[AnyHttpUrl(f"{self.project_url}/auth/v1")],
+ authorization_servers=[AnyHttpUrl(f"{self.project_url}/{self.auth_route}")],
base_url=self.base_url,
)
@@ -162,7 +167,7 @@ async def oauth_authorization_server_metadata(request):
try:
async with httpx.AsyncClient() as client:
response = await client.get(
- f"{self.project_url}/auth/v1/.well-known/oauth-authorization-server"
+ f"{self.project_url}/{self.auth_route}/.well-known/oauth-authorization-server"
)
response.raise_for_status()
metadata = response.json()
diff --git a/tests/server/auth/providers/test_supabase.py b/tests/server/auth/providers/test_supabase.py
index 7cdc081309..5944fcfbb4 100644
--- a/tests/server/auth/providers/test_supabase.py
+++ b/tests/server/auth/providers/test_supabase.py
@@ -40,12 +40,14 @@ def test_init_with_env_vars(self, scopes_env):
{
"FASTMCP_SERVER_AUTH_SUPABASE_PROJECT_URL": "https://env123.supabase.co",
"FASTMCP_SERVER_AUTH_SUPABASE_BASE_URL": "https://envserver.com",
+ "FASTMCP_SERVER_AUTH_SUPABASE_AUTH_ROUTE": "/custom/auth/route",
},
):
provider = SupabaseProvider()
assert provider.project_url == "https://env123.supabase.co"
assert str(provider.base_url) == "https://envserver.com/"
+ assert provider.auth_route == "custom/auth/route"
def test_environment_variable_loading(self):
"""Test that environment variables are loaded correctly."""
@@ -150,6 +152,28 @@ def test_algorithm_from_env_var(self):
assert provider.token_verifier.algorithm == "RS256" # type: ignore[attr-defined]
+ def test_custom_auth_route(self):
+ provider = SupabaseProvider(
+ project_url="https://abc123.supabase.co",
+ base_url="https://myserver.com",
+ auth_route="/custom/auth/route",
+ )
+
+ assert provider.auth_route == "custom/auth/route"
+ assert (
+ provider.token_verifier.jwks_uri
+ == "https://abc123.supabase.co/custom/auth/route/.well-known/jwks.json"
+ ) # type: ignore[attr-defined]
+
+ def test_custom_auth_route_trailing_slash(self):
+ provider = SupabaseProvider(
+ project_url="https://abc123.supabase.co",
+ base_url="https://myserver.com",
+ auth_route="/custom/auth/route/",
+ )
+
+ assert provider.auth_route == "custom/auth/route"
+
def run_mcp_server(host: str, port: int) -> None:
mcp = FastMCP(