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