@mcp-guardian/server 0.4.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +105 -8
- package/dist/auth/auth-types.d.ts +40 -0
- package/dist/auth/auth-types.d.ts.map +1 -0
- package/dist/auth/auth-types.js +5 -0
- package/dist/auth/auth-types.js.map +1 -0
- package/dist/auth/dashboard-auth.d.ts +97 -0
- package/dist/auth/dashboard-auth.d.ts.map +1 -0
- package/dist/auth/dashboard-auth.js +319 -0
- package/dist/auth/dashboard-auth.js.map +1 -0
- package/dist/auth/dpop.d.ts +38 -0
- package/dist/auth/dpop.d.ts.map +1 -0
- package/dist/auth/dpop.js +72 -0
- package/dist/auth/dpop.js.map +1 -0
- package/dist/auth/oauth.d.ts +25 -0
- package/dist/auth/oauth.d.ts.map +1 -0
- package/dist/auth/oauth.js +96 -0
- package/dist/auth/oauth.js.map +1 -0
- package/dist/auth/redis-session-cache.d.ts +21 -0
- package/dist/auth/redis-session-cache.d.ts.map +1 -0
- package/dist/auth/redis-session-cache.js +74 -0
- package/dist/auth/redis-session-cache.js.map +1 -0
- package/dist/auth/session-cache.d.ts +47 -0
- package/dist/auth/session-cache.d.ts.map +1 -0
- package/dist/auth/session-cache.js +91 -0
- package/dist/auth/session-cache.js.map +1 -0
- package/dist/cli.js +23 -5
- package/dist/cli.js.map +1 -1
- package/dist/database/database-interface.d.ts +17 -0
- package/dist/database/database-interface.d.ts.map +1 -0
- package/dist/database/database-interface.js +2 -0
- package/dist/database/database-interface.js.map +1 -0
- package/dist/database/postgres-db.d.ts +18 -0
- package/dist/database/postgres-db.d.ts.map +1 -0
- package/dist/database/postgres-db.js +118 -0
- package/dist/database/postgres-db.js.map +1 -0
- package/dist/index.js +1 -1
- package/dist/policy/policy-watcher.d.ts +24 -0
- package/dist/policy/policy-watcher.d.ts.map +1 -0
- package/dist/policy/policy-watcher.js +68 -0
- package/dist/policy/policy-watcher.js.map +1 -0
- package/dist/policy/shell-tokenizer.d.ts +92 -0
- package/dist/policy/shell-tokenizer.d.ts.map +1 -0
- package/dist/policy/shell-tokenizer.js +300 -0
- package/dist/policy/shell-tokenizer.js.map +1 -0
- package/dist/proxy/http-proxy-server.d.ts +26 -0
- package/dist/proxy/http-proxy-server.d.ts.map +1 -0
- package/dist/proxy/http-proxy-server.js +172 -0
- package/dist/proxy/http-proxy-server.js.map +1 -0
- package/dist/proxy/proxy-manager.d.ts +3 -1
- package/dist/proxy/proxy-manager.d.ts.map +1 -1
- package/dist/proxy/proxy-manager.js +10 -3
- package/dist/proxy/proxy-manager.js.map +1 -1
- package/dist/proxy/proxy-server.d.ts +15 -8
- package/dist/proxy/proxy-server.d.ts.map +1 -1
- package/dist/proxy/proxy-server.js +80 -26
- package/dist/proxy/proxy-server.js.map +1 -1
- package/dist/utils/circuit-breaker.d.ts +29 -0
- package/dist/utils/circuit-breaker.d.ts.map +1 -0
- package/dist/utils/circuit-breaker.js +81 -0
- package/dist/utils/circuit-breaker.js.map +1 -0
- package/dist/utils/dashboard-server.d.ts +19 -0
- package/dist/utils/dashboard-server.d.ts.map +1 -0
- package/dist/utils/dashboard-server.js +258 -0
- package/dist/utils/dashboard-server.js.map +1 -0
- package/dist/utils/metrics.d.ts +17 -0
- package/dist/utils/metrics.d.ts.map +1 -0
- package/dist/utils/metrics.js +79 -0
- package/dist/utils/metrics.js.map +1 -0
- package/dist/utils/mtls-config.d.ts +27 -0
- package/dist/utils/mtls-config.d.ts.map +1 -0
- package/dist/utils/mtls-config.js +82 -0
- package/dist/utils/mtls-config.js.map +1 -0
- package/dist/utils/payload-normalizer.d.ts +62 -0
- package/dist/utils/payload-normalizer.d.ts.map +1 -0
- package/dist/utils/payload-normalizer.js +240 -0
- package/dist/utils/payload-normalizer.js.map +1 -0
- package/dist/utils/policy-auditor.d.ts +24 -0
- package/dist/utils/policy-auditor.d.ts.map +1 -0
- package/dist/utils/policy-auditor.js +58 -0
- package/dist/utils/policy-auditor.js.map +1 -0
- package/dist/utils/redis-rate-limiter.d.ts +22 -0
- package/dist/utils/redis-rate-limiter.d.ts.map +1 -0
- package/dist/utils/redis-rate-limiter.js +61 -0
- package/dist/utils/redis-rate-limiter.js.map +1 -0
- package/dist/utils/structured-logger.d.ts +1 -1
- package/dist/utils/structured-logger.d.ts.map +1 -1
- package/dist/utils/tracing.d.ts +7 -0
- package/dist/utils/tracing.d.ts.map +1 -0
- package/dist/utils/tracing.js +34 -0
- package/dist/utils/tracing.js.map +1 -0
- package/package.json +2 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dashboard-auth.js","sourceRoot":"","sources":["../../src/auth/dashboard-auth.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import * as jose from 'jose';
|
|
2
|
+
/**
|
|
3
|
+
* DPoP (Demonstrating Proof of Possession) — RFC 9449.
|
|
4
|
+
* Validates sender-constrained tokens to prevent token replay.
|
|
5
|
+
* The client must include a DPoP proof JWT in the DPoP header.
|
|
6
|
+
*/
|
|
7
|
+
export interface DPoPProof {
|
|
8
|
+
/** The access token hash (ath) claim */
|
|
9
|
+
ath?: string;
|
|
10
|
+
/** The HTTP method of the request */
|
|
11
|
+
htm: string;
|
|
12
|
+
/** The HTTP URI of the request */
|
|
13
|
+
htu: string;
|
|
14
|
+
/** Issued at (Unix timestamp) */
|
|
15
|
+
iat: number;
|
|
16
|
+
/** Unique JWT ID for replay detection */
|
|
17
|
+
jti: string;
|
|
18
|
+
}
|
|
19
|
+
export declare class DPoPValidator {
|
|
20
|
+
private usedNonces;
|
|
21
|
+
private readonly nonceTtlMs;
|
|
22
|
+
private lastCleanup;
|
|
23
|
+
constructor(nonceTtlMs?: number);
|
|
24
|
+
/**
|
|
25
|
+
* Validate a DPoP proof JWT.
|
|
26
|
+
* Checks: signature (JWK), htm, htu, iat freshness (60s window), ath (if access token provided), nonce replay.
|
|
27
|
+
*/
|
|
28
|
+
validate(proofToken: string, jwk: jose.JWK, httpMethod: string, httpUri: string, accessToken?: string): Promise<{
|
|
29
|
+
valid: boolean;
|
|
30
|
+
error?: string;
|
|
31
|
+
}>;
|
|
32
|
+
/**
|
|
33
|
+
* Compute the access token hash (ath) as per RFC 9449 §4.2.
|
|
34
|
+
* ath = base64url(sha256(access_token))
|
|
35
|
+
*/
|
|
36
|
+
private computeAth;
|
|
37
|
+
}
|
|
38
|
+
//# sourceMappingURL=dpop.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dpop.d.ts","sourceRoot":"","sources":["../../src/auth/dpop.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,IAAI,MAAM,MAAM,CAAC;AAG7B;;;;GAIG;AACH,MAAM,WAAW,SAAS;IACxB,wCAAwC;IACxC,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,qCAAqC;IACrC,GAAG,EAAE,MAAM,CAAC;IACZ,kCAAkC;IAClC,GAAG,EAAE,MAAM,CAAC;IACZ,iCAAiC;IACjC,GAAG,EAAE,MAAM,CAAC;IACZ,yCAAyC;IACzC,GAAG,EAAE,MAAM,CAAC;CACb;AAED,qBAAa,aAAa;IACxB,OAAO,CAAC,UAAU,CAA0B;IAC5C,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;IACpC,OAAO,CAAC,WAAW,CAAsB;gBAE7B,UAAU,GAAE,MAAuB;IAI/C;;;OAGG;IACG,QAAQ,CAAC,UAAU,EAAE,MAAM,EAAE,GAAG,EAAE,IAAI,CAAC,GAAG,EAAE,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,KAAK,EAAE,OAAO,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAyDzJ;;;OAGG;YACW,UAAU;CAIzB"}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import * as jose from 'jose';
|
|
2
|
+
import { Logger } from '../utils/logger.js';
|
|
3
|
+
export class DPoPValidator {
|
|
4
|
+
usedNonces = new Set();
|
|
5
|
+
nonceTtlMs;
|
|
6
|
+
lastCleanup = Date.now();
|
|
7
|
+
constructor(nonceTtlMs = 10 * 60 * 1000) {
|
|
8
|
+
this.nonceTtlMs = nonceTtlMs;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Validate a DPoP proof JWT.
|
|
12
|
+
* Checks: signature (JWK), htm, htu, iat freshness (60s window), ath (if access token provided), nonce replay.
|
|
13
|
+
*/
|
|
14
|
+
async validate(proofToken, jwk, httpMethod, httpUri, accessToken) {
|
|
15
|
+
try {
|
|
16
|
+
// Verify the proof JWT is signed by the client's private key matching the JWK
|
|
17
|
+
const publicKey = await jose.importJWK(jwk, 'ES256');
|
|
18
|
+
const { payload } = await jose.jwtVerify(proofToken, publicKey, {
|
|
19
|
+
algorithms: ['ES256', 'RS256', 'EdDSA'],
|
|
20
|
+
clockTolerance: 10,
|
|
21
|
+
});
|
|
22
|
+
const proof = payload;
|
|
23
|
+
// Validate htm (HTTP method)
|
|
24
|
+
if (proof.htm !== httpMethod.toUpperCase()) {
|
|
25
|
+
return { valid: false, error: `DPoP: htm mismatch (expected ${httpMethod.toUpperCase()}, got ${proof.htm})` };
|
|
26
|
+
}
|
|
27
|
+
// Validate htu (HTTP URI) — must match the request URI
|
|
28
|
+
if (proof.htu !== httpUri) {
|
|
29
|
+
return { valid: false, error: `DPoP: htu mismatch (expected ${httpUri}, got ${proof.htu})` };
|
|
30
|
+
}
|
|
31
|
+
// Validate iat freshness (must be within last 60 seconds)
|
|
32
|
+
const now = Math.floor(Date.now() / 1000);
|
|
33
|
+
if (proof.iat < now - 60) {
|
|
34
|
+
return { valid: false, error: 'DPoP: proof too old (iat > 60s ago)' };
|
|
35
|
+
}
|
|
36
|
+
if (proof.iat > now + 10) {
|
|
37
|
+
return { valid: false, error: 'DPoP: proof from the future' };
|
|
38
|
+
}
|
|
39
|
+
// Validate nonce (jti) for replay detection
|
|
40
|
+
if (this.usedNonces.has(proof.jti)) {
|
|
41
|
+
Logger.warn(`[dpop] Replay detected: jti ${proof.jti}`);
|
|
42
|
+
return { valid: false, error: 'DPoP: nonce already used (replay detected)' };
|
|
43
|
+
}
|
|
44
|
+
this.usedNonces.add(proof.jti);
|
|
45
|
+
// Validate ath (access token hash) if access token provided
|
|
46
|
+
if (accessToken && proof.ath) {
|
|
47
|
+
const expectedAth = await this.computeAth(accessToken);
|
|
48
|
+
if (proof.ath !== expectedAth) {
|
|
49
|
+
return { valid: false, error: 'DPoP: ath mismatch (access token hash does not match)' };
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
// Periodic cleanup of old nonces
|
|
53
|
+
if (Date.now() - this.lastCleanup > 60000) {
|
|
54
|
+
this.usedNonces.clear();
|
|
55
|
+
this.lastCleanup = Date.now();
|
|
56
|
+
}
|
|
57
|
+
return { valid: true };
|
|
58
|
+
}
|
|
59
|
+
catch (err) {
|
|
60
|
+
return { valid: false, error: `DPoP validation failed: ${err?.message}` };
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Compute the access token hash (ath) as per RFC 9449 §4.2.
|
|
65
|
+
* ath = base64url(sha256(access_token))
|
|
66
|
+
*/
|
|
67
|
+
async computeAth(accessToken) {
|
|
68
|
+
const digest = await crypto.subtle.digest('SHA-256', new TextEncoder().encode(accessToken));
|
|
69
|
+
return Buffer.from(digest).toString('base64url');
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
//# sourceMappingURL=dpop.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dpop.js","sourceRoot":"","sources":["../../src/auth/dpop.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { AuthConfig, AuthValidationResult, OIDCDiscovery } from './auth-types.js';
|
|
2
|
+
export declare class OAuthValidator {
|
|
3
|
+
private config;
|
|
4
|
+
private jwks;
|
|
5
|
+
private cachedDiscovery;
|
|
6
|
+
constructor(config: AuthConfig);
|
|
7
|
+
/**
|
|
8
|
+
* Perform OIDC discovery to fetch JWKS URI from issuer.
|
|
9
|
+
*/
|
|
10
|
+
discover(): Promise<OIDCDiscovery>;
|
|
11
|
+
/**
|
|
12
|
+
* Initialize JWKS from discovery or explicit URI.
|
|
13
|
+
*/
|
|
14
|
+
init(): Promise<void>;
|
|
15
|
+
/**
|
|
16
|
+
* Validate a JWT bearer token and extract agent identity.
|
|
17
|
+
*/
|
|
18
|
+
validate(token: string): Promise<AuthValidationResult>;
|
|
19
|
+
/**
|
|
20
|
+
* Extract Bearer token from Authorization header.
|
|
21
|
+
*/
|
|
22
|
+
static extractToken(authorizationHeader?: string): string | null;
|
|
23
|
+
getConfig(): AuthConfig;
|
|
24
|
+
}
|
|
25
|
+
//# sourceMappingURL=oauth.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"oauth.d.ts","sourceRoot":"","sources":["../../src/auth/oauth.ts"],"names":[],"mappings":"AASA,OAAO,EAAE,UAAU,EAAE,oBAAoB,EAAiB,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAGjG,qBAAa,cAAc;IACzB,OAAO,CAAC,MAAM,CAAa;IAC3B,OAAO,CAAC,IAAI,CAA2D;IACvE,OAAO,CAAC,eAAe,CAA8B;gBAEzC,MAAM,EAAE,UAAU;IAI9B;;OAEG;IACG,QAAQ,IAAI,OAAO,CAAC,aAAa,CAAC;IAiBxC;;OAEG;IACG,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAS3B;;OAEG;IACG,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,oBAAoB,CAAC;IAkC5D;;OAEG;IACH,MAAM,CAAC,YAAY,CAAC,mBAAmB,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI;IAMhE,SAAS,IAAI,UAAU;CAGxB"}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OAuth 2.1 / OIDC JWT Validator for MCP Guardian proxy.
|
|
3
|
+
*
|
|
4
|
+
* Validates bearer tokens from MCP requests against an OIDC provider.
|
|
5
|
+
* Uses OIDC Discovery (RFC 8414) to auto-configure JWKS endpoint.
|
|
6
|
+
* Supports Client Credentials flow (most common for server-to-agent MCP).
|
|
7
|
+
*/
|
|
8
|
+
import * as jose from 'jose';
|
|
9
|
+
import { StructuredLogger } from '../utils/structured-logger.js';
|
|
10
|
+
export class OAuthValidator {
|
|
11
|
+
config;
|
|
12
|
+
jwks = null;
|
|
13
|
+
cachedDiscovery = null;
|
|
14
|
+
constructor(config) {
|
|
15
|
+
this.config = config;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Perform OIDC discovery to fetch JWKS URI from issuer.
|
|
19
|
+
*/
|
|
20
|
+
async discover() {
|
|
21
|
+
if (this.cachedDiscovery)
|
|
22
|
+
return this.cachedDiscovery;
|
|
23
|
+
const discoveryUrl = `${this.config.issuer}/.well-known/openid-configuration`;
|
|
24
|
+
try {
|
|
25
|
+
const res = await fetch(discoveryUrl);
|
|
26
|
+
if (!res.ok)
|
|
27
|
+
throw new Error(`OIDC discovery failed: HTTP ${res.status}`);
|
|
28
|
+
const meta = (await res.json());
|
|
29
|
+
this.cachedDiscovery = meta;
|
|
30
|
+
StructuredLogger.info({ event: 'oidc_discovery', issuer: this.config.issuer, jwks_uri: meta.jwks_uri });
|
|
31
|
+
return meta;
|
|
32
|
+
}
|
|
33
|
+
catch (err) {
|
|
34
|
+
StructuredLogger.logError({ event: 'oidc_discovery_error', serverName: 'oauth', error: `Failed to discover OIDC config: ${err?.message}` });
|
|
35
|
+
throw err;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Initialize JWKS from discovery or explicit URI.
|
|
40
|
+
*/
|
|
41
|
+
async init() {
|
|
42
|
+
let jwksUri = this.config.jwksUri;
|
|
43
|
+
if (!jwksUri) {
|
|
44
|
+
const discovery = await this.discover();
|
|
45
|
+
jwksUri = discovery.jwks_uri;
|
|
46
|
+
}
|
|
47
|
+
this.jwks = jose.createRemoteJWKSet(new URL(jwksUri));
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Validate a JWT bearer token and extract agent identity.
|
|
51
|
+
*/
|
|
52
|
+
async validate(token) {
|
|
53
|
+
if (!this.jwks) {
|
|
54
|
+
try {
|
|
55
|
+
await this.init();
|
|
56
|
+
}
|
|
57
|
+
catch (err) {
|
|
58
|
+
return { valid: false, error: `Auth provider unreachable: ${err?.message}` };
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
if (!this.jwks) {
|
|
62
|
+
return { valid: false, error: 'JWKS not initialized' };
|
|
63
|
+
}
|
|
64
|
+
try {
|
|
65
|
+
const { payload } = await jose.jwtVerify(token, this.jwks, {
|
|
66
|
+
issuer: this.config.issuer,
|
|
67
|
+
audience: this.config.audience,
|
|
68
|
+
clockTolerance: this.config.clockTolerance || 30,
|
|
69
|
+
});
|
|
70
|
+
const identity = {
|
|
71
|
+
sub: payload.sub || 'unknown',
|
|
72
|
+
clientId: payload.client_id || payload.azp,
|
|
73
|
+
scopes: payload.scope ? String(payload.scope).split(' ') : undefined,
|
|
74
|
+
issuer: payload.iss || this.config.issuer,
|
|
75
|
+
expiresAt: payload.exp ? payload.exp * 1000 : undefined,
|
|
76
|
+
};
|
|
77
|
+
return { valid: true, identity };
|
|
78
|
+
}
|
|
79
|
+
catch (err) {
|
|
80
|
+
return { valid: false, error: `JWT validation failed: ${err?.message}` };
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Extract Bearer token from Authorization header.
|
|
85
|
+
*/
|
|
86
|
+
static extractToken(authorizationHeader) {
|
|
87
|
+
if (!authorizationHeader)
|
|
88
|
+
return null;
|
|
89
|
+
const match = authorizationHeader.match(/^Bearer\s+(.+)$/i);
|
|
90
|
+
return match ? match[1] : null;
|
|
91
|
+
}
|
|
92
|
+
getConfig() {
|
|
93
|
+
return this.config;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
//# sourceMappingURL=oauth.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"oauth.js","sourceRoot":"","sources":["../../src/auth/oauth.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { AgentIdentity } from './auth-types.js';
|
|
2
|
+
import { SessionCache, SessionEntry } from './session-cache.js';
|
|
3
|
+
/**
|
|
4
|
+
* Redis-backed session cache for multi-replica HA deployments.
|
|
5
|
+
* Extends SessionCache to use Redis instead of in-memory Maps.
|
|
6
|
+
* Enable with: REDIS_URL=redis://localhost:6379
|
|
7
|
+
*/
|
|
8
|
+
export declare class RedisSessionCache extends SessionCache {
|
|
9
|
+
private redis;
|
|
10
|
+
private readonly prefix;
|
|
11
|
+
private readonly noncePrefix;
|
|
12
|
+
constructor(sessionTtlMs?: number, nonceTtlMs?: number);
|
|
13
|
+
createSession(identity: AgentIdentity, jwtNonce?: string): SessionEntry;
|
|
14
|
+
validateSession(token: string): AgentIdentity | null;
|
|
15
|
+
validateSessionAsync(token: string): Promise<AgentIdentity | null>;
|
|
16
|
+
isNonceUsedAsync(nonce: string): Promise<boolean>;
|
|
17
|
+
revokeSessionAsync(token: string): Promise<void>;
|
|
18
|
+
cleanup(): Promise<void>;
|
|
19
|
+
close(): Promise<void>;
|
|
20
|
+
}
|
|
21
|
+
//# sourceMappingURL=redis-session-cache.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"redis-session-cache.d.ts","sourceRoot":"","sources":["../../src/auth/redis-session-cache.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChD,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAGhE;;;;GAIG;AACH,qBAAa,iBAAkB,SAAQ,YAAY;IACjD,OAAO,CAAC,KAAK,CAAQ;IACrB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAA2B;IAClD,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAyB;gBAEzC,YAAY,GAAE,MAAsB,EAAE,UAAU,GAAE,MAAuB;IAU5E,aAAa,CAAC,QAAQ,EAAE,aAAa,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,YAAY;IAqBvE,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,aAAa,GAAG,IAAI;IAUvD,oBAAoB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC;IAelE,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAKjD,kBAAkB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAIhD,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAIxB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;CAG7B"}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { Redis } from 'ioredis';
|
|
2
|
+
import { SessionCache } from './session-cache.js';
|
|
3
|
+
import { Logger } from '../utils/logger.js';
|
|
4
|
+
/**
|
|
5
|
+
* Redis-backed session cache for multi-replica HA deployments.
|
|
6
|
+
* Extends SessionCache to use Redis instead of in-memory Maps.
|
|
7
|
+
* Enable with: REDIS_URL=redis://localhost:6379
|
|
8
|
+
*/
|
|
9
|
+
export class RedisSessionCache extends SessionCache {
|
|
10
|
+
redis;
|
|
11
|
+
prefix = 'mcp_guardian:session:';
|
|
12
|
+
noncePrefix = 'mcp_guardian:nonce:';
|
|
13
|
+
constructor(sessionTtlMs = 5 * 60 * 1000, nonceTtlMs = 10 * 60 * 1000) {
|
|
14
|
+
super(sessionTtlMs, nonceTtlMs);
|
|
15
|
+
const redisUrl = process.env['REDIS_URL'] || 'redis://localhost:6379';
|
|
16
|
+
this.redis = new Redis(redisUrl, {
|
|
17
|
+
maxRetriesPerRequest: 3,
|
|
18
|
+
lazyConnect: false,
|
|
19
|
+
});
|
|
20
|
+
Logger.info(`[redis-session-cache] Connected to ${redisUrl}`);
|
|
21
|
+
}
|
|
22
|
+
createSession(identity, jwtNonce) {
|
|
23
|
+
const entry = super.createSession(identity, jwtNonce);
|
|
24
|
+
// Store in Redis with TTL
|
|
25
|
+
const ttlSeconds = Math.ceil((entry.expiresAt - Date.now()) / 1000);
|
|
26
|
+
this.redis.setex(`${this.prefix}${entry.token}`, ttlSeconds, JSON.stringify(entry)).catch(err => Logger.error(`[redis-session-cache] Failed to store session: ${err?.message}`));
|
|
27
|
+
// Store nonce with longer TTL
|
|
28
|
+
if (entry.nonce) {
|
|
29
|
+
const nonceTtlSeconds = Math.ceil(this.sessionTtlMs / 1000) * 2;
|
|
30
|
+
this.redis.setex(`${this.noncePrefix}${entry.nonce}`, nonceTtlSeconds, '1')
|
|
31
|
+
.catch(err => Logger.error(`[redis-session-cache] Failed to store nonce: ${err?.message}`));
|
|
32
|
+
}
|
|
33
|
+
return entry;
|
|
34
|
+
}
|
|
35
|
+
validateSession(token) {
|
|
36
|
+
// Check local cache first, then Redis
|
|
37
|
+
const local = super.validateSession(token);
|
|
38
|
+
if (local)
|
|
39
|
+
return local;
|
|
40
|
+
// Fallback to Redis for cross-replica sessions
|
|
41
|
+
// Note: async validateSession would require refactoring proxy-server
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
async validateSessionAsync(token) {
|
|
45
|
+
const raw = await this.redis.get(`${this.prefix}${token}`);
|
|
46
|
+
if (!raw)
|
|
47
|
+
return null;
|
|
48
|
+
try {
|
|
49
|
+
const entry = JSON.parse(raw);
|
|
50
|
+
if (Date.now() > entry.expiresAt) {
|
|
51
|
+
this.redis.del(`${this.prefix}${token}`);
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
return entry.identity;
|
|
55
|
+
}
|
|
56
|
+
catch {
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
async isNonceUsedAsync(nonce) {
|
|
61
|
+
const exists = await this.redis.exists(`${this.noncePrefix}${nonce}`);
|
|
62
|
+
return exists === 1;
|
|
63
|
+
}
|
|
64
|
+
async revokeSessionAsync(token) {
|
|
65
|
+
await this.redis.del(`${this.prefix}${token}`);
|
|
66
|
+
}
|
|
67
|
+
async cleanup() {
|
|
68
|
+
// Redis handles expiry via TTL — no manual cleanup needed
|
|
69
|
+
}
|
|
70
|
+
async close() {
|
|
71
|
+
await this.redis.quit();
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
//# sourceMappingURL=redis-session-cache.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"redis-session-cache.js","sourceRoot":"","sources":["../../src/auth/redis-session-cache.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { AgentIdentity } from './auth-types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Session cache for replay protection.
|
|
4
|
+
* After a JWT is validated once, a short-lived session token is issued.
|
|
5
|
+
* Subsequent calls must include this session token, not the raw JWT.
|
|
6
|
+
* This prevents replay of captured JWTs within their expiry window.
|
|
7
|
+
*
|
|
8
|
+
* In production, replace with Redis for multi-replica HA.
|
|
9
|
+
*/
|
|
10
|
+
export interface SessionEntry {
|
|
11
|
+
token: string;
|
|
12
|
+
identity: AgentIdentity;
|
|
13
|
+
nonce: string;
|
|
14
|
+
createdAt: number;
|
|
15
|
+
expiresAt: number;
|
|
16
|
+
}
|
|
17
|
+
export declare class SessionCache {
|
|
18
|
+
private sessions;
|
|
19
|
+
private usedNonces;
|
|
20
|
+
protected readonly sessionTtlMs: number;
|
|
21
|
+
protected readonly nonceTtlMs: number;
|
|
22
|
+
constructor(sessionTtlMs?: number, nonceTtlMs?: number);
|
|
23
|
+
/**
|
|
24
|
+
* Create a session after successful JWT validation.
|
|
25
|
+
* Returns a session token the client must use for subsequent calls.
|
|
26
|
+
* The JWT cannot be replayed because:
|
|
27
|
+
* 1. We track used nonces (jti or sub+iat)
|
|
28
|
+
* 2. We issue a session token with a short (5min) TTL
|
|
29
|
+
*/
|
|
30
|
+
createSession(identity: AgentIdentity, jwtNonce?: string): SessionEntry;
|
|
31
|
+
/**
|
|
32
|
+
* Validate a session token.
|
|
33
|
+
* Returns the agent identity if valid, null if expired/not found.
|
|
34
|
+
*/
|
|
35
|
+
validateSession(token: string): AgentIdentity | null;
|
|
36
|
+
/**
|
|
37
|
+
* Check if a JWT nonce has been used (replay detection).
|
|
38
|
+
*/
|
|
39
|
+
isNonceUsed(nonce: string): boolean;
|
|
40
|
+
/**
|
|
41
|
+
* Revoke a session (e.g., on logout or suspicious activity).
|
|
42
|
+
*/
|
|
43
|
+
revokeSession(token: string): void;
|
|
44
|
+
protected cleanup(): void;
|
|
45
|
+
get size(): number;
|
|
46
|
+
}
|
|
47
|
+
//# sourceMappingURL=session-cache.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session-cache.d.ts","sourceRoot":"","sources":["../../src/auth/session-cache.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAGhD;;;;;;;GAOG;AAEH,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,aAAa,CAAC;IACxB,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,qBAAa,YAAY;IACvB,OAAO,CAAC,QAAQ,CAAwC;IACxD,OAAO,CAAC,UAAU,CAA0B;IAC5C,SAAS,CAAC,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IACxC,SAAS,CAAC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;gBAE1B,YAAY,GAAE,MAAsB,EAAE,UAAU,GAAE,MAAuB;IAOrF;;;;;;OAMG;IACH,aAAa,CAAC,QAAQ,EAAE,aAAa,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,YAAY;IAuBvE;;;OAGG;IACH,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,aAAa,GAAG,IAAI;IAUpD;;OAEG;IACH,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO;IAInC;;OAEG;IACH,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAIlC,SAAS,CAAC,OAAO,IAAI,IAAI;IAqBzB,IAAI,IAAI,IAAI,MAAM,CAEjB;CACF"}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { randomUUID } from 'crypto';
|
|
2
|
+
import { Logger } from '../utils/logger.js';
|
|
3
|
+
export class SessionCache {
|
|
4
|
+
sessions = new Map();
|
|
5
|
+
usedNonces = new Set();
|
|
6
|
+
sessionTtlMs;
|
|
7
|
+
nonceTtlMs;
|
|
8
|
+
constructor(sessionTtlMs = 5 * 60 * 1000, nonceTtlMs = 10 * 60 * 1000) {
|
|
9
|
+
this.sessionTtlMs = sessionTtlMs;
|
|
10
|
+
this.nonceTtlMs = nonceTtlMs;
|
|
11
|
+
// Cleanup expired entries every 60s
|
|
12
|
+
setInterval(() => this.cleanup(), 60000);
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Create a session after successful JWT validation.
|
|
16
|
+
* Returns a session token the client must use for subsequent calls.
|
|
17
|
+
* The JWT cannot be replayed because:
|
|
18
|
+
* 1. We track used nonces (jti or sub+iat)
|
|
19
|
+
* 2. We issue a session token with a short (5min) TTL
|
|
20
|
+
*/
|
|
21
|
+
createSession(identity, jwtNonce) {
|
|
22
|
+
const nonce = jwtNonce || `${identity.sub}:${Date.now()}:${randomUUID()}`;
|
|
23
|
+
// Prevent nonce replay
|
|
24
|
+
if (this.usedNonces.has(nonce)) {
|
|
25
|
+
Logger.warn(`[session-cache] Replay detected: nonce ${nonce}`);
|
|
26
|
+
}
|
|
27
|
+
this.usedNonces.add(nonce);
|
|
28
|
+
const token = `mcp_guardian_session_${randomUUID()}`;
|
|
29
|
+
const now = Date.now();
|
|
30
|
+
const entry = {
|
|
31
|
+
token,
|
|
32
|
+
identity,
|
|
33
|
+
nonce,
|
|
34
|
+
createdAt: now,
|
|
35
|
+
expiresAt: now + this.sessionTtlMs,
|
|
36
|
+
};
|
|
37
|
+
this.sessions.set(token, entry);
|
|
38
|
+
return entry;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Validate a session token.
|
|
42
|
+
* Returns the agent identity if valid, null if expired/not found.
|
|
43
|
+
*/
|
|
44
|
+
validateSession(token) {
|
|
45
|
+
const entry = this.sessions.get(token);
|
|
46
|
+
if (!entry)
|
|
47
|
+
return null;
|
|
48
|
+
if (Date.now() > entry.expiresAt) {
|
|
49
|
+
this.sessions.delete(token);
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
return entry.identity;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Check if a JWT nonce has been used (replay detection).
|
|
56
|
+
*/
|
|
57
|
+
isNonceUsed(nonce) {
|
|
58
|
+
return this.usedNonces.has(nonce);
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Revoke a session (e.g., on logout or suspicious activity).
|
|
62
|
+
*/
|
|
63
|
+
revokeSession(token) {
|
|
64
|
+
this.sessions.delete(token);
|
|
65
|
+
}
|
|
66
|
+
cleanup() {
|
|
67
|
+
const now = Date.now();
|
|
68
|
+
// Clean expired sessions
|
|
69
|
+
for (const [token, entry] of this.sessions) {
|
|
70
|
+
if (now > entry.expiresAt) {
|
|
71
|
+
this.sessions.delete(token);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
// Clean expired nonces (keep for nonceTtlMs to detect replays)
|
|
75
|
+
// This is simplified — in production, use a time-sorted structure
|
|
76
|
+
if (this.usedNonces.size > 10000) {
|
|
77
|
+
// Full sweep
|
|
78
|
+
const entries = Array.from(this.sessions.values());
|
|
79
|
+
const validTokens = new Set(entries.map(e => e.token));
|
|
80
|
+
for (const token of this.sessions.keys()) {
|
|
81
|
+
if (!validTokens.has(token))
|
|
82
|
+
this.sessions.delete(token);
|
|
83
|
+
}
|
|
84
|
+
this.usedNonces.clear();
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
get size() {
|
|
88
|
+
return this.sessions.size;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
//# sourceMappingURL=session-cache.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session-cache.js","sourceRoot":"","sources":["../../src/auth/session-cache.ts"],"names":[],"mappings":"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"}
|
package/dist/cli.js
CHANGED
|
@@ -7,6 +7,7 @@ import { ReportGenerator } from './reporter/report-generator.js';
|
|
|
7
7
|
import { calculateOverallScore } from './utils/scoring.js';
|
|
8
8
|
import { ProxyManager } from './proxy/proxy-manager.js';
|
|
9
9
|
import { PolicyEngine } from './policy/policy-engine.js';
|
|
10
|
+
import { OAuthValidator } from './auth/oauth.js';
|
|
10
11
|
import { createContainer } from './container.js';
|
|
11
12
|
// ── Shared helpers ────────────────────────────────────────────────────
|
|
12
13
|
function loadConfigs(options) {
|
|
@@ -40,7 +41,7 @@ const program = new Command();
|
|
|
40
41
|
program
|
|
41
42
|
.name('mcp-guardian')
|
|
42
43
|
.description('Security, cost, and health audit for MCP infrastructure')
|
|
43
|
-
.version('0.
|
|
44
|
+
.version('0.5.0');
|
|
44
45
|
program
|
|
45
46
|
.command('scan')
|
|
46
47
|
.description('Run security scan on MCP servers')
|
|
@@ -184,10 +185,13 @@ program
|
|
|
184
185
|
});
|
|
185
186
|
program
|
|
186
187
|
.command('proxy')
|
|
187
|
-
.description('Start MCP Guardian proxy
|
|
188
|
+
.description('Start MCP Guardian proxy with optional OAuth 2.1 authentication and active policy enforcement')
|
|
188
189
|
.option('-c, --config <path>', 'Path to MCP config file')
|
|
189
190
|
.option('--policy <path>', 'Path to policy YAML file (enables active blocking)')
|
|
190
191
|
.option('--blocking-mode <mode>', 'Override policy mode: audit (passive), warn (flag), block (enforce)', 'block')
|
|
192
|
+
.option('--auth-issuer <url>', 'OIDC issuer URL for JWT validation (e.g., https://accounts.google.com)')
|
|
193
|
+
.option('--auth-audience <aud>', 'Expected audience claim in JWT')
|
|
194
|
+
.option('--auth-required', 'Require authentication for all tool calls (fail-closed)', false)
|
|
191
195
|
.action(async (opts) => {
|
|
192
196
|
const paths = opts.config ? [opts.config] : ConfigParser.findConfigPaths();
|
|
193
197
|
if (paths.length === 0) {
|
|
@@ -199,6 +203,21 @@ program
|
|
|
199
203
|
console.error(chalk.yellow('No servers found in config.'));
|
|
200
204
|
process.exit(0);
|
|
201
205
|
}
|
|
206
|
+
// Configure OAuth 2.1 if --auth-issuer provided
|
|
207
|
+
let authValidator;
|
|
208
|
+
if (opts.authIssuer) {
|
|
209
|
+
if (!opts.authAudience) {
|
|
210
|
+
console.error(chalk.red('--auth-audience is required when --auth-issuer is set'));
|
|
211
|
+
process.exit(1);
|
|
212
|
+
}
|
|
213
|
+
const authConfig = {
|
|
214
|
+
issuer: opts.authIssuer,
|
|
215
|
+
audience: opts.authAudience,
|
|
216
|
+
required: opts.authRequired || false,
|
|
217
|
+
};
|
|
218
|
+
authValidator = new OAuthValidator(authConfig);
|
|
219
|
+
console.error(chalk.green(`OAuth 2.1 enabled: ${authConfig.issuer} (audience: ${authConfig.audience})${authConfig.required ? ' [REQUIRED]' : ' [OPTIONAL]'}`));
|
|
220
|
+
}
|
|
202
221
|
// Load policy config if --policy flag provided
|
|
203
222
|
let policyEngine;
|
|
204
223
|
if (opts.policy) {
|
|
@@ -207,7 +226,6 @@ program
|
|
|
207
226
|
const { load } = await import('js-yaml');
|
|
208
227
|
const policyYaml = readFileSync(opts.policy, 'utf-8');
|
|
209
228
|
const policyConfig = load(policyYaml);
|
|
210
|
-
// Override mode from CLI flag if specified
|
|
211
229
|
if (opts.blockingMode && ['audit', 'warn', 'block'].includes(opts.blockingMode)) {
|
|
212
230
|
policyConfig.policy.mode = opts.blockingMode;
|
|
213
231
|
}
|
|
@@ -221,10 +239,10 @@ program
|
|
|
221
239
|
}
|
|
222
240
|
}
|
|
223
241
|
else {
|
|
224
|
-
console.error(chalk.dim('No policy file specified — running in audit-only mode
|
|
242
|
+
console.error(chalk.dim('No policy file specified — running in audit-only mode'));
|
|
225
243
|
}
|
|
226
244
|
const db = new HistoryDatabase();
|
|
227
|
-
const manager = new ProxyManager(db, policyEngine);
|
|
245
|
+
const manager = new ProxyManager(db, policyEngine, authValidator);
|
|
228
246
|
await manager.startAll(servers);
|
|
229
247
|
console.error(chalk.green('MCP Guardian proxy running. Press Ctrl+C to stop.'));
|
|
230
248
|
const cleanup = () => { manager.stopAll(); db.close(); process.exit(0); };
|