Implement Redis namespace isolation for auth and MCP keys
This commit is contained in:
parent
4a081bc816
commit
34fe20efc4
4 changed files with 35 additions and 9 deletions
13
README.md
13
README.md
|
|
@ -520,6 +520,8 @@ The server is designed for horizontal scaling using Redis as the backbone:
|
||||||
- **Automatic Cleanup**: No explicit cleanup required
|
- **Automatic Cleanup**: No explicit cleanup required
|
||||||
|
|
||||||
#### Redis Key Structure
|
#### Redis Key Structure
|
||||||
|
|
||||||
|
##### MCP Session Keys
|
||||||
```
|
```
|
||||||
session:{sessionId}:owner # Session ownership
|
session:{sessionId}:owner # Session ownership
|
||||||
mcp:shttp:toserver:{sessionId} # Client→Server messages
|
mcp:shttp:toserver:{sessionId} # Client→Server messages
|
||||||
|
|
@ -527,6 +529,17 @@ mcp:shttp:toclient:{sessionId}:{requestId} # Server→Client responses
|
||||||
mcp:control:{sessionId} # Control messages
|
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
|
### Transport Methods
|
||||||
|
|
||||||
#### Streamable HTTP (Recommended)
|
#### Streamable HTTP (Recommended)
|
||||||
|
|
|
||||||
|
|
@ -127,6 +127,7 @@ Redis stores session ownership information using a structured key system.
|
||||||
|
|
||||||
### Redis Key Structure
|
### Redis Key Structure
|
||||||
|
|
||||||
|
#### MCP Session Keys (MCP Server)
|
||||||
```
|
```
|
||||||
session:{sessionId}:owner → userId # Session ownership
|
session:{sessionId}:owner → userId # Session ownership
|
||||||
mcp:shttp:toserver:{sessionId} → [pub/sub channel] # Client→Server messages (also indicates liveness)
|
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
|
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
|
### Redis Operations
|
||||||
|
|
||||||
| Operation | Key Pattern | Value | Purpose |
|
| Operation | Key Pattern | Value | Purpose |
|
||||||
|
|
|
||||||
|
|
@ -7,13 +7,14 @@ import { logger } from "../src/utils/logger.js";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Redis key prefixes for different data types
|
* 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 = {
|
export const REDIS_KEY_PREFIXES = {
|
||||||
CLIENT_REGISTRATION: "client:",
|
CLIENT_REGISTRATION: "auth:client:",
|
||||||
PENDING_AUTHORIZATION: "pending:",
|
PENDING_AUTHORIZATION: "auth:pending:",
|
||||||
MCP_AUTHORIZATION: "mcp:",
|
MCP_AUTHORIZATION: "auth:installation:", // Changed from "mcp:" to avoid collision
|
||||||
TOKEN_EXCHANGE: "exch:",
|
TOKEN_EXCHANGE: "auth:exch:",
|
||||||
REFRESH_TOKEN: "refresh:",
|
REFRESH_TOKEN: "auth:refresh:",
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -124,8 +124,8 @@ describe("auth utils", () => {
|
||||||
// instead of using exchangeToken which changes the value
|
// instead of using exchangeToken which changes the value
|
||||||
await saveTokenExchange(authCode, tokenExchange);
|
await saveTokenExchange(authCode, tokenExchange);
|
||||||
|
|
||||||
// Get the key used by saveTokenExchange
|
// Get the key used by saveTokenExchange (now with auth: prefix)
|
||||||
const key = "exch:" + crypto.createHash("sha256").update(authCode).digest("hex");
|
const key = "auth:exch:" + crypto.createHash("sha256").update(authCode).digest("hex");
|
||||||
|
|
||||||
// Get the encrypted data
|
// Get the encrypted data
|
||||||
const encryptedData = await mockRedis.get(key);
|
const encryptedData = await mockRedis.get(key);
|
||||||
|
|
@ -266,8 +266,8 @@ describe("auth utils", () => {
|
||||||
|
|
||||||
await revokeMcpInstallation(accessToken);
|
await revokeMcpInstallation(accessToken);
|
||||||
|
|
||||||
// Should have called getDel with the correct key
|
// Should have called getDel with the correct key (now auth:installation:)
|
||||||
expect(getDel).toHaveBeenCalledWith(expect.stringContaining("mcp:"));
|
expect(getDel).toHaveBeenCalledWith(expect.stringContaining("auth:installation:"));
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue