Implement Redis namespace isolation for auth and MCP keys

This commit is contained in:
Basil Hosmer 2025-09-15 02:33:59 -04:00
commit 34fe20efc4
No known key found for this signature in database
4 changed files with 35 additions and 9 deletions

View file

@ -520,6 +520,8 @@ The server is designed for horizontal scaling using Redis as the backbone:
- **Automatic Cleanup**: No explicit cleanup required
#### Redis Key Structure
##### MCP Session Keys
```
session:{sessionId}:owner # Session ownership
mcp:shttp:toserver:{sessionId} # Client→Server messages
@ -527,6 +529,17 @@ mcp:shttp:toclient:{sessionId}:{requestId} # Server→Client responses
mcp:control:{sessionId} # Control messages
```
##### OAuth/Auth Keys
```
auth:client:{clientId} # OAuth client registrations
auth:pending:{authCode} # Pending authorizations
auth:installation:{accessToken} # Active MCP installations
auth:exch:{authCode} # Token exchanges
auth:refresh:{refreshToken} # Refresh tokens
```
Note: The `auth:` prefix ensures complete namespace isolation between auth and MCP functions in both integrated and separate modes.
### Transport Methods
#### Streamable HTTP (Recommended)

View file

@ -127,6 +127,7 @@ Redis stores session ownership information using a structured key system.
### Redis Key Structure
#### MCP Session Keys (MCP Server)
```
session:{sessionId}:owner → userId # Session ownership
mcp:shttp:toserver:{sessionId} → [pub/sub channel] # Client→Server messages (also indicates liveness)
@ -134,6 +135,17 @@ mcp:shttp:toclient:{sessionId}:{requestId} → [pub/sub channel] # Server→Clie
mcp:control:{sessionId} → [pub/sub channel] # Control messages
```
#### Auth Keys (Auth Server)
```
auth:client:{clientId} → client registration # OAuth client registrations
auth:pending:{authCode} → pending authorization # Pending auth (10 min TTL)
auth:installation:{accessToken} → MCP installation # Active sessions (7 days TTL)
auth:exch:{authCode} → token exchange # Token exchange (10 min TTL)
auth:refresh:{refreshToken} → access token # Refresh tokens (7 days TTL)
```
Note: The `auth:` prefix ensures complete isolation from MCP session keys, allowing both integrated and separate modes to work consistently.
### Redis Operations
| Operation | Key Pattern | Value | Purpose |

View file

@ -7,13 +7,14 @@ import { logger } from "../src/utils/logger.js";
/**
* Redis key prefixes for different data types
* All auth-related keys use "auth:" prefix to avoid collision with MCP session keys
*/
export const REDIS_KEY_PREFIXES = {
CLIENT_REGISTRATION: "client:",
PENDING_AUTHORIZATION: "pending:",
MCP_AUTHORIZATION: "mcp:",
TOKEN_EXCHANGE: "exch:",
REFRESH_TOKEN: "refresh:",
CLIENT_REGISTRATION: "auth:client:",
PENDING_AUTHORIZATION: "auth:pending:",
MCP_AUTHORIZATION: "auth:installation:", // Changed from "mcp:" to avoid collision
TOKEN_EXCHANGE: "auth:exch:",
REFRESH_TOKEN: "auth:refresh:",
} as const;
/**

View file

@ -124,8 +124,8 @@ describe("auth utils", () => {
// instead of using exchangeToken which changes the value
await saveTokenExchange(authCode, tokenExchange);
// Get the key used by saveTokenExchange
const key = "exch:" + crypto.createHash("sha256").update(authCode).digest("hex");
// Get the key used by saveTokenExchange (now with auth: prefix)
const key = "auth:exch:" + crypto.createHash("sha256").update(authCode).digest("hex");
// Get the encrypted data
const encryptedData = await mockRedis.get(key);
@ -266,8 +266,8 @@ describe("auth utils", () => {
await revokeMcpInstallation(accessToken);
// Should have called getDel with the correct key
expect(getDel).toHaveBeenCalledWith(expect.stringContaining("mcp:"));
// Should have called getDel with the correct key (now auth:installation:)
expect(getDel).toHaveBeenCalledWith(expect.stringContaining("auth:installation:"));
});