Skip to content

Commit cc2dfd6

Browse files
committed
feat: add fastmcp-mcp-fga-js example
1 parent 9097bd6 commit cc2dfd6

19 files changed

Lines changed: 3974 additions & 1 deletion
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
AUTH0_DOMAIN=
2+
AUTH0_AUDIENCE=
3+
PORT=3001
4+
MCP_SERVER_URL=http://localhost:3001
5+
6+
# Get these from dashboard.fga.dev / Store Settings
7+
FGA_API_URL='https://api.us1.fga.dev'
8+
FGA_STORE_ID=<store_id>
9+
FGA_API_TOKEN_ISSUER='auth.fga.dev'
10+
FGA_API_AUDIENCE='https://api.us1.fga.dev/'
11+
FGA_CLIENT_ID='<client_id>'
12+
FGA_CLIENT_SECRET='<client_secret>'
Lines changed: 205 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,205 @@
1+
# Example FastMCP MCP Server with Auth0 and Auth0 FGA Integration
2+
3+
This is a practical example of securing a [Model Context Protocol (MCP)](https://modelcontextprotocol.io/docs) server
4+
with Auth0 using the [FastMCP](https://github.com/punkpeye/fastmcp) TypeScript framework. It demonstrates
5+
real-world OAuth 2.0 and OIDC integration with JWT token verification, and how to implement fine grained authorization for MCPs.
6+
7+
8+
## Available Tools
9+
10+
The server exposes the following tools:
11+
12+
- `whoami` - Returns authenticated user information and granted scopes
13+
- `greet` - Personalized greeting demonstrating authenticated tool access
14+
- `get_datetime` Returns the current UTC date and time (no scope required)
15+
- `get_documents` - Returns a list of documents from a mock API. Depending on the user role, it will return private documents or not.
16+
17+
## Install dependencies
18+
19+
Install the dependencies using npm:
20+
21+
```bash
22+
npm install
23+
```
24+
25+
## Auth0 Tenant Setup
26+
27+
For detailed instructions on setting up your Auth0 tenant for MCP server integration, please refer to the [Auth0 Tenant Setup guide](https://github.com/auth0-samples/auth0-ai-samples/tree/main/auth-for-mcp/fastmcp-mcp-js/README.md#auth0-tenant-setup) in the FastMCP example.
28+
29+
30+
## Auth0 FGA Setup
31+
32+
Auth0 FGA provides fine-grained authorization using [Relationship-Based Access Control (ReBAC)](https://docs.fga.dev/concepts#what-is-relationship-based-access-control-rebac). It's built on [OpenFGA](https://openfga.dev), a CNCF incubation project, and offers more flexible authorization patterns than traditional RBAC.
33+
34+
### Prerequisites
35+
36+
1. **Create an Auth0 FGA Account**: Sign up for free at [fga.dev](https://fga.dev)
37+
2. **Generate API Credentials**: Follow [this guide](https://docs.fga.dev/intro/settings) to create credentials with full permissions
38+
3. **Install the FGA CLI**:
39+
```bash
40+
# macOS
41+
brew install openfga/tap/fga
42+
43+
# Other platforms - download from:
44+
# https://github.com/openfga/cli/releases
45+
```
46+
47+
### CLI Configuration
48+
49+
After creating your FGA credentials, export the following (provided during credential creation):
50+
51+
```bash
52+
export FGA_API_URL='https://api.us1.fga.dev'
53+
export FGA_STORE_ID='<your-store-id>'
54+
export FGA_API_TOKEN_ISSUER='auth.fga.dev'
55+
export FGA_API_AUDIENCE='https://api.us1.fga.dev/'
56+
export FGA_CLIENT_ID='<your-client-id>'
57+
export FGA_CLIENT_SECRET='<your-client-secret>'
58+
```
59+
60+
### Authorization Model
61+
62+
This example uses an authorization model defined in [`fga/model.fga`](./fga/model.fga) that supports:
63+
64+
- **Public Tools**: Accessible to all authenticated users (e.g., `get_datetime`)
65+
- **Role-Based Access**: Tools assigned to specific roles
66+
- **Group Membership**: Users inherit permissions through group membership
67+
- **Temporal Access**: Time-limited tool access with automatic expiration
68+
- **Resource-Specific Permissions**: Fine-grained access (e.g., viewing private documents)
69+
70+
### Initial Setup
71+
72+
1. **Deploy the Authorization Model**:
73+
```bash
74+
fga model write --file ./fga/model.fga
75+
```
76+
77+
2. **Import Initial Data**: The [`fga/tuples.yaml`](./fga/tuples.yaml) file defines:
78+
- Two roles: `admin` and `content_manager`
79+
- Two groups: `marketing` (content_manager role) and `managers` (admin role)
80+
- Tool permissions:
81+
- Everyone: `get_datetime`
82+
- Admin & Content Manager: `greet`, `whoami`, `get_documents`
83+
- Admin only: Can view private documents
84+
85+
Import the tuples:
86+
```bash
87+
fga tuple write --file ./fga/tuples.yaml
88+
```
89+
90+
### Managing User Access
91+
92+
Use the provided scripts to manage user permissions dynamically:
93+
94+
#### Add User to Group
95+
Grants all permissions associated with the group:
96+
97+
```bash
98+
# Add user to managers group (admin role - full access including private documents)
99+
./fga/add-user-to-group.sh user@example.com managers
100+
101+
# Add user to marketing group (content_manager role - no private document access)
102+
./fga/add-user-to-group.sh user@example.com marketing
103+
```
104+
105+
#### Remove User from Group
106+
Revokes group-based permissions:
107+
108+
```bash
109+
./fga/remove-user-from-group.sh user@example.com managers
110+
```
111+
112+
#### Grant Temporary Access
113+
Provides time-limited access to specific tools:
114+
115+
```bash
116+
# Grant 20-second access to the greet tool
117+
./fga/add-temporal-access.sh user@example.com greet 20s
118+
119+
# Grant 1-hour access
120+
./fga/add-temporal-access.sh user@example.com greet 1h
121+
```
122+
123+
Temporal access automatically expires.
124+
125+
### Testing Access Changes
126+
127+
After modifying permissions, test with different users to verify:
128+
129+
1. **Managers Group** (admin role):
130+
- Should see: `get_datetime`, `greet`, `whoami`, `get_documents`
131+
- Can view: All documents (public + private)
132+
133+
2. **Marketing Group** (content_manager role):
134+
- Should see: `get_datetime`, `greet`, `whoami`, `get_documents`
135+
- Can view: Public documents only
136+
137+
3. **User with Temporal Access**:
138+
- Should see: `get_datetime` + temporarily granted tool
139+
- Access expires after specified duration
140+
141+
4. **User with No Assignments**:
142+
- Should see: `get_datetime` only
143+
144+
You can also manage tuples directly in the [Auth0 FGA Dashboard](https://dashboard.fga.dev) for.
145+
146+
## Configuration
147+
148+
Rename `.env.example` to `.env` and configure the domain and audience:
149+
150+
```
151+
# Auth0 tenant domain
152+
AUTH0_DOMAIN=example-tenant.us.auth0.com
153+
154+
# Auth0 API Identifier
155+
AUTH0_AUDIENCE=http://localhost:3001/
156+
157+
# FGA Configuration
158+
FGA_API_URL='https://api.us1.fga.dev'
159+
FGA_STORE_ID=<store_id>
160+
FGA_API_TOKEN_ISSUER='auth.fga.dev'
161+
FGA_API_AUDIENCE='https://api.us1.fga.dev/'
162+
FGA_CLIENT_ID='<client_secret>'
163+
FGA_CLIENT_SECRET='<client_secret>'
164+
```
165+
166+
With the configuration in place, the example can be started by running:
167+
168+
```bash
169+
npm run start
170+
```
171+
172+
## Testing
173+
174+
Use an MCP client like [MCP Inspector](https://github.com/modelcontextprotocol/inspector) to test your server interactively:
175+
176+
```bash
177+
npx @modelcontextprotocol/inspector
178+
```
179+
180+
The server will start up and the UI will be accessible at http://localhost:6274.
181+
182+
In the MCP Inspector, select `Streamable HTTP` as the `Transport Type`, enter `http://localhost:3001/mcp` as the URL, and select `Via Proxy` for `Connection Type`.
183+
184+
### Using cURL
185+
186+
You can use cURL to verify that the server is running:
187+
188+
```bash
189+
# Test that the server is running and accessible - check OAuth resource metadata
190+
curl -v http://localhost:3001/.well-known/oauth-protected-resource
191+
192+
# Test MCP initialization (requires valid Auth0 access token)
193+
curl -X POST http://localhost:3001/mcp \
194+
-H "Content-Type: application/json" \
195+
-H "Accept: application/json, text/event-stream" \
196+
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
197+
-d '{"jsonrpc": "2.0", "id": 1, "method": "initialize", "params": {"protocolVersion": "2025-06-18", "capabilities": {}, "clientInfo": {"name": "curl-test", "version": "1.0.0"}}}'
198+
199+
# Test get_datetime tool (no scope required) - outputs ISO string like 2025-10-31T14:12:03.123Z
200+
curl -X POST http://localhost:3001/mcp \
201+
-H "Content-Type: application/json" \
202+
-H "Accept: application/json, text/event-stream" \
203+
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
204+
-d '{"jsonrpc": "2.0", "id": 2, "method": "tools/call", "params": {"name": "get_datetime", "arguments": {}}}'
205+
```

0 commit comments

Comments
 (0)