mirror of
https://github.com/PrefectHQ/fastmcp.git
synced 2026-08-14 09:39:11 +02:00
348 lines
11 KiB
Text
348 lines
11 KiB
Text
---
|
|
title: Authorization
|
|
sidebarTitle: Authorization
|
|
description: Control access to components using callable-based authorization checks that filter visibility and enforce permissions.
|
|
icon: shield-halved
|
|
tag: NEW
|
|
---
|
|
|
|
import { VersionBadge } from "/snippets/version-badge.mdx"
|
|
|
|
<VersionBadge version="3.0.0" />
|
|
|
|
Authorization controls what authenticated users can do with your FastMCP server. While [authentication](/servers/auth/authentication) verifies identity (who you are), authorization determines access (what you can do). FastMCP provides a callable-based authorization system that works at both the component level and globally via middleware.
|
|
|
|
The authorization model centers on a simple concept: callable functions that receive context about the current request and return `True` to allow access or `False` to deny it. Multiple checks combine with AND logic, meaning all checks must pass for access to be granted.
|
|
|
|
<Note>
|
|
Authorization relies on OAuth tokens which are only available with HTTP transports (SSE, Streamable HTTP). In STDIO mode, there's no OAuth mechanism, so `get_access_token()` returns `None` and all auth checks are skipped.
|
|
</Note>
|
|
|
|
## Auth Checks
|
|
|
|
An auth check is any callable that accepts an `AuthContext` and returns a boolean. The `AuthContext` provides access to the current token (if any) and the component being accessed.
|
|
|
|
```python
|
|
from fastmcp.server.auth import AuthContext
|
|
|
|
def my_custom_check(ctx: AuthContext) -> bool:
|
|
# ctx.token is AccessToken | None
|
|
# ctx.component is the Tool, Resource, or Prompt being accessed
|
|
return ctx.token is not None and "special" in ctx.token.scopes
|
|
```
|
|
|
|
FastMCP provides three built-in auth checks that cover common authorization patterns.
|
|
|
|
### require_auth
|
|
|
|
The simplest check verifies that any valid authentication token is present. Unauthenticated requests are denied.
|
|
|
|
```python
|
|
from fastmcp import FastMCP
|
|
from fastmcp.server.auth import require_auth
|
|
|
|
mcp = FastMCP("Protected Server")
|
|
|
|
@mcp.tool(auth=require_auth)
|
|
def protected_operation() -> str:
|
|
"""Only accessible to authenticated users."""
|
|
return "Success"
|
|
```
|
|
|
|
### require_scopes
|
|
|
|
For scope-based authorization, `require_scopes` checks that the token contains all specified OAuth scopes. When multiple scopes are provided, all must be present (AND logic).
|
|
|
|
```python
|
|
from fastmcp import FastMCP
|
|
from fastmcp.server.auth import require_scopes
|
|
|
|
mcp = FastMCP("Scoped Server")
|
|
|
|
@mcp.tool(auth=require_scopes("admin"))
|
|
def admin_operation() -> str:
|
|
"""Requires the 'admin' scope."""
|
|
return "Admin action completed"
|
|
|
|
@mcp.tool(auth=require_scopes("read", "write"))
|
|
def read_write_operation() -> str:
|
|
"""Requires both 'read' AND 'write' scopes."""
|
|
return "Read/write action completed"
|
|
```
|
|
|
|
### restrict_tag
|
|
|
|
Tag-based restrictions apply scope requirements conditionally. If a component has the specified tag, the token must have the required scopes. Components without the tag are unaffected.
|
|
|
|
```python
|
|
from fastmcp import FastMCP
|
|
from fastmcp.server.auth import restrict_tag
|
|
from fastmcp.server.middleware import AuthMiddleware
|
|
|
|
mcp = FastMCP(
|
|
"Tagged Server",
|
|
middleware=[
|
|
AuthMiddleware(auth=restrict_tag("admin", scopes=["admin"]))
|
|
]
|
|
)
|
|
|
|
@mcp.tool(tags={"admin"})
|
|
def admin_tool() -> str:
|
|
"""Tagged 'admin', so requires 'admin' scope."""
|
|
return "Admin only"
|
|
|
|
@mcp.tool(tags={"public"})
|
|
def public_tool() -> str:
|
|
"""Not tagged 'admin', so no scope required by the restriction."""
|
|
return "Anyone can access"
|
|
```
|
|
|
|
### Combining Checks
|
|
|
|
Multiple auth checks can be combined by passing a list. All checks must pass for authorization to succeed (AND logic).
|
|
|
|
```python
|
|
from fastmcp import FastMCP
|
|
from fastmcp.server.auth import require_auth, require_scopes
|
|
|
|
mcp = FastMCP("Combined Auth Server")
|
|
|
|
@mcp.tool(auth=[require_auth, require_scopes("admin")])
|
|
def secure_admin_action() -> str:
|
|
"""Requires authentication AND the 'admin' scope."""
|
|
return "Secure admin action"
|
|
```
|
|
|
|
### Custom Auth Checks
|
|
|
|
Any callable that accepts `AuthContext` and returns `bool` can serve as an auth check. This enables authorization logic based on token claims, component metadata, or external systems.
|
|
|
|
```python
|
|
from fastmcp import FastMCP
|
|
from fastmcp.server.auth import AuthContext
|
|
|
|
mcp = FastMCP("Custom Auth Server")
|
|
|
|
def require_premium_user(ctx: AuthContext) -> bool:
|
|
"""Check for premium user status in token claims."""
|
|
if ctx.token is None:
|
|
return False
|
|
return ctx.token.claims.get("premium", False) is True
|
|
|
|
def require_access_level(minimum_level: int):
|
|
"""Factory function for level-based authorization."""
|
|
def check(ctx: AuthContext) -> bool:
|
|
if ctx.token is None:
|
|
return False
|
|
user_level = ctx.token.claims.get("level", 0)
|
|
return user_level >= minimum_level
|
|
return check
|
|
|
|
@mcp.tool(auth=require_premium_user)
|
|
def premium_feature() -> str:
|
|
"""Only for premium users."""
|
|
return "Premium content"
|
|
|
|
@mcp.tool(auth=require_access_level(5))
|
|
def advanced_feature() -> str:
|
|
"""Requires access level 5 or higher."""
|
|
return "Advanced feature"
|
|
```
|
|
|
|
Auth checks can raise exceptions for explicit denial with custom messages:
|
|
|
|
- **`AuthorizationError`**: Propagates with its custom message, useful for explaining why access was denied
|
|
- **Other exceptions**: Masked for security (logged internally, treated as denial)
|
|
|
|
```python
|
|
from fastmcp.server.auth import AuthContext
|
|
from fastmcp.exceptions import AuthorizationError
|
|
|
|
def require_verified_email(ctx: AuthContext) -> bool:
|
|
"""Require verified email with explicit denial message."""
|
|
if ctx.token is None:
|
|
raise AuthorizationError("Authentication required")
|
|
if not ctx.token.claims.get("email_verified"):
|
|
raise AuthorizationError("Email verification required")
|
|
return True
|
|
```
|
|
|
|
## Component-Level Authorization
|
|
|
|
The `auth` parameter on decorators controls visibility of individual components. When auth checks fail for the current request, the component is hidden from list responses—it simply doesn't appear.
|
|
|
|
```python
|
|
from fastmcp import FastMCP
|
|
from fastmcp.server.auth import require_auth, require_scopes
|
|
|
|
mcp = FastMCP("Component Auth Server")
|
|
|
|
@mcp.tool(auth=require_auth)
|
|
def authenticated_tool() -> str:
|
|
"""Only visible to authenticated users."""
|
|
return "Authenticated"
|
|
|
|
@mcp.resource("secret://data", auth=require_scopes("read"))
|
|
def secret_resource() -> str:
|
|
"""Only visible to users with 'read' scope."""
|
|
return "Secret data"
|
|
|
|
@mcp.prompt(auth=require_scopes("admin"))
|
|
def admin_prompt() -> str:
|
|
"""Only visible to users with 'admin' scope."""
|
|
return "Admin prompt content"
|
|
```
|
|
|
|
<Warning>
|
|
Component-level `auth` only controls visibility in list operations. It does not block direct access. Use `AuthMiddleware` to enforce authorization on execution.
|
|
</Warning>
|
|
|
|
## Server-Level Authorization
|
|
|
|
For server-wide authorization enforcement, use `AuthMiddleware`. This middleware applies auth checks globally to all components—filtering list responses and blocking unauthorized execution.
|
|
|
|
```python
|
|
from fastmcp import FastMCP
|
|
from fastmcp.server.auth import require_auth
|
|
from fastmcp.server.middleware import AuthMiddleware
|
|
|
|
mcp = FastMCP(
|
|
"Enforced Auth Server",
|
|
middleware=[AuthMiddleware(auth=require_auth)]
|
|
)
|
|
|
|
@mcp.tool
|
|
def any_tool() -> str:
|
|
"""Requires authentication to see AND call."""
|
|
return "Protected"
|
|
```
|
|
|
|
### Filtering vs Enforcement
|
|
|
|
| Behavior | Component-level `auth` | `AuthMiddleware` |
|
|
|----------|------------------------|------------------|
|
|
| Filters list responses | Yes | Yes |
|
|
| Blocks execution | No | Yes (raises `AuthorizationError`) |
|
|
|
|
Component-level auth is useful for hiding components from unauthorized users while still allowing advanced clients to access them directly. `AuthMiddleware` provides complete enforcement by raising `AuthorizationError` when unauthorized requests attempt execution.
|
|
|
|
### Tag-Based Global Authorization
|
|
|
|
A common pattern uses `restrict_tag` with `AuthMiddleware` to apply scope requirements based on component tags.
|
|
|
|
```python
|
|
from fastmcp import FastMCP
|
|
from fastmcp.server.auth import restrict_tag
|
|
from fastmcp.server.middleware import AuthMiddleware
|
|
|
|
mcp = FastMCP(
|
|
"Tag-Based Auth Server",
|
|
middleware=[
|
|
AuthMiddleware(auth=restrict_tag("admin", scopes=["admin"])),
|
|
AuthMiddleware(auth=restrict_tag("write", scopes=["write"])),
|
|
]
|
|
)
|
|
|
|
@mcp.tool(tags={"admin"})
|
|
def delete_all_data() -> str:
|
|
"""Requires 'admin' scope."""
|
|
return "Deleted"
|
|
|
|
@mcp.tool(tags={"write"})
|
|
def update_record(id: str, data: str) -> str:
|
|
"""Requires 'write' scope."""
|
|
return f"Updated {id}"
|
|
|
|
@mcp.tool
|
|
def read_record(id: str) -> str:
|
|
"""No tag restrictions, accessible to all."""
|
|
return f"Record {id}"
|
|
```
|
|
|
|
## Accessing Tokens in Tools
|
|
|
|
Tools can access the current authentication token using `get_access_token()` from `fastmcp.server.dependencies`. This enables tools to make decisions based on user identity or permissions beyond simple authorization checks.
|
|
|
|
```python
|
|
from fastmcp import FastMCP
|
|
from fastmcp.server.dependencies import get_access_token
|
|
|
|
mcp = FastMCP("Token Access Server")
|
|
|
|
@mcp.tool
|
|
def personalized_greeting() -> str:
|
|
"""Greet the user based on their token claims."""
|
|
token = get_access_token()
|
|
|
|
if token is None:
|
|
return "Hello, guest!"
|
|
|
|
name = token.claims.get("name", "user")
|
|
return f"Hello, {name}!"
|
|
|
|
@mcp.tool
|
|
def user_dashboard() -> dict:
|
|
"""Return user-specific data based on token."""
|
|
token = get_access_token()
|
|
|
|
if token is None:
|
|
return {"error": "Not authenticated"}
|
|
|
|
return {
|
|
"client_id": token.client_id,
|
|
"scopes": token.scopes,
|
|
"claims": token.claims,
|
|
}
|
|
```
|
|
|
|
## Reference
|
|
|
|
### AccessToken
|
|
|
|
The `AccessToken` object contains information extracted from the OAuth token.
|
|
|
|
| Property | Type | Description |
|
|
|----------|------|-------------|
|
|
| `token` | `str` | The raw token string |
|
|
| `client_id` | `str \| None` | OAuth client identifier |
|
|
| `scopes` | `list[str]` | Granted OAuth scopes |
|
|
| `expires_at` | `datetime \| None` | Token expiration time |
|
|
| `claims` | `dict[str, Any]` | All JWT claims or custom token data |
|
|
|
|
### AuthContext
|
|
|
|
The `AuthContext` dataclass is passed to all auth check functions.
|
|
|
|
| Property | Type | Description |
|
|
|----------|------|-------------|
|
|
| `token` | `AccessToken \| None` | Current access token, or `None` if unauthenticated |
|
|
| `component` | `Tool \| Resource \| Prompt` | The component being accessed |
|
|
|
|
Access to the component object enables authorization decisions based on metadata like tags, name, or custom properties.
|
|
|
|
```python
|
|
from fastmcp.server.auth import AuthContext
|
|
|
|
def require_matching_tag(ctx: AuthContext) -> bool:
|
|
"""Require a scope matching each of the component's tags."""
|
|
if ctx.token is None:
|
|
return False
|
|
user_scopes = set(ctx.token.scopes)
|
|
return ctx.component.tags.issubset(user_scopes)
|
|
```
|
|
|
|
### Imports
|
|
|
|
```python
|
|
from fastmcp.server.auth import (
|
|
AccessToken, # Token with .token, .client_id, .scopes, .expires_at, .claims
|
|
AuthContext, # Context with .token, .component
|
|
AuthCheck, # Type alias: Callable[[AuthContext], bool]
|
|
require_auth, # Built-in: requires any valid token
|
|
require_scopes, # Built-in: requires specific scopes
|
|
restrict_tag, # Built-in: tag-based scope requirements
|
|
run_auth_checks, # Utility: run checks with AND logic
|
|
)
|
|
|
|
from fastmcp.server.middleware import AuthMiddleware
|
|
```
|