Skip to content

Commit c13ffbc

Browse files
committed
oauth wip
Signed-off-by: phernandez <paul@basicmachines.co>
1 parent b038eae commit c13ffbc

9 files changed

Lines changed: 1376 additions & 2 deletions

File tree

.env.oauth.example

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
# OAuth Configuration for Basic Memory MCP Server
2+
# Copy this file to .env and update the values
3+
4+
# Enable OAuth authentication
5+
FASTMCP_AUTH_ENABLED=true
6+
7+
# OAuth provider type: basic, github, or google
8+
# - basic: Built-in OAuth provider with in-memory storage
9+
# - github: Integrate with GitHub OAuth
10+
# - google: Integrate with Google OAuth
11+
FASTMCP_AUTH_PROVIDER=basic
12+
13+
# OAuth issuer URL (your MCP server URL)
14+
FASTMCP_AUTH_ISSUER_URL=http://localhost:8000
15+
16+
# Documentation URL for OAuth endpoints
17+
FASTMCP_AUTH_DOCS_URL=http://localhost:8000/docs/oauth
18+
19+
# Required scopes (comma-separated)
20+
# Examples: read,write,admin
21+
FASTMCP_AUTH_REQUIRED_SCOPES=read,write
22+
23+
# Secret key for JWT tokens (auto-generated if not set)
24+
# FASTMCP_AUTH_SECRET_KEY=your-secret-key-here
25+
26+
# Enable client registration endpoint
27+
FASTMCP_AUTH_CLIENT_REGISTRATION_ENABLED=true
28+
29+
# Enable token revocation endpoint
30+
FASTMCP_AUTH_REVOCATION_ENABLED=true
31+
32+
# Default scopes for new clients
33+
FASTMCP_AUTH_DEFAULT_SCOPES=read
34+
35+
# Valid scopes that can be requested
36+
FASTMCP_AUTH_VALID_SCOPES=read,write,admin
37+
38+
# Client secret expiry in seconds (optional)
39+
# FASTMCP_AUTH_CLIENT_SECRET_EXPIRY=86400
40+
41+
# GitHub OAuth settings (if using github provider)
42+
# GITHUB_CLIENT_ID=your-github-client-id
43+
# GITHUB_CLIENT_SECRET=your-github-client-secret
44+
45+
# Google OAuth settings (if using google provider)
46+
# GOOGLE_CLIENT_ID=your-google-client-id
47+
# GOOGLE_CLIENT_SECRET=your-google-client-secret

AUTH.md

Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
To test the built-in OAuth flow locally, here's what you need to do:
2+
3+
1. Set Environment Variables
4+
5+
First, create a .env file based on the example:
6+
7+
cp .env.oauth.example .env
8+
9+
Then edit .env to enable OAuth:
10+
11+
# Enable OAuth authentication
12+
FASTMCP_AUTH_ENABLED=true
13+
14+
# Use the basic (built-in) provider
15+
FASTMCP_AUTH_PROVIDER=basic
16+
17+
# OAuth issuer URL (your MCP server URL)
18+
FASTMCP_AUTH_ISSUER_URL=http://localhost:8000
19+
20+
2. Start the MCP Server with OAuth
21+
22+
Start the server using the streamable-http transport (OAuth works best with HTTP):
23+
24+
# Run with OAuth enabled
25+
FASTMCP_AUTH_ENABLED=true basic-memory mcp --transport streamable-http
26+
27+
# Or if you have the env vars in .env file:
28+
basic-memory mcp --transport streamable-http
29+
30+
You should see:
31+
OAuth authentication is ENABLED
32+
Issuer URL: http://localhost:8000
33+
34+
3. Register an OAuth Client
35+
36+
In a new terminal, register a test client:
37+
38+
basic-memory auth register-client
39+
40+
This will output something like:
41+
Client registered successfully!
42+
Client ID: AbCdEfGhIjKlMnOp
43+
Client Secret: QrStUvWxYz123456789...
44+
45+
Save these credentials!
46+
47+
4. Test the OAuth Flow
48+
49+
Run the built-in test command:
50+
51+
basic-memory auth test-auth
52+
53+
This will:
54+
1. Register a test client
55+
2. Generate an authorization URL
56+
3. Exchange the auth code for tokens
57+
4. Validate the access token
58+
59+
You'll see output like:
60+
Registered test client: ABC123...
61+
Authorization URL: http://localhost:3000/callback?code=XYZ&state=test-state
62+
Access token: eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9...
63+
Refresh token: QrStUvWxYz...
64+
Expires in: 3600 seconds
65+
Access token validated successfully!
66+
67+
5. Test with a Real Client
68+
69+
To test with an actual MCP client, you'll need to:
70+
71+
1. Make an authorization request:
72+
# Use the client_id from step 3
73+
curl "http://localhost:8000/authorize?client_id=YOUR_CLIENT_ID&redirect_uri=http://localhost:3000/callback&response_type=code&code_challenge=test-challenge&code_challenge_method=S256"
74+
75+
2. Exchange the code for tokens:
76+
# Use the code from the redirect URL
77+
curl -X POST http://localhost:8000/token \
78+
-H "Content-Type: application/x-www-form-urlencoded" \
79+
-d "grant_type=authorization_code&code=YOUR_AUTH_CODE&client_id=YOUR_CLIENT_ID&client_secret=YOUR_CLIENT_SECRET&code_verifier=test-verifier"
80+
81+
3. Use the access token to call MCP endpoints:
82+
curl http://localhost:8000/mcp \
83+
-H "Authorization: Bearer YOUR_ACCESS_TOKEN"
84+
85+
6. Quick Test Script
86+
87+
Here's a simple Python script to test the flow:
88+
89+
import httpx
90+
import asyncio
91+
from urllib.parse import urlparse, parse_qs
92+
93+
async def test_oauth():
94+
client = httpx.AsyncClient()
95+
96+
# Your client credentials
97+
client_id = "YOUR_CLIENT_ID"
98+
client_secret = "YOUR_CLIENT_SECRET"
99+
100+
# 1. Get authorization URL
101+
auth_response = await client.get(
102+
"http://localhost:8000/authorize",
103+
params={
104+
"client_id": client_id,
105+
"redirect_uri": "http://localhost:3000/callback",
106+
"response_type": "code",
107+
"code_challenge": "test-challenge",
108+
"code_challenge_method": "S256",
109+
}
110+
)
111+
112+
# Extract code from redirect
113+
redirect_url = auth_response.headers.get("Location")
114+
parsed = urlparse(redirect_url)
115+
code = parse_qs(parsed.query)["code"][0]
116+
117+
# 2. Exchange code for tokens
118+
token_response = await client.post(
119+
"http://localhost:8000/token",
120+
data={
121+
"grant_type": "authorization_code",
122+
"code": code,
123+
"client_id": client_id,
124+
"client_secret": client_secret,
125+
"code_verifier": "test-verifier",
126+
}
127+
)
128+
tokens = token_response.json()
129+
130+
# 3. Use access token
131+
mcp_response = await client.get(
132+
"http://localhost:8000/mcp",
133+
headers={"Authorization": f"Bearer {tokens['access_token']}"}
134+
)
135+
136+
print(f"MCP Response: {mcp_response.status_code}")
137+
138+
asyncio.run(test_oauth())
139+
140+
7. Debug Tips
141+
142+
- Check the server logs for OAuth-related messages
143+
- The basic provider stores everything in memory, so clients/tokens are lost on restart
144+
- You can modify the log level for more details:
145+
FASTMCP_AUTH_ENABLED=true basic-memory mcp --transport streamable-http
146+
147+
That's it! The built-in OAuth provider is perfect for local development and testing. For production, you'd want to use an external provider (GitHub/Google) or implement persistent storage for the basic provider.

docs/OAuth Authentication.md

Lines changed: 187 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,187 @@
1+
# OAuth Authentication for Basic Memory MCP Server
2+
3+
Basic Memory supports OAuth 2.1 authentication for secure access to the MCP server. This guide explains how to configure and use OAuth authentication.
4+
5+
## Overview
6+
7+
OAuth support in Basic Memory includes:
8+
- Built-in OAuth provider with in-memory storage
9+
- Integration with external OAuth providers (GitHub, Google)
10+
- OAuth client management via CLI
11+
- Support for PKCE (Proof Key for Code Exchange)
12+
- Token validation and revocation
13+
14+
## Configuration
15+
16+
### Environment Variables
17+
18+
Copy `.env.oauth.example` to `.env` and configure:
19+
20+
```bash
21+
# Enable OAuth authentication
22+
FASTMCP_AUTH_ENABLED=true
23+
24+
# OAuth provider type: basic, github, or google
25+
FASTMCP_AUTH_PROVIDER=basic
26+
27+
# OAuth issuer URL (your MCP server URL)
28+
FASTMCP_AUTH_ISSUER_URL=http://localhost:8000
29+
30+
# Required scopes (comma-separated)
31+
FASTMCP_AUTH_REQUIRED_SCOPES=read,write
32+
```
33+
34+
### Provider Types
35+
36+
1. **Basic Provider** (default)
37+
- Built-in OAuth implementation
38+
- In-memory storage (not suitable for production)
39+
- Good for development and testing
40+
41+
2. **GitHub Provider**
42+
- Integrates with GitHub OAuth
43+
- Requires GitHub OAuth app configuration
44+
- Set `GITHUB_CLIENT_ID` and `GITHUB_CLIENT_SECRET`
45+
46+
3. **Google Provider**
47+
- Integrates with Google OAuth
48+
- Requires Google OAuth app configuration
49+
- Set `GOOGLE_CLIENT_ID` and `GOOGLE_CLIENT_SECRET`
50+
51+
## Usage
52+
53+
### Start the MCP Server with OAuth
54+
55+
```bash
56+
# Start with OAuth enabled
57+
FASTMCP_AUTH_ENABLED=true bm mcp
58+
59+
# Check OAuth status
60+
# The server will display:
61+
# "OAuth authentication is ENABLED"
62+
# "Issuer URL: http://localhost:8000"
63+
```
64+
65+
### Managing OAuth Clients
66+
67+
Register a new OAuth client:
68+
69+
```bash
70+
# Auto-generate client credentials
71+
bm auth register-client
72+
73+
# Specify custom client ID
74+
bm auth register-client --client-id my-client-id
75+
```
76+
77+
Test the OAuth flow:
78+
79+
```bash
80+
bm auth test-auth
81+
```
82+
83+
### OAuth Flow
84+
85+
1. **Authorization Request**
86+
```
87+
GET /authorize?client_id=xxx&redirect_uri=xxx&response_type=code&state=xxx&code_challenge=xxx
88+
```
89+
90+
2. **Token Exchange**
91+
```
92+
POST /token
93+
Content-Type: application/x-www-form-urlencoded
94+
95+
grant_type=authorization_code&code=xxx&client_id=xxx&client_secret=xxx
96+
```
97+
98+
3. **Access Protected Resources**
99+
```
100+
GET /mcp
101+
Authorization: Bearer <access_token>
102+
```
103+
104+
4. **Refresh Token**
105+
```
106+
POST /token
107+
grant_type=refresh_token&refresh_token=xxx&client_id=xxx&client_secret=xxx
108+
```
109+
110+
## HTTP Transport with OAuth
111+
112+
When using streamable-http transport with OAuth:
113+
114+
```bash
115+
# Start server with OAuth
116+
FASTMCP_AUTH_ENABLED=true bm mcp --transport streamable-http --host 0.0.0.0 --port 8000
117+
118+
# The server provides these endpoints:
119+
# - /authorize - OAuth authorization
120+
# - /token - Token exchange
121+
# - /mcp - Protected MCP endpoint
122+
```
123+
124+
## Security Considerations
125+
126+
1. **HTTPS Required**: Always use HTTPS in production
127+
2. **Secret Storage**: Store client secrets securely
128+
3. **Token Expiration**: Access tokens expire after 1 hour
129+
4. **Scope Validation**: Configure required scopes appropriately
130+
5. **PKCE**: Always use PKCE for enhanced security
131+
132+
## External Provider Setup
133+
134+
### GitHub OAuth
135+
136+
1. Create a GitHub OAuth App:
137+
- Go to GitHub Settings > Developer settings > OAuth Apps
138+
- Create new OAuth App
139+
- Set Authorization callback URL to `http://localhost:8000/callback`
140+
141+
2. Configure environment:
142+
```bash
143+
FASTMCP_AUTH_PROVIDER=github
144+
GITHUB_CLIENT_ID=your-client-id
145+
GITHUB_CLIENT_SECRET=your-client-secret
146+
```
147+
148+
### Google OAuth
149+
150+
1. Create Google OAuth credentials:
151+
- Go to Google Cloud Console
152+
- Create OAuth 2.0 Client ID
153+
- Add redirect URI: `http://localhost:8000/callback`
154+
155+
2. Configure environment:
156+
```bash
157+
FASTMCP_AUTH_PROVIDER=google
158+
GOOGLE_CLIENT_ID=your-client-id
159+
GOOGLE_CLIENT_SECRET=your-client-secret
160+
```
161+
162+
## Development
163+
164+
For development, use the basic provider with test clients:
165+
166+
```bash
167+
# Enable OAuth with basic provider
168+
FASTMCP_AUTH_ENABLED=true FASTMCP_AUTH_PROVIDER=basic bm mcp
169+
170+
# Register test client
171+
bm auth register-client
172+
173+
# Test the flow
174+
bm auth test-auth
175+
```
176+
177+
## Troubleshooting
178+
179+
- **401 Unauthorized**: Check token validity and expiration
180+
- **403 Forbidden**: Verify required scopes are present
181+
- **Invalid client**: Ensure client credentials are correct
182+
- **Token expired**: Use refresh token to get new access token
183+
184+
## API Reference
185+
186+
See the MCP Authorization specification:
187+
https://modelcontextprotocol.io/specification/2025-03-26/basic/authorization

0 commit comments

Comments
 (0)