Skip to content

Commit d368c8d

Browse files
committed
address comments:
* fix: add /mcp to endpoints in misc README.mds * improve architecture interaction diagrams in README.md * add "OAuth Flow Analysis" section in README.md
1 parent b796560 commit d368c8d

File tree

4 files changed

+83
-23
lines changed

4 files changed

+83
-23
lines changed

README.md

Lines changed: 75 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ npm run dev:integrated
9090
npx -y @modelcontextprotocol/inspector
9191

9292
# 4. Connect and test:
93-
# - Connect to http://localhost:3232
93+
# - Connect to http://localhost:3232/mcp
9494
# - Navigate to the Auth tab
9595
# - Complete the OAuth flow
9696
# - All auth endpoints will be served from :3232
@@ -108,7 +108,7 @@ npm run dev:with-separate-auth
108108
npx -y @modelcontextprotocol/inspector
109109

110110
# 4. Connect and test:
111-
# - Connect to http://localhost:3232
111+
# - Connect to http://localhost:3232/mcp
112112
# - Navigate to the Auth tab
113113
# - The auth flow will redirect to :3001 for authentication
114114
# - After auth, tokens from :3001 will be used on :3232
@@ -117,26 +117,84 @@ npx -y @modelcontextprotocol/inspector
117117
### Architecture Diagrams
118118

119119
#### Integrated Mode
120-
```
121-
┌──────────┐ ┌───────────────────┐
122-
│ MCP │◀────▶│ MCP Server │
123-
│ Inspector│ │ (port 3232) │
124-
└──────────┘ │ │
125-
│ - OAuth Server │
126-
│ - Resource Server │
127-
└───────────────────┘
120+
```mermaid
121+
graph TD
122+
Client["MCP Client<br/>(Inspector)"]
123+
MCP["MCP Server<br/>(port 3232)<br/>• OAuth Server<br/>• Resource Server"]
124+
125+
Client <-->|"OAuth flow & MCP resources"| MCP
128126
```
129127

130128
#### Separate Mode
129+
```mermaid
130+
graph TD
131+
Client["MCP Client<br/>(Inspector)"]
132+
MCP["MCP Server<br/>(port 3232)<br/>Resource Server"]
133+
Auth["Auth Server<br/>(port 3001)<br/>OAuth Server"]
134+
135+
Client <-->|"1. Discover metadata"| MCP
136+
Client <-->|"2. OAuth flow<br/>(register, authorize, token)"| Auth
137+
Client <-->|"3. Use tokens for MCP resources"| MCP
138+
MCP <-->|"Token validation<br/>(introspect)"| Auth
139+
```
140+
141+
## OAuth Flow Analysis
142+
143+
### OAuth 2.0 + PKCE Flow Sequence
144+
145+
The server implements a complete OAuth 2.0 authorization code flow with PKCE. Here's how each step maps to data storage and expiry:
146+
147+
**1. Client Registration** (app setup - happens once)
148+
```
149+
App → Auth Server: "I want to use OAuth, here's my info"
150+
Auth Server → App: "OK, your client_id is XYZ, client_secret is ABC"
151+
```
152+
- **Storage**: Client credentials for future OAuth flows
153+
- **Expiry**: 30 days (long-lived app credentials)
154+
155+
**2. Authorization Request** (starts each OAuth flow)
156+
```
157+
User → App: "I want to connect to MCP server"
158+
App → Auth Server: "User wants access, here's my PKCE challenge"
159+
Auth Server: Stores pending authorization, shows auth page
131160
```
132-
┌──────────┐ ┌───────────────────┐ ┌─────────────────┐
133-
│ MCP │◀────▶│ MCP Server │◀────▶│ Auth Server │
134-
│ Inspector│ │ (port 3232) │ │ (port 3001) │
135-
└──────────┘ │ │ │ │
136-
│ │ - Resource Server │ │ - OAuth Server │
137-
└───────────▶│ │ │ │
138-
└───────────────────┘ └─────────────────┘
161+
- **Storage**: `PENDING_AUTHORIZATION` - temporary state during flow
162+
- **Expiry**: 10 minutes (short-lived temporary state)
163+
164+
**3. Authorization Code Exchange** (completes OAuth flow)
165+
```
166+
User → Auth Server: "I approve this app"
167+
Auth Server → App: "Here's your authorization code"
168+
App → Auth Server: "Exchange code + PKCE verifier for tokens"
169+
Auth Server → App: "Here are your access/refresh tokens"
170+
```
171+
- **Storage**: `TOKEN_EXCHANGE` - prevents replay attacks
172+
- **Expiry**: 10 minutes (single-use, consumed immediately)
173+
174+
**4. Token Storage** (long-term user session)
175+
```
176+
Auth Server: Issues access_token + refresh_token
177+
Server: Stores user installation with tokens
139178
```
179+
- **Storage**: `UPSTREAM_INSTALLATION` - the actual user session
180+
- **Expiry**: 7 days (balances security vs usability)
181+
182+
**5. Token Refresh** (extends user session)
183+
```
184+
App → Auth Server: "My access token expired, here's my refresh token"
185+
Auth Server → App: "Here's a new access token"
186+
```
187+
- **Storage**: `REFRESH_TOKEN` - mapping for token rotation
188+
- **Expiry**: 7 days (matches installation lifetime)
189+
190+
### Data Lifecycle Hierarchy
191+
192+
**Timeline (shortest to longest expiry):**
193+
1. **OAuth flow state** (10 minutes) - very temporary
194+
2. **User sessions** (7 days) - medium-term
195+
3. **Client credentials** (30 days) - long-term
196+
197+
This creates a logical hierarchy where each layer outlives the layers it supports.
140198

141199
## Installation
142200

auth-server/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ curl http://localhost:3001/.well-known/oauth-authorization-server
6969
1. Start this auth server: `npm run dev:auth-server`
7070
2. Start MCP server in separate mode: `AUTH_MODE=separate npm run dev`
7171
3. Open Inspector: `npx -y @modelcontextprotocol/inspector`
72-
4. Connect to `http://localhost:3232`
72+
4. Connect to `http://localhost:3232/mcp`
7373
5. Auth flow will redirect to this server (port 3001)
7474

7575
## Configuration

auth-server/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -142,5 +142,5 @@ app.listen(AUTH_SERVER_PORT, () => {
142142
console.log('💡 To test separate mode:');
143143
console.log(' 1. Keep this server running');
144144
console.log(' 2. In another terminal: AUTH_MODE=separate npm run dev');
145-
console.log(' 3. Connect Inspector to http://localhost:3232');
145+
console.log(' 3. Connect Inspector to http://localhost:3232/mcp');
146146
});

shared/redis-auth.ts

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,11 @@ export const REDIS_KEY_PREFIXES = {
2020
* Redis key expiry times in seconds
2121
*/
2222
export const REDIS_EXPIRY_TIMES = {
23-
PENDING_AUTHORIZATION: 10 * 60, // 10 minutes - authorization code -> PendingAuthorization
24-
TOKEN_EXCHANGE: 10 * 60, // 10 minutes - authorization code -> MCP access token
23+
CLIENT_REGISTRATION: 30 * 24 * 60 * 60, // 30 days - client app credentials
24+
PENDING_AUTHORIZATION: 10 * 60, // 10 minutes - authorization code -> PendingAuthorization
25+
TOKEN_EXCHANGE: 10 * 60, // 10 minutes - authorization code -> MCP access token
2526
UPSTREAM_INSTALLATION: 7 * 24 * 60 * 60, // 7 days - MCP access token -> UpstreamInstallation
26-
REFRESH_TOKEN: 7 * 24 * 60 * 60, // 7 days - MCP refresh token -> access token
27+
REFRESH_TOKEN: 7 * 24 * 60 * 60, // 7 days - MCP refresh token -> access token
2728
} as const;
2829

2930
/**
@@ -92,7 +93,8 @@ export async function saveClientRegistration(
9293
): Promise<void> {
9394
await redisClient.set(
9495
REDIS_KEY_PREFIXES.CLIENT_REGISTRATION + clientId,
95-
JSON.stringify(registration)
96+
JSON.stringify(registration),
97+
{ EX: REDIS_EXPIRY_TIMES.CLIENT_REGISTRATION }
9698
);
9799
}
98100

0 commit comments

Comments
 (0)