@mcp-guardian/server 0.3.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 +362 -136
- 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 +48 -3
- 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-engine.d.ts +19 -0
- package/dist/policy/policy-engine.d.ts.map +1 -0
- package/dist/policy/policy-engine.js +87 -0
- package/dist/policy/policy-engine.js.map +1 -0
- package/dist/policy/policy-types.d.ts +42 -0
- package/dist/policy/policy-types.d.ts.map +1 -0
- package/dist/policy/policy-types.js +5 -0
- package/dist/policy/policy-types.js.map +1 -0
- 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 +5 -1
- package/dist/proxy/proxy-manager.d.ts.map +1 -1
- package/dist/proxy/proxy-manager.js +12 -3
- package/dist/proxy/proxy-manager.js.map +1 -1
- package/dist/proxy/proxy-server.d.ts +20 -5
- package/dist/proxy/proxy-server.d.ts.map +1 -1
- package/dist/proxy/proxy-server.js +126 -9
- 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 +47 -0
- package/dist/utils/structured-logger.d.ts.map +1 -0
- package/dist/utils/structured-logger.js +48 -0
- package/dist/utils/structured-logger.js.map +1 -0
- 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 +14 -8
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OAuth 2.1 / OIDC authentication types for MCP Guardian proxy.
|
|
3
|
+
*/
|
|
4
|
+
export interface AuthConfig {
|
|
5
|
+
/** OIDC issuer URL (e.g., https://accounts.google.com) */
|
|
6
|
+
issuer: string;
|
|
7
|
+
/** Expected audience claim in JWT */
|
|
8
|
+
audience: string;
|
|
9
|
+
/** Whether authentication is required (fail-closed) or optional (fail-open) */
|
|
10
|
+
required: boolean;
|
|
11
|
+
/** JWKS URI override (default: auto-discovered from issuer) */
|
|
12
|
+
jwksUri?: string;
|
|
13
|
+
/** Clock tolerance in seconds for JWT validation */
|
|
14
|
+
clockTolerance?: number;
|
|
15
|
+
}
|
|
16
|
+
export interface AgentIdentity {
|
|
17
|
+
/** Subject claim (sub) — unique agent identifier */
|
|
18
|
+
sub: string;
|
|
19
|
+
/** Client ID from the token */
|
|
20
|
+
clientId?: string;
|
|
21
|
+
/** Scopes granted to this agent */
|
|
22
|
+
scopes?: string[];
|
|
23
|
+
/** Issuer of the token */
|
|
24
|
+
issuer: string;
|
|
25
|
+
/** Token expiry timestamp */
|
|
26
|
+
expiresAt?: number;
|
|
27
|
+
}
|
|
28
|
+
export interface AuthValidationResult {
|
|
29
|
+
valid: boolean;
|
|
30
|
+
identity?: AgentIdentity;
|
|
31
|
+
error?: string;
|
|
32
|
+
}
|
|
33
|
+
export interface OIDCDiscovery {
|
|
34
|
+
issuer: string;
|
|
35
|
+
jwks_uri: string;
|
|
36
|
+
authorization_endpoint?: string;
|
|
37
|
+
token_endpoint?: string;
|
|
38
|
+
scopes_supported?: string[];
|
|
39
|
+
}
|
|
40
|
+
//# sourceMappingURL=auth-types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth-types.d.ts","sourceRoot":"","sources":["../../src/auth/auth-types.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,MAAM,WAAW,UAAU;IACzB,0DAA0D;IAC1D,MAAM,EAAE,MAAM,CAAC;IACf,qCAAqC;IACrC,QAAQ,EAAE,MAAM,CAAC;IACjB,+EAA+E;IAC/E,QAAQ,EAAE,OAAO,CAAC;IAClB,+DAA+D;IAC/D,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,oDAAoD;IACpD,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,aAAa;IAC5B,oDAAoD;IACpD,GAAG,EAAE,MAAM,CAAC;IACZ,+BAA+B;IAC/B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,mCAAmC;IACnC,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,0BAA0B;IAC1B,MAAM,EAAE,MAAM,CAAC;IACf,6BAA6B;IAC7B,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,oBAAoB;IACnC,KAAK,EAAE,OAAO,CAAC;IACf,QAAQ,CAAC,EAAE,aAAa,CAAC;IACzB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,sBAAsB,CAAC,EAAE,MAAM,CAAC;IAChC,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,gBAAgB,CAAC,EAAE,MAAM,EAAE,CAAC;CAC7B"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth-types.js","sourceRoot":"","sources":["../../src/auth/auth-types.ts"],"names":[],"mappings":"AAAA;;GAEG"}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
export interface AuthResult {
|
|
2
|
+
authenticated: boolean;
|
|
3
|
+
reason?: string;
|
|
4
|
+
identity?: string;
|
|
5
|
+
}
|
|
6
|
+
export interface DashboardAuthConfig {
|
|
7
|
+
/** Enable authentication on dashboard API */
|
|
8
|
+
enabled: boolean;
|
|
9
|
+
/** Pre-shared API key (simplest auth) */
|
|
10
|
+
apiKey?: string;
|
|
11
|
+
/** JWT HMAC secret for session tokens */
|
|
12
|
+
jwtSecret?: string;
|
|
13
|
+
/** Session token expiry in seconds */
|
|
14
|
+
sessionTtlSeconds: number;
|
|
15
|
+
/** Allowed origins for CORS/CSRF validation */
|
|
16
|
+
allowedOrigins: string[];
|
|
17
|
+
/** Maximum login attempts per minute per IP */
|
|
18
|
+
maxLoginAttemptsPerMinute: number;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* DashboardAuth provides authentication for the dashboard HTTP server.
|
|
22
|
+
*
|
|
23
|
+
* Two modes:
|
|
24
|
+
* 1. API Key: Set DASHBOARD_API_KEY, pass as ?api_key=<key> or Authorization: Bearer <key>
|
|
25
|
+
* 2. JWT Sessions: Set DASHBOARD_JWT_SECRET, POST /api/login with credentials
|
|
26
|
+
*/
|
|
27
|
+
export declare class DashboardAuth {
|
|
28
|
+
private config;
|
|
29
|
+
private loginRateMap;
|
|
30
|
+
private activeTokens;
|
|
31
|
+
private cleanupInterval;
|
|
32
|
+
constructor(config?: Partial<DashboardAuthConfig>);
|
|
33
|
+
/**
|
|
34
|
+
* Authenticate a dashboard HTTP request.
|
|
35
|
+
* Checks multiple sources:
|
|
36
|
+
* 1. ?api_key=<key> query parameter
|
|
37
|
+
* 2. Authorization: Bearer <token> header
|
|
38
|
+
* 3. X-API-Key: <key> header
|
|
39
|
+
*/
|
|
40
|
+
authenticate(req: {
|
|
41
|
+
url?: string;
|
|
42
|
+
headers?: Record<string, string | string[] | undefined>;
|
|
43
|
+
method?: string;
|
|
44
|
+
}): AuthResult;
|
|
45
|
+
/**
|
|
46
|
+
* Handle a login attempt. Creates a session token if credentials are valid.
|
|
47
|
+
* Credentials are validated against DASHBOARD_USERNAME / DASHBOARD_PASSWORD env vars.
|
|
48
|
+
*/
|
|
49
|
+
login(req: {
|
|
50
|
+
url?: string;
|
|
51
|
+
headers?: Record<string, string | string[] | undefined>;
|
|
52
|
+
body?: {
|
|
53
|
+
username?: string;
|
|
54
|
+
password?: string;
|
|
55
|
+
api_key?: string;
|
|
56
|
+
};
|
|
57
|
+
ip?: string;
|
|
58
|
+
}): {
|
|
59
|
+
success: boolean;
|
|
60
|
+
token?: string;
|
|
61
|
+
error?: string;
|
|
62
|
+
};
|
|
63
|
+
/**
|
|
64
|
+
* Revoke a session token (logout).
|
|
65
|
+
*/
|
|
66
|
+
logout(token: string): void;
|
|
67
|
+
/**
|
|
68
|
+
* Generate login page HTML (serves at /login when JWT auth is enabled).
|
|
69
|
+
*/
|
|
70
|
+
getLoginPageHtml(error?: string): string;
|
|
71
|
+
/**
|
|
72
|
+
* Check if auth is enabled and required.
|
|
73
|
+
*/
|
|
74
|
+
isEnabled(): boolean;
|
|
75
|
+
/**
|
|
76
|
+
* Check if JWT session-based auth is configured (vs API key only).
|
|
77
|
+
*/
|
|
78
|
+
hasJwtSessionAuth(): boolean;
|
|
79
|
+
/**
|
|
80
|
+
* Create a signed HMAC session token.
|
|
81
|
+
*/
|
|
82
|
+
private createSessionToken;
|
|
83
|
+
/**
|
|
84
|
+
* Timing-safe string comparison to prevent timing attacks on API keys.
|
|
85
|
+
*/
|
|
86
|
+
private timingSafeCompare;
|
|
87
|
+
/**
|
|
88
|
+
* Validate CSRF protection via Origin/Referer headers.
|
|
89
|
+
*/
|
|
90
|
+
private validateCsrf;
|
|
91
|
+
private isAllowedOrigin;
|
|
92
|
+
private checkLoginRate;
|
|
93
|
+
private normalizeHeaders;
|
|
94
|
+
private cleanupRateMap;
|
|
95
|
+
dispose(): void;
|
|
96
|
+
}
|
|
97
|
+
//# sourceMappingURL=dashboard-auth.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dashboard-auth.d.ts","sourceRoot":"","sources":["../../src/auth/dashboard-auth.ts"],"names":[],"mappings":"AAmBA,MAAM,WAAW,UAAU;IACzB,aAAa,EAAE,OAAO,CAAC;IACvB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,mBAAmB;IAClC,6CAA6C;IAC7C,OAAO,EAAE,OAAO,CAAC;IACjB,yCAAyC;IACzC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,yCAAyC;IACzC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,sCAAsC;IACtC,iBAAiB,EAAE,MAAM,CAAC;IAC1B,+CAA+C;IAC/C,cAAc,EAAE,MAAM,EAAE,CAAC;IACzB,+CAA+C;IAC/C,yBAAyB,EAAE,MAAM,CAAC;CACnC;AAUD;;;;;;GAMG;AACH,qBAAa,aAAa;IACxB,OAAO,CAAC,MAAM,CAAsB;IACpC,OAAO,CAAC,YAAY,CAA0C;IAC9D,OAAO,CAAC,YAAY,CAA0B;IAC9C,OAAO,CAAC,eAAe,CAA+C;gBAE1D,MAAM,CAAC,EAAE,OAAO,CAAC,mBAAmB,CAAC;IA4BjD;;;;;;OAMG;IACH,YAAY,CAAC,GAAG,EAAE;QAChB,GAAG,CAAC,EAAE,MAAM,CAAC;QACb,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,SAAS,CAAC,CAAC;QACxD,MAAM,CAAC,EAAE,MAAM,CAAC;KACjB,GAAG,UAAU;IAyDd;;;OAGG;IACH,KAAK,CAAC,GAAG,EAAE;QACT,GAAG,CAAC,EAAE,MAAM,CAAC;QACb,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,SAAS,CAAC,CAAC;QACxD,IAAI,CAAC,EAAE;YAAE,QAAQ,CAAC,EAAE,MAAM,CAAC;YAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;YAAC,OAAO,CAAC,EAAE,MAAM,CAAA;SAAE,CAAC;QAClE,EAAE,CAAC,EAAE,MAAM,CAAC;KACb,GAAG;QAAE,OAAO,EAAE,OAAO,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE;IAuDxD;;OAEG;IACH,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAI3B;;OAEG;IACH,gBAAgB,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM;IAyCxC;;OAEG;IACH,SAAS,IAAI,OAAO;IAIpB;;OAEG;IACH,iBAAiB,IAAI,OAAO;IAI5B;;OAEG;IACH,OAAO,CAAC,kBAAkB;IAmB1B;;OAEG;IACH,OAAO,CAAC,iBAAiB;IAOzB;;OAEG;IACH,OAAO,CAAC,YAAY;IA0BpB,OAAO,CAAC,eAAe;IAQvB,OAAO,CAAC,cAAc;IAYtB,OAAO,CAAC,gBAAgB;IASxB,OAAO,CAAC,cAAc;IAOtB,OAAO,IAAI,IAAI;CAKhB"}
|
|
@@ -0,0 +1,319 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dashboard Authentication Middleware
|
|
3
|
+
*
|
|
4
|
+
* Provides JWT-based authentication for the dashboard HTTP API.
|
|
5
|
+
* Supports:
|
|
6
|
+
* - API key authentication (simple, internal deployments)
|
|
7
|
+
* - JWT session tokens (for multi-user deployments)
|
|
8
|
+
* - CSRF protection via Origin/Referer validation
|
|
9
|
+
* - Rate limiting on auth endpoints
|
|
10
|
+
* - Login endpoint with configurable credential source
|
|
11
|
+
*
|
|
12
|
+
* Enable with: DASHBOARD_AUTH_ENABLED=true
|
|
13
|
+
* Configure API key: DASHBOARD_API_KEY=<key>
|
|
14
|
+
* Configure JWT secret: DASHBOARD_JWT_SECRET=<secret>
|
|
15
|
+
*/
|
|
16
|
+
import { createHmac, randomBytes, timingSafeEqual } from 'crypto';
|
|
17
|
+
import { Logger } from '../utils/logger.js';
|
|
18
|
+
import { StructuredLogger } from '../utils/structured-logger.js';
|
|
19
|
+
/**
|
|
20
|
+
* DashboardAuth provides authentication for the dashboard HTTP server.
|
|
21
|
+
*
|
|
22
|
+
* Two modes:
|
|
23
|
+
* 1. API Key: Set DASHBOARD_API_KEY, pass as ?api_key=<key> or Authorization: Bearer <key>
|
|
24
|
+
* 2. JWT Sessions: Set DASHBOARD_JWT_SECRET, POST /api/login with credentials
|
|
25
|
+
*/
|
|
26
|
+
export class DashboardAuth {
|
|
27
|
+
config;
|
|
28
|
+
loginRateMap = new Map();
|
|
29
|
+
activeTokens = new Set();
|
|
30
|
+
cleanupInterval = null;
|
|
31
|
+
constructor(config) {
|
|
32
|
+
const enabled = process.env['DASHBOARD_AUTH_ENABLED'] === 'true';
|
|
33
|
+
this.config = {
|
|
34
|
+
enabled: config?.enabled ?? enabled,
|
|
35
|
+
apiKey: config?.apiKey ?? process.env['DASHBOARD_API_KEY'] ?? undefined,
|
|
36
|
+
jwtSecret: config?.jwtSecret ?? process.env['DASHBOARD_JWT_SECRET'] ?? undefined,
|
|
37
|
+
sessionTtlSeconds: config?.sessionTtlSeconds ?? 3600,
|
|
38
|
+
allowedOrigins: config?.allowedOrigins ?? (process.env['DASHBOARD_ALLOWED_ORIGINS']
|
|
39
|
+
? process.env['DASHBOARD_ALLOWED_ORIGINS'].split(',').map(s => s.trim())
|
|
40
|
+
: ['http://localhost:4000', 'http://localhost:3000', 'http://127.0.0.1:4000']),
|
|
41
|
+
maxLoginAttemptsPerMinute: config?.maxLoginAttemptsPerMinute ?? 5,
|
|
42
|
+
};
|
|
43
|
+
if (this.config.enabled && this.config.apiKey) {
|
|
44
|
+
Logger.info('[dashboard-auth] API key authentication enabled');
|
|
45
|
+
}
|
|
46
|
+
else if (this.config.enabled && this.config.jwtSecret) {
|
|
47
|
+
Logger.info('[dashboard-auth] JWT session authentication enabled');
|
|
48
|
+
}
|
|
49
|
+
else if (this.config.enabled) {
|
|
50
|
+
Logger.warn('[dashboard-auth] Auth enabled but no API key or JWT secret configured');
|
|
51
|
+
}
|
|
52
|
+
// Periodic cleanup of rate limit entries
|
|
53
|
+
if (this.config.enabled) {
|
|
54
|
+
this.cleanupInterval = setInterval(() => this.cleanupRateMap(), 60000);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Authenticate a dashboard HTTP request.
|
|
59
|
+
* Checks multiple sources:
|
|
60
|
+
* 1. ?api_key=<key> query parameter
|
|
61
|
+
* 2. Authorization: Bearer <token> header
|
|
62
|
+
* 3. X-API-Key: <key> header
|
|
63
|
+
*/
|
|
64
|
+
authenticate(req) {
|
|
65
|
+
if (!this.config.enabled) {
|
|
66
|
+
return { authenticated: true, identity: 'anonymous' };
|
|
67
|
+
}
|
|
68
|
+
const url = req.url || '/';
|
|
69
|
+
const headers = this.normalizeHeaders(req.headers || {});
|
|
70
|
+
// ── CSRF check for mutating requests ──
|
|
71
|
+
if (req.method && ['POST', 'PUT', 'DELETE', 'PATCH'].includes(req.method)) {
|
|
72
|
+
const csrfResult = this.validateCsrf(headers);
|
|
73
|
+
if (!csrfResult.authenticated)
|
|
74
|
+
return csrfResult;
|
|
75
|
+
}
|
|
76
|
+
// ── Check query param API key ──
|
|
77
|
+
try {
|
|
78
|
+
const urlObj = new URL(url, 'http://localhost');
|
|
79
|
+
const queryKey = urlObj.searchParams.get('api_key');
|
|
80
|
+
if (queryKey && this.config.apiKey) {
|
|
81
|
+
if (this.timingSafeCompare(queryKey, this.config.apiKey)) {
|
|
82
|
+
return { authenticated: true, identity: 'api_key' };
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
catch {
|
|
87
|
+
// Malformed URL — continue to other auth methods
|
|
88
|
+
}
|
|
89
|
+
// ── Check Authorization header ──
|
|
90
|
+
const authHeader = headers['authorization'];
|
|
91
|
+
if (authHeader) {
|
|
92
|
+
const bearerMatch = authHeader.match(/^Bearer\s+(.+)$/i);
|
|
93
|
+
if (bearerMatch) {
|
|
94
|
+
const token = bearerMatch[1];
|
|
95
|
+
// Check if it's the API key
|
|
96
|
+
if (this.config.apiKey && this.timingSafeCompare(token, this.config.apiKey)) {
|
|
97
|
+
return { authenticated: true, identity: 'api_key' };
|
|
98
|
+
}
|
|
99
|
+
// Check if it's a valid session token
|
|
100
|
+
if (this.activeTokens.has(token)) {
|
|
101
|
+
return { authenticated: true, identity: 'session' };
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
// ── Check X-API-Key header ──
|
|
106
|
+
const apiKeyHeader = headers['x-api-key'];
|
|
107
|
+
if (apiKeyHeader && this.config.apiKey) {
|
|
108
|
+
if (this.timingSafeCompare(apiKeyHeader, this.config.apiKey)) {
|
|
109
|
+
return { authenticated: true, identity: 'api_key' };
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
return { authenticated: false, reason: 'No valid authentication provided' };
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Handle a login attempt. Creates a session token if credentials are valid.
|
|
116
|
+
* Credentials are validated against DASHBOARD_USERNAME / DASHBOARD_PASSWORD env vars.
|
|
117
|
+
*/
|
|
118
|
+
login(req) {
|
|
119
|
+
if (!this.config.enabled || !this.config.jwtSecret) {
|
|
120
|
+
return { success: false, error: 'JWT auth not configured. Set DASHBOARD_JWT_SECRET.' };
|
|
121
|
+
}
|
|
122
|
+
// ── Rate limit login attempts ──
|
|
123
|
+
const ip = req.ip || 'unknown';
|
|
124
|
+
if (!this.checkLoginRate(ip)) {
|
|
125
|
+
StructuredLogger.info({
|
|
126
|
+
event: 'dashboard_login_rate_limited',
|
|
127
|
+
ip,
|
|
128
|
+
});
|
|
129
|
+
return { success: false, error: 'Too many login attempts. Try again later.' };
|
|
130
|
+
}
|
|
131
|
+
const body = req.body || {};
|
|
132
|
+
// Check API key shortcut
|
|
133
|
+
if (body.api_key && this.config.apiKey && this.timingSafeCompare(body.api_key, this.config.apiKey)) {
|
|
134
|
+
const token = this.createSessionToken();
|
|
135
|
+
Logger.info(`[dashboard-auth] Login via API key from ${ip}`);
|
|
136
|
+
return { success: true, token };
|
|
137
|
+
}
|
|
138
|
+
// Check username/password
|
|
139
|
+
const expectedUsername = process.env['DASHBOARD_USERNAME'];
|
|
140
|
+
const expectedPassword = process.env['DASHBOARD_PASSWORD'];
|
|
141
|
+
if (!expectedUsername || !expectedPassword) {
|
|
142
|
+
return { success: false, error: 'Login credentials not configured on server. Set DASHBOARD_USERNAME and DASHBOARD_PASSWORD.' };
|
|
143
|
+
}
|
|
144
|
+
if (body.username === expectedUsername &&
|
|
145
|
+
body.password &&
|
|
146
|
+
this.timingSafeCompare(body.password, expectedPassword)) {
|
|
147
|
+
const token = this.createSessionToken();
|
|
148
|
+
StructuredLogger.info({
|
|
149
|
+
event: 'dashboard_login',
|
|
150
|
+
ip,
|
|
151
|
+
identity: body.username,
|
|
152
|
+
});
|
|
153
|
+
return { success: true, token };
|
|
154
|
+
}
|
|
155
|
+
StructuredLogger.info({
|
|
156
|
+
event: 'dashboard_login_failed',
|
|
157
|
+
ip,
|
|
158
|
+
identity: body.username || 'unknown',
|
|
159
|
+
});
|
|
160
|
+
return { success: false, error: 'Invalid credentials' };
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Revoke a session token (logout).
|
|
164
|
+
*/
|
|
165
|
+
logout(token) {
|
|
166
|
+
this.activeTokens.delete(token);
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* Generate login page HTML (serves at /login when JWT auth is enabled).
|
|
170
|
+
*/
|
|
171
|
+
getLoginPageHtml(error) {
|
|
172
|
+
const errorHtml = error ? `<div style="color:#f85149;margin-bottom:16px;padding:8px;background:#3d1f1f;border-radius:6px;">${error}</div>` : '';
|
|
173
|
+
return `<!DOCTYPE html>
|
|
174
|
+
<html lang="en">
|
|
175
|
+
<head>
|
|
176
|
+
<meta charset="UTF-8">
|
|
177
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
178
|
+
<title>MCP Guardian — Login</title>
|
|
179
|
+
<style>
|
|
180
|
+
* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
181
|
+
body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, sans-serif; background: #0d1117; color: #c9d1d9; display: flex; justify-content: center; align-items: center; min-height: 100vh; }
|
|
182
|
+
.container { background: #161b22; border: 1px solid #30363d; border-radius: 8px; padding: 32px; width: 100%; max-width: 400px; }
|
|
183
|
+
h1 { font-size: 20px; color: #58a6ff; margin-bottom: 8px; }
|
|
184
|
+
h2 { font-size: 14px; color: #8b949e; margin-bottom: 24px; }
|
|
185
|
+
label { display: block; font-size: 13px; color: #8b949e; margin-bottom: 4px; margin-top: 12px; }
|
|
186
|
+
input { width: 100%; padding: 8px 12px; background: #0d1117; border: 1px solid #30363d; border-radius: 6px; color: #c9d1d9; font-size: 14px; }
|
|
187
|
+
input:focus { outline: none; border-color: #58a6ff; }
|
|
188
|
+
button { width: 100%; padding: 10px; background: #238636; color: #fff; border: none; border-radius: 6px; font-size: 14px; cursor: pointer; margin-top: 20px; }
|
|
189
|
+
button:hover { background: #2ea043; }
|
|
190
|
+
.footer { font-size: 12px; color: #8b949e; margin-top: 16px; text-align: center; }
|
|
191
|
+
</style>
|
|
192
|
+
</head>
|
|
193
|
+
<body>
|
|
194
|
+
<div class="container">
|
|
195
|
+
<h1>🛡️ MCP Guardian</h1>
|
|
196
|
+
<h2>Dashboard Authentication</h2>
|
|
197
|
+
${errorHtml}
|
|
198
|
+
<form method="POST" action="/api/login">
|
|
199
|
+
<label for="username">Username</label>
|
|
200
|
+
<input type="text" id="username" name="username" required autofocus>
|
|
201
|
+
<label for="password">Password</label>
|
|
202
|
+
<input type="password" id="password" name="password" required>
|
|
203
|
+
<button type="submit">Sign In</button>
|
|
204
|
+
</form>
|
|
205
|
+
<div class="footer">Internal deployment — authorized access only</div>
|
|
206
|
+
</div>
|
|
207
|
+
</body>
|
|
208
|
+
</html>`;
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
211
|
+
* Check if auth is enabled and required.
|
|
212
|
+
*/
|
|
213
|
+
isEnabled() {
|
|
214
|
+
return this.config.enabled && !!(this.config.apiKey || this.config.jwtSecret);
|
|
215
|
+
}
|
|
216
|
+
/**
|
|
217
|
+
* Check if JWT session-based auth is configured (vs API key only).
|
|
218
|
+
*/
|
|
219
|
+
hasJwtSessionAuth() {
|
|
220
|
+
return this.config.enabled && !!this.config.jwtSecret;
|
|
221
|
+
}
|
|
222
|
+
/**
|
|
223
|
+
* Create a signed HMAC session token.
|
|
224
|
+
*/
|
|
225
|
+
createSessionToken() {
|
|
226
|
+
const payload = Buffer.from(JSON.stringify({
|
|
227
|
+
iat: Math.floor(Date.now() / 1000),
|
|
228
|
+
jti: randomBytes(16).toString('hex'),
|
|
229
|
+
})).toString('base64url');
|
|
230
|
+
const signature = createHmac('sha256', this.config.jwtSecret || randomBytes(32).toString('hex'))
|
|
231
|
+
.update(payload)
|
|
232
|
+
.digest('base64url');
|
|
233
|
+
const token = `${payload}.${signature}`;
|
|
234
|
+
this.activeTokens.add(token);
|
|
235
|
+
// Auto-expire after TTL
|
|
236
|
+
setTimeout(() => this.activeTokens.delete(token), this.config.sessionTtlSeconds * 1000);
|
|
237
|
+
return token;
|
|
238
|
+
}
|
|
239
|
+
/**
|
|
240
|
+
* Timing-safe string comparison to prevent timing attacks on API keys.
|
|
241
|
+
*/
|
|
242
|
+
timingSafeCompare(a, b) {
|
|
243
|
+
if (a.length !== b.length)
|
|
244
|
+
return false;
|
|
245
|
+
const bufA = Buffer.from(a, 'utf-8');
|
|
246
|
+
const bufB = Buffer.from(b, 'utf-8');
|
|
247
|
+
return timingSafeEqual(bufA, bufB);
|
|
248
|
+
}
|
|
249
|
+
/**
|
|
250
|
+
* Validate CSRF protection via Origin/Referer headers.
|
|
251
|
+
*/
|
|
252
|
+
validateCsrf(headers) {
|
|
253
|
+
const origin = headers['origin'];
|
|
254
|
+
const referer = headers['referer'];
|
|
255
|
+
// If both are missing and we're strict, could block
|
|
256
|
+
// For now, only validate when present
|
|
257
|
+
if (origin) {
|
|
258
|
+
if (!this.isAllowedOrigin(origin)) {
|
|
259
|
+
return { authenticated: false, reason: `Origin '${origin}' not allowed` };
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
if (referer) {
|
|
263
|
+
try {
|
|
264
|
+
const refererOrigin = new URL(referer).origin;
|
|
265
|
+
if (!this.isAllowedOrigin(refererOrigin)) {
|
|
266
|
+
return { authenticated: false, reason: `Referer origin '${refererOrigin}' not allowed` };
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
catch {
|
|
270
|
+
// Malformed referer — allow through
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
return { authenticated: true };
|
|
274
|
+
}
|
|
275
|
+
isAllowedOrigin(origin) {
|
|
276
|
+
return this.config.allowedOrigins.some(allowed => {
|
|
277
|
+
if (allowed === '*')
|
|
278
|
+
return true;
|
|
279
|
+
if (allowed === origin)
|
|
280
|
+
return true;
|
|
281
|
+
return false;
|
|
282
|
+
});
|
|
283
|
+
}
|
|
284
|
+
checkLoginRate(ip) {
|
|
285
|
+
const now = Date.now();
|
|
286
|
+
let entry = this.loginRateMap.get(ip);
|
|
287
|
+
if (!entry || now > entry.resetAt) {
|
|
288
|
+
entry = { count: 1, resetAt: now + 60000 };
|
|
289
|
+
this.loginRateMap.set(ip, entry);
|
|
290
|
+
return true;
|
|
291
|
+
}
|
|
292
|
+
entry.count++;
|
|
293
|
+
return entry.count <= this.config.maxLoginAttemptsPerMinute;
|
|
294
|
+
}
|
|
295
|
+
normalizeHeaders(headers) {
|
|
296
|
+
const result = {};
|
|
297
|
+
for (const [key, value] of Object.entries(headers)) {
|
|
298
|
+
if (Array.isArray(value))
|
|
299
|
+
result[key.toLowerCase()] = value[0] || '';
|
|
300
|
+
else if (value !== undefined)
|
|
301
|
+
result[key.toLowerCase()] = value;
|
|
302
|
+
}
|
|
303
|
+
return result;
|
|
304
|
+
}
|
|
305
|
+
cleanupRateMap() {
|
|
306
|
+
const now = Date.now();
|
|
307
|
+
for (const [ip, entry] of this.loginRateMap) {
|
|
308
|
+
if (now > entry.resetAt)
|
|
309
|
+
this.loginRateMap.delete(ip);
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
dispose() {
|
|
313
|
+
if (this.cleanupInterval)
|
|
314
|
+
clearInterval(this.cleanupInterval);
|
|
315
|
+
this.activeTokens.clear();
|
|
316
|
+
this.loginRateMap.clear();
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
//# sourceMappingURL=dashboard-auth.js.map
|
|
@@ -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"}
|