sub-bridge 1.0.0 → 1.0.3
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/.github/workflows/npm-publish.yml +3 -2
- package/.release-please-manifest.json +1 -1
- package/CHANGELOG.md +16 -0
- package/dist/auth/provider.d.ts +1 -0
- package/dist/auth/provider.d.ts.map +1 -1
- package/dist/auth/provider.js +22 -0
- package/dist/auth/provider.js.map +1 -1
- package/dist/cli.js +0 -14
- package/dist/cli.js.map +1 -1
- package/dist/oauth/authorize.d.ts +73 -0
- package/dist/oauth/authorize.d.ts.map +1 -0
- package/dist/oauth/authorize.js +197 -0
- package/dist/oauth/authorize.js.map +1 -0
- package/dist/oauth/crypto.d.ts +58 -0
- package/dist/oauth/crypto.d.ts.map +1 -0
- package/dist/oauth/crypto.js +170 -0
- package/dist/oauth/crypto.js.map +1 -0
- package/dist/oauth/dcr.d.ts +44 -0
- package/dist/oauth/dcr.d.ts.map +1 -0
- package/dist/oauth/dcr.js +84 -0
- package/dist/oauth/dcr.js.map +1 -0
- package/dist/oauth/metadata.d.ts +23 -0
- package/dist/oauth/metadata.d.ts.map +1 -0
- package/dist/oauth/metadata.js +29 -0
- package/dist/oauth/metadata.js.map +1 -0
- package/dist/oauth/token.d.ts +29 -0
- package/dist/oauth/token.d.ts.map +1 -0
- package/dist/oauth/token.js +117 -0
- package/dist/oauth/token.js.map +1 -0
- package/dist/routes/chat.d.ts.map +1 -1
- package/dist/routes/chat.js +128 -15
- package/dist/routes/chat.js.map +1 -1
- package/dist/routes/oauth.d.ts +13 -0
- package/dist/routes/oauth.d.ts.map +1 -0
- package/dist/routes/oauth.js +174 -0
- package/dist/routes/oauth.js.map +1 -0
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +14 -6
- package/dist/server.js.map +1 -1
- package/dist/tunnel/providers/cloudflare.d.ts.map +1 -1
- package/dist/tunnel/providers/cloudflare.js +19 -5
- package/dist/tunnel/providers/cloudflare.js.map +1 -1
- package/dist/tunnel/registry.d.ts.map +1 -1
- package/dist/tunnel/registry.js +75 -9
- package/dist/tunnel/registry.js.map +1 -1
- package/dist/utils/cloudflared-config.d.ts +2 -0
- package/dist/utils/cloudflared-config.d.ts.map +1 -0
- package/dist/utils/cloudflared-config.js +116 -0
- package/dist/utils/cloudflared-config.js.map +1 -0
- package/dist/utils/setup-instructions.d.ts.map +1 -1
- package/dist/utils/setup-instructions.js +6 -9
- package/dist/utils/setup-instructions.js.map +1 -1
- package/index.html +268 -281
- package/package.json +6 -2
- package/src/cli.ts +0 -14
- package/src/routes/chat.ts +3 -0
- package/src/server.ts +10 -6
- package/src/tunnel/providers/cloudflare.ts +18 -5
- package/src/tunnel/registry.ts +79 -8
- package/src/utils/cloudflared-config.ts +121 -0
- package/src/utils/setup-instructions.ts +6 -9
- package/dist/auth/oauth-flow.d.ts +0 -24
- package/dist/auth/oauth-flow.d.ts.map +0 -1
- package/dist/auth/oauth-flow.js +0 -184
- package/dist/auth/oauth-flow.js.map +0 -1
- package/dist/auth/oauth-manager.d.ts +0 -13
- package/dist/auth/oauth-manager.d.ts.map +0 -1
- package/dist/auth/oauth-manager.js +0 -25
- package/dist/auth/oauth-manager.js.map +0 -1
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.getServerSecret = getServerSecret;
|
|
7
|
+
exports.encrypt = encrypt;
|
|
8
|
+
exports.decrypt = decrypt;
|
|
9
|
+
exports.createAccessToken = createAccessToken;
|
|
10
|
+
exports.decodeAccessToken = decodeAccessToken;
|
|
11
|
+
exports.generateAuthCode = generateAuthCode;
|
|
12
|
+
exports.generateClientId = generateClientId;
|
|
13
|
+
exports.generateClientSecret = generateClientSecret;
|
|
14
|
+
/**
|
|
15
|
+
* Cryptographic utilities for MCP OAuth token encryption
|
|
16
|
+
*
|
|
17
|
+
* Uses AES-256-GCM for authenticated encryption of upstream credentials.
|
|
18
|
+
* Server secret is generated on first run and stored locally.
|
|
19
|
+
*/
|
|
20
|
+
const node_crypto_1 = __importDefault(require("node:crypto"));
|
|
21
|
+
const node_fs_1 = __importDefault(require("node:fs"));
|
|
22
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
23
|
+
const node_os_1 = __importDefault(require("node:os"));
|
|
24
|
+
// ============================================================================
|
|
25
|
+
// Configuration
|
|
26
|
+
// ============================================================================
|
|
27
|
+
const SECRET_DIR = node_path_1.default.join(node_os_1.default.homedir(), '.sub-bridge');
|
|
28
|
+
const SECRET_FILE = node_path_1.default.join(SECRET_DIR, 'secret.key');
|
|
29
|
+
const ALGORITHM = 'aes-256-gcm';
|
|
30
|
+
const IV_LENGTH = 12; // GCM recommended IV length
|
|
31
|
+
const TAG_LENGTH = 16; // GCM auth tag length
|
|
32
|
+
// ============================================================================
|
|
33
|
+
// Server Secret Management
|
|
34
|
+
// ============================================================================
|
|
35
|
+
let cachedSecret = null;
|
|
36
|
+
/**
|
|
37
|
+
* Get or generate the server encryption secret.
|
|
38
|
+
* Secret is 32 bytes (256 bits) for AES-256.
|
|
39
|
+
*/
|
|
40
|
+
function getServerSecret() {
|
|
41
|
+
if (cachedSecret)
|
|
42
|
+
return cachedSecret;
|
|
43
|
+
// Try to read existing secret
|
|
44
|
+
try {
|
|
45
|
+
if (node_fs_1.default.existsSync(SECRET_FILE)) {
|
|
46
|
+
const secret = node_fs_1.default.readFileSync(SECRET_FILE);
|
|
47
|
+
if (secret.length === 32) {
|
|
48
|
+
cachedSecret = secret;
|
|
49
|
+
return cachedSecret;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
catch {
|
|
54
|
+
// Will generate new secret
|
|
55
|
+
}
|
|
56
|
+
// Generate new secret
|
|
57
|
+
const secret = node_crypto_1.default.randomBytes(32);
|
|
58
|
+
// Ensure directory exists
|
|
59
|
+
if (!node_fs_1.default.existsSync(SECRET_DIR)) {
|
|
60
|
+
node_fs_1.default.mkdirSync(SECRET_DIR, { recursive: true, mode: 0o700 });
|
|
61
|
+
}
|
|
62
|
+
// Write secret with restrictive permissions
|
|
63
|
+
node_fs_1.default.writeFileSync(SECRET_FILE, secret, { mode: 0o600 });
|
|
64
|
+
cachedSecret = secret;
|
|
65
|
+
return cachedSecret;
|
|
66
|
+
}
|
|
67
|
+
// ============================================================================
|
|
68
|
+
// Encryption / Decryption
|
|
69
|
+
// ============================================================================
|
|
70
|
+
/**
|
|
71
|
+
* Encrypt data using AES-256-GCM.
|
|
72
|
+
* Returns base64url-encoded string: IV + ciphertext + authTag
|
|
73
|
+
*/
|
|
74
|
+
function encrypt(data) {
|
|
75
|
+
const secret = getServerSecret();
|
|
76
|
+
const iv = node_crypto_1.default.randomBytes(IV_LENGTH);
|
|
77
|
+
const cipher = node_crypto_1.default.createCipheriv(ALGORITHM, secret, iv);
|
|
78
|
+
const encrypted = Buffer.concat([
|
|
79
|
+
cipher.update(data, 'utf8'),
|
|
80
|
+
cipher.final()
|
|
81
|
+
]);
|
|
82
|
+
const authTag = cipher.getAuthTag();
|
|
83
|
+
// Combine: IV (12) + encrypted + authTag (16)
|
|
84
|
+
const combined = Buffer.concat([iv, encrypted, authTag]);
|
|
85
|
+
return combined.toString('base64url');
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Decrypt data encrypted with encrypt().
|
|
89
|
+
* Throws on invalid/tampered data.
|
|
90
|
+
*/
|
|
91
|
+
function decrypt(encryptedData) {
|
|
92
|
+
const secret = getServerSecret();
|
|
93
|
+
const combined = Buffer.from(encryptedData, 'base64url');
|
|
94
|
+
if (combined.length < IV_LENGTH + TAG_LENGTH) {
|
|
95
|
+
throw new Error('Invalid encrypted data: too short');
|
|
96
|
+
}
|
|
97
|
+
const iv = combined.subarray(0, IV_LENGTH);
|
|
98
|
+
const authTag = combined.subarray(combined.length - TAG_LENGTH);
|
|
99
|
+
const encrypted = combined.subarray(IV_LENGTH, combined.length - TAG_LENGTH);
|
|
100
|
+
const decipher = node_crypto_1.default.createDecipheriv(ALGORITHM, secret, iv);
|
|
101
|
+
decipher.setAuthTag(authTag);
|
|
102
|
+
try {
|
|
103
|
+
const decrypted = Buffer.concat([
|
|
104
|
+
decipher.update(encrypted),
|
|
105
|
+
decipher.final()
|
|
106
|
+
]);
|
|
107
|
+
return decrypted.toString('utf8');
|
|
108
|
+
}
|
|
109
|
+
catch {
|
|
110
|
+
throw new Error('Decryption failed: invalid or tampered data');
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Create an encrypted access token containing upstream credentials.
|
|
115
|
+
* Format: sb1.<encrypted_payload>
|
|
116
|
+
*
|
|
117
|
+
* The "sb1" prefix identifies this as a Sub Bridge v1 token.
|
|
118
|
+
*/
|
|
119
|
+
function createAccessToken(payload) {
|
|
120
|
+
const json = JSON.stringify(payload);
|
|
121
|
+
const encrypted = encrypt(json);
|
|
122
|
+
return `sb1.${encrypted}`;
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Decode and validate an access token.
|
|
126
|
+
* Returns null if token is invalid or expired.
|
|
127
|
+
*/
|
|
128
|
+
function decodeAccessToken(token) {
|
|
129
|
+
if (!token.startsWith('sb1.')) {
|
|
130
|
+
return null;
|
|
131
|
+
}
|
|
132
|
+
try {
|
|
133
|
+
const encrypted = token.slice(4); // Remove "sb1." prefix
|
|
134
|
+
const json = decrypt(encrypted);
|
|
135
|
+
const payload = JSON.parse(json);
|
|
136
|
+
// Validate required fields
|
|
137
|
+
if (payload.iss !== 'sub-bridge')
|
|
138
|
+
return null;
|
|
139
|
+
if (!payload.sub || !payload.aud)
|
|
140
|
+
return null;
|
|
141
|
+
if (!payload.exp || !payload.iat)
|
|
142
|
+
return null;
|
|
143
|
+
// Check expiration
|
|
144
|
+
if (Date.now() > payload.exp * 1000)
|
|
145
|
+
return null;
|
|
146
|
+
return payload;
|
|
147
|
+
}
|
|
148
|
+
catch {
|
|
149
|
+
return null;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Generate a random authorization code.
|
|
154
|
+
*/
|
|
155
|
+
function generateAuthCode() {
|
|
156
|
+
return node_crypto_1.default.randomBytes(32).toString('base64url');
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* Generate a random client ID for DCR.
|
|
160
|
+
*/
|
|
161
|
+
function generateClientId() {
|
|
162
|
+
return `sb_${node_crypto_1.default.randomBytes(16).toString('hex')}`;
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Generate a random client secret for DCR.
|
|
166
|
+
*/
|
|
167
|
+
function generateClientSecret() {
|
|
168
|
+
return node_crypto_1.default.randomBytes(32).toString('base64url');
|
|
169
|
+
}
|
|
170
|
+
//# sourceMappingURL=crypto.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"crypto.js","sourceRoot":"","sources":["../../src/oauth/crypto.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
export interface ClientRegistration {
|
|
2
|
+
client_id: string;
|
|
3
|
+
client_secret?: string;
|
|
4
|
+
client_name?: string;
|
|
5
|
+
redirect_uris: string[];
|
|
6
|
+
grant_types: string[];
|
|
7
|
+
response_types: string[];
|
|
8
|
+
token_endpoint_auth_method: string;
|
|
9
|
+
created_at: number;
|
|
10
|
+
}
|
|
11
|
+
export interface RegistrationRequest {
|
|
12
|
+
client_name?: string;
|
|
13
|
+
redirect_uris?: string[];
|
|
14
|
+
grant_types?: string[];
|
|
15
|
+
response_types?: string[];
|
|
16
|
+
token_endpoint_auth_method?: string;
|
|
17
|
+
}
|
|
18
|
+
export interface RegistrationResponse {
|
|
19
|
+
client_id: string;
|
|
20
|
+
client_secret?: string;
|
|
21
|
+
client_name?: string;
|
|
22
|
+
redirect_uris: string[];
|
|
23
|
+
grant_types: string[];
|
|
24
|
+
response_types: string[];
|
|
25
|
+
token_endpoint_auth_method: string;
|
|
26
|
+
client_id_issued_at: number;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Register a new OAuth client.
|
|
30
|
+
*/
|
|
31
|
+
export declare function registerClient(request: RegistrationRequest): RegistrationResponse;
|
|
32
|
+
/**
|
|
33
|
+
* Get a registered client by ID.
|
|
34
|
+
*/
|
|
35
|
+
export declare function getClient(clientId: string): ClientRegistration | undefined;
|
|
36
|
+
/**
|
|
37
|
+
* Validate client credentials for token endpoint.
|
|
38
|
+
*/
|
|
39
|
+
export declare function validateClient(clientId: string, clientSecret?: string): ClientRegistration | null;
|
|
40
|
+
/**
|
|
41
|
+
* Validate redirect URI against registered URIs.
|
|
42
|
+
*/
|
|
43
|
+
export declare function validateRedirectUri(clientId: string, redirectUri: string): boolean;
|
|
44
|
+
//# sourceMappingURL=dcr.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dcr.d.ts","sourceRoot":"","sources":["../../src/oauth/dcr.ts"],"names":[],"mappings":"AAYA,MAAM,WAAW,kBAAkB;IACjC,SAAS,EAAE,MAAM,CAAA;IACjB,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,aAAa,EAAE,MAAM,EAAE,CAAA;IACvB,WAAW,EAAE,MAAM,EAAE,CAAA;IACrB,cAAc,EAAE,MAAM,EAAE,CAAA;IACxB,0BAA0B,EAAE,MAAM,CAAA;IAClC,UAAU,EAAE,MAAM,CAAA;CACnB;AAED,MAAM,WAAW,mBAAmB;IAClC,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,aAAa,CAAC,EAAE,MAAM,EAAE,CAAA;IACxB,WAAW,CAAC,EAAE,MAAM,EAAE,CAAA;IACtB,cAAc,CAAC,EAAE,MAAM,EAAE,CAAA;IACzB,0BAA0B,CAAC,EAAE,MAAM,CAAA;CACpC;AAED,MAAM,WAAW,oBAAoB;IACnC,SAAS,EAAE,MAAM,CAAA;IACjB,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,aAAa,EAAE,MAAM,EAAE,CAAA;IACvB,WAAW,EAAE,MAAM,EAAE,CAAA;IACrB,cAAc,EAAE,MAAM,EAAE,CAAA;IACxB,0BAA0B,EAAE,MAAM,CAAA;IAClC,mBAAmB,EAAE,MAAM,CAAA;CAC5B;AAQD;;GAEG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,mBAAmB,GAAG,oBAAoB,CA+BjF;AAED;;GAEG;AACH,wBAAgB,SAAS,CAAC,QAAQ,EAAE,MAAM,GAAG,kBAAkB,GAAG,SAAS,CAE1E;AAED;;GAEG;AACH,wBAAgB,cAAc,CAC5B,QAAQ,EAAE,MAAM,EAChB,YAAY,CAAC,EAAE,MAAM,GACpB,kBAAkB,GAAG,IAAI,CAe3B;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CACjC,QAAQ,EAAE,MAAM,EAChB,WAAW,EAAE,MAAM,GAClB,OAAO,CAQT"}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.registerClient = registerClient;
|
|
4
|
+
exports.getClient = getClient;
|
|
5
|
+
exports.validateClient = validateClient;
|
|
6
|
+
exports.validateRedirectUri = validateRedirectUri;
|
|
7
|
+
/**
|
|
8
|
+
* Dynamic Client Registration (RFC 7591)
|
|
9
|
+
*
|
|
10
|
+
* Allows MCP clients to register themselves without manual setup.
|
|
11
|
+
* Client registrations are stored in-memory (local-only deployment).
|
|
12
|
+
*/
|
|
13
|
+
const crypto_1 = require("./crypto");
|
|
14
|
+
// ============================================================================
|
|
15
|
+
// Client Storage (In-Memory)
|
|
16
|
+
// ============================================================================
|
|
17
|
+
const clients = new Map();
|
|
18
|
+
/**
|
|
19
|
+
* Register a new OAuth client.
|
|
20
|
+
*/
|
|
21
|
+
function registerClient(request) {
|
|
22
|
+
const clientId = (0, crypto_1.generateClientId)();
|
|
23
|
+
const clientSecret = request.token_endpoint_auth_method === 'none'
|
|
24
|
+
? undefined
|
|
25
|
+
: (0, crypto_1.generateClientSecret)();
|
|
26
|
+
const now = Math.floor(Date.now() / 1000);
|
|
27
|
+
const registration = {
|
|
28
|
+
client_id: clientId,
|
|
29
|
+
client_secret: clientSecret,
|
|
30
|
+
client_name: request.client_name,
|
|
31
|
+
redirect_uris: request.redirect_uris || [],
|
|
32
|
+
grant_types: request.grant_types || ['authorization_code'],
|
|
33
|
+
response_types: request.response_types || ['code'],
|
|
34
|
+
token_endpoint_auth_method: request.token_endpoint_auth_method || 'client_secret_post',
|
|
35
|
+
created_at: now,
|
|
36
|
+
};
|
|
37
|
+
clients.set(clientId, registration);
|
|
38
|
+
return {
|
|
39
|
+
client_id: clientId,
|
|
40
|
+
client_secret: clientSecret,
|
|
41
|
+
client_name: registration.client_name,
|
|
42
|
+
redirect_uris: registration.redirect_uris,
|
|
43
|
+
grant_types: registration.grant_types,
|
|
44
|
+
response_types: registration.response_types,
|
|
45
|
+
token_endpoint_auth_method: registration.token_endpoint_auth_method,
|
|
46
|
+
client_id_issued_at: now,
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Get a registered client by ID.
|
|
51
|
+
*/
|
|
52
|
+
function getClient(clientId) {
|
|
53
|
+
return clients.get(clientId);
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Validate client credentials for token endpoint.
|
|
57
|
+
*/
|
|
58
|
+
function validateClient(clientId, clientSecret) {
|
|
59
|
+
const client = clients.get(clientId);
|
|
60
|
+
if (!client)
|
|
61
|
+
return null;
|
|
62
|
+
// For public clients (no secret required)
|
|
63
|
+
if (client.token_endpoint_auth_method === 'none') {
|
|
64
|
+
return client;
|
|
65
|
+
}
|
|
66
|
+
// For confidential clients, verify secret
|
|
67
|
+
if (client.client_secret && client.client_secret === clientSecret) {
|
|
68
|
+
return client;
|
|
69
|
+
}
|
|
70
|
+
return null;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Validate redirect URI against registered URIs.
|
|
74
|
+
*/
|
|
75
|
+
function validateRedirectUri(clientId, redirectUri) {
|
|
76
|
+
const client = clients.get(clientId);
|
|
77
|
+
if (!client)
|
|
78
|
+
return false;
|
|
79
|
+
// If no redirect URIs registered, allow any (for development)
|
|
80
|
+
if (client.redirect_uris.length === 0)
|
|
81
|
+
return true;
|
|
82
|
+
return client.redirect_uris.includes(redirectUri);
|
|
83
|
+
}
|
|
84
|
+
//# sourceMappingURL=dcr.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dcr.js","sourceRoot":"","sources":["../../src/oauth/dcr.ts"],"names":[],"mappings":";;AAmDA,wCA+BC;AAKD,8BAEC;AAKD,wCAkBC;AAKD,kDAWC;AAhID;;;;;GAKG;AACH,qCAAiE;AAoCjE,+EAA+E;AAC/E,6BAA6B;AAC7B,+EAA+E;AAE/E,MAAM,OAAO,GAAG,IAAI,GAAG,EAA8B,CAAA;AAErD;;GAEG;AACH,SAAgB,cAAc,CAAC,OAA4B;IACzD,MAAM,QAAQ,GAAG,IAAA,yBAAgB,GAAE,CAAA;IACnC,MAAM,YAAY,GAAG,OAAO,CAAC,0BAA0B,KAAK,MAAM;QAChE,CAAC,CAAC,SAAS;QACX,CAAC,CAAC,IAAA,6BAAoB,GAAE,CAAA;IAE1B,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,CAAA;IAEzC,MAAM,YAAY,GAAuB;QACvC,SAAS,EAAE,QAAQ;QACnB,aAAa,EAAE,YAAY;QAC3B,WAAW,EAAE,OAAO,CAAC,WAAW;QAChC,aAAa,EAAE,OAAO,CAAC,aAAa,IAAI,EAAE;QAC1C,WAAW,EAAE,OAAO,CAAC,WAAW,IAAI,CAAC,oBAAoB,CAAC;QAC1D,cAAc,EAAE,OAAO,CAAC,cAAc,IAAI,CAAC,MAAM,CAAC;QAClD,0BAA0B,EAAE,OAAO,CAAC,0BAA0B,IAAI,oBAAoB;QACtF,UAAU,EAAE,GAAG;KAChB,CAAA;IAED,OAAO,CAAC,GAAG,CAAC,QAAQ,EAAE,YAAY,CAAC,CAAA;IAEnC,OAAO;QACL,SAAS,EAAE,QAAQ;QACnB,aAAa,EAAE,YAAY;QAC3B,WAAW,EAAE,YAAY,CAAC,WAAW;QACrC,aAAa,EAAE,YAAY,CAAC,aAAa;QACzC,WAAW,EAAE,YAAY,CAAC,WAAW;QACrC,cAAc,EAAE,YAAY,CAAC,cAAc;QAC3C,0BAA0B,EAAE,YAAY,CAAC,0BAA0B;QACnE,mBAAmB,EAAE,GAAG;KACzB,CAAA;AACH,CAAC;AAED;;GAEG;AACH,SAAgB,SAAS,CAAC,QAAgB;IACxC,OAAO,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAA;AAC9B,CAAC;AAED;;GAEG;AACH,SAAgB,cAAc,CAC5B,QAAgB,EAChB,YAAqB;IAErB,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAA;IACpC,IAAI,CAAC,MAAM;QAAE,OAAO,IAAI,CAAA;IAExB,0CAA0C;IAC1C,IAAI,MAAM,CAAC,0BAA0B,KAAK,MAAM,EAAE,CAAC;QACjD,OAAO,MAAM,CAAA;IACf,CAAC;IAED,0CAA0C;IAC1C,IAAI,MAAM,CAAC,aAAa,IAAI,MAAM,CAAC,aAAa,KAAK,YAAY,EAAE,CAAC;QAClE,OAAO,MAAM,CAAA;IACf,CAAC;IAED,OAAO,IAAI,CAAA;AACb,CAAC;AAED;;GAEG;AACH,SAAgB,mBAAmB,CACjC,QAAgB,EAChB,WAAmB;IAEnB,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAA;IACpC,IAAI,CAAC,MAAM;QAAE,OAAO,KAAK,CAAA;IAEzB,8DAA8D;IAC9D,IAAI,MAAM,CAAC,aAAa,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAA;IAElD,OAAO,MAAM,CAAC,aAAa,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAA;AACnD,CAAC"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OAuth 2.0 Authorization Server Metadata (RFC 8414)
|
|
3
|
+
*
|
|
4
|
+
* Provides the /.well-known/oauth-authorization-server endpoint
|
|
5
|
+
* that MCP clients use to discover OAuth endpoints.
|
|
6
|
+
*/
|
|
7
|
+
export interface OAuthMetadata {
|
|
8
|
+
issuer: string;
|
|
9
|
+
authorization_endpoint: string;
|
|
10
|
+
token_endpoint: string;
|
|
11
|
+
registration_endpoint?: string;
|
|
12
|
+
scopes_supported: string[];
|
|
13
|
+
response_types_supported: string[];
|
|
14
|
+
grant_types_supported: string[];
|
|
15
|
+
token_endpoint_auth_methods_supported: string[];
|
|
16
|
+
code_challenge_methods_supported: string[];
|
|
17
|
+
service_documentation?: string;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Generate OAuth metadata for the given issuer URL.
|
|
21
|
+
*/
|
|
22
|
+
export declare function getOAuthMetadata(issuerUrl: string): OAuthMetadata;
|
|
23
|
+
//# sourceMappingURL=metadata.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"metadata.d.ts","sourceRoot":"","sources":["../../src/oauth/metadata.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,MAAM,CAAA;IACd,sBAAsB,EAAE,MAAM,CAAA;IAC9B,cAAc,EAAE,MAAM,CAAA;IACtB,qBAAqB,CAAC,EAAE,MAAM,CAAA;IAC9B,gBAAgB,EAAE,MAAM,EAAE,CAAA;IAC1B,wBAAwB,EAAE,MAAM,EAAE,CAAA;IAClC,qBAAqB,EAAE,MAAM,EAAE,CAAA;IAC/B,qCAAqC,EAAE,MAAM,EAAE,CAAA;IAC/C,gCAAgC,EAAE,MAAM,EAAE,CAAA;IAC1C,qBAAqB,CAAC,EAAE,MAAM,CAAA;CAC/B;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,SAAS,EAAE,MAAM,GAAG,aAAa,CAgBjE"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* OAuth 2.0 Authorization Server Metadata (RFC 8414)
|
|
4
|
+
*
|
|
5
|
+
* Provides the /.well-known/oauth-authorization-server endpoint
|
|
6
|
+
* that MCP clients use to discover OAuth endpoints.
|
|
7
|
+
*/
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.getOAuthMetadata = getOAuthMetadata;
|
|
10
|
+
/**
|
|
11
|
+
* Generate OAuth metadata for the given issuer URL.
|
|
12
|
+
*/
|
|
13
|
+
function getOAuthMetadata(issuerUrl) {
|
|
14
|
+
// Ensure no trailing slash
|
|
15
|
+
const baseUrl = issuerUrl.replace(/\/$/, '');
|
|
16
|
+
return {
|
|
17
|
+
issuer: baseUrl,
|
|
18
|
+
authorization_endpoint: `${baseUrl}/oauth/authorize`,
|
|
19
|
+
token_endpoint: `${baseUrl}/oauth/token`,
|
|
20
|
+
registration_endpoint: `${baseUrl}/oauth/register`,
|
|
21
|
+
scopes_supported: ['openid', 'profile', 'offline_access'],
|
|
22
|
+
response_types_supported: ['code'],
|
|
23
|
+
grant_types_supported: ['authorization_code', 'refresh_token'],
|
|
24
|
+
token_endpoint_auth_methods_supported: ['client_secret_post', 'none'],
|
|
25
|
+
code_challenge_methods_supported: ['S256', 'plain'],
|
|
26
|
+
service_documentation: 'https://github.com/anthropics/sub-bridge',
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
//# sourceMappingURL=metadata.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"metadata.js","sourceRoot":"","sources":["../../src/oauth/metadata.ts"],"names":[],"mappings":";AAAA;;;;;GAKG;;AAkBH,4CAgBC;AAnBD;;GAEG;AACH,SAAgB,gBAAgB,CAAC,SAAiB;IAChD,2BAA2B;IAC3B,MAAM,OAAO,GAAG,SAAS,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAA;IAE5C,OAAO;QACL,MAAM,EAAE,OAAO;QACf,sBAAsB,EAAE,GAAG,OAAO,kBAAkB;QACpD,cAAc,EAAE,GAAG,OAAO,cAAc;QACxC,qBAAqB,EAAE,GAAG,OAAO,iBAAiB;QAClD,gBAAgB,EAAE,CAAC,QAAQ,EAAE,SAAS,EAAE,gBAAgB,CAAC;QACzD,wBAAwB,EAAE,CAAC,MAAM,CAAC;QAClC,qBAAqB,EAAE,CAAC,oBAAoB,EAAE,eAAe,CAAC;QAC9D,qCAAqC,EAAE,CAAC,oBAAoB,EAAE,MAAM,CAAC;QACrE,gCAAgC,EAAE,CAAC,MAAM,EAAE,OAAO,CAAC;QACnD,qBAAqB,EAAE,0CAA0C;KAClE,CAAA;AACH,CAAC"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
export interface TokenRequest {
|
|
2
|
+
grant_type: string;
|
|
3
|
+
code?: string;
|
|
4
|
+
redirect_uri?: string;
|
|
5
|
+
client_id?: string;
|
|
6
|
+
client_secret?: string;
|
|
7
|
+
code_verifier?: string;
|
|
8
|
+
refresh_token?: string;
|
|
9
|
+
}
|
|
10
|
+
export interface TokenResponse {
|
|
11
|
+
access_token: string;
|
|
12
|
+
token_type: 'Bearer';
|
|
13
|
+
expires_in: number;
|
|
14
|
+
refresh_token?: string;
|
|
15
|
+
scope?: string;
|
|
16
|
+
}
|
|
17
|
+
export interface TokenError {
|
|
18
|
+
error: string;
|
|
19
|
+
error_description: string;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Process a token request and return an access token or error.
|
|
23
|
+
*/
|
|
24
|
+
export declare function processTokenRequest(request: TokenRequest): TokenResponse | TokenError;
|
|
25
|
+
/**
|
|
26
|
+
* Check if a response is an error.
|
|
27
|
+
*/
|
|
28
|
+
export declare function isTokenError(response: TokenResponse | TokenError): response is TokenError;
|
|
29
|
+
//# sourceMappingURL=token.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"token.d.ts","sourceRoot":"","sources":["../../src/oauth/token.ts"],"names":[],"mappings":"AAcA,MAAM,WAAW,YAAY;IAC3B,UAAU,EAAE,MAAM,CAAA;IAClB,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,aAAa,CAAC,EAAE,MAAM,CAAA;CACvB;AAED,MAAM,WAAW,aAAa;IAC5B,YAAY,EAAE,MAAM,CAAA;IACpB,UAAU,EAAE,QAAQ,CAAA;IACpB,UAAU,EAAE,MAAM,CAAA;IAClB,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,KAAK,CAAC,EAAE,MAAM,CAAA;CACf;AAED,MAAM,WAAW,UAAU;IACzB,KAAK,EAAE,MAAM,CAAA;IACb,iBAAiB,EAAE,MAAM,CAAA;CAC1B;AAQD;;GAEG;AACH,wBAAgB,mBAAmB,CACjC,OAAO,EAAE,YAAY,GACpB,aAAa,GAAG,UAAU,CAc5B;AAuGD;;GAEG;AACH,wBAAgB,YAAY,CAC1B,QAAQ,EAAE,aAAa,GAAG,UAAU,GACnC,QAAQ,IAAI,UAAU,CAExB"}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.processTokenRequest = processTokenRequest;
|
|
4
|
+
exports.isTokenError = isTokenError;
|
|
5
|
+
/**
|
|
6
|
+
* OAuth Token Endpoint
|
|
7
|
+
*
|
|
8
|
+
* Handles token exchange and issues encrypted access tokens
|
|
9
|
+
* containing upstream provider credentials.
|
|
10
|
+
*/
|
|
11
|
+
const crypto_1 = require("./crypto");
|
|
12
|
+
const dcr_1 = require("./dcr");
|
|
13
|
+
const authorize_1 = require("./authorize");
|
|
14
|
+
// ============================================================================
|
|
15
|
+
// Token Generation
|
|
16
|
+
// ============================================================================
|
|
17
|
+
const ACCESS_TOKEN_TTL_SECONDS = 3600 * 24 * 7; // 7 days
|
|
18
|
+
/**
|
|
19
|
+
* Process a token request and return an access token or error.
|
|
20
|
+
*/
|
|
21
|
+
function processTokenRequest(request) {
|
|
22
|
+
// Validate grant_type
|
|
23
|
+
if (request.grant_type === 'authorization_code') {
|
|
24
|
+
return handleAuthorizationCodeGrant(request);
|
|
25
|
+
}
|
|
26
|
+
if (request.grant_type === 'refresh_token') {
|
|
27
|
+
return handleRefreshTokenGrant(request);
|
|
28
|
+
}
|
|
29
|
+
return {
|
|
30
|
+
error: 'unsupported_grant_type',
|
|
31
|
+
error_description: 'Only authorization_code and refresh_token grants are supported',
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Handle authorization_code grant type.
|
|
36
|
+
*/
|
|
37
|
+
function handleAuthorizationCodeGrant(request) {
|
|
38
|
+
// Validate required parameters
|
|
39
|
+
if (!request.code) {
|
|
40
|
+
return {
|
|
41
|
+
error: 'invalid_request',
|
|
42
|
+
error_description: 'Missing authorization code',
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
if (!request.client_id) {
|
|
46
|
+
return {
|
|
47
|
+
error: 'invalid_request',
|
|
48
|
+
error_description: 'Missing client_id',
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
if (!request.redirect_uri) {
|
|
52
|
+
return {
|
|
53
|
+
error: 'invalid_request',
|
|
54
|
+
error_description: 'Missing redirect_uri',
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
// Validate client
|
|
58
|
+
const client = (0, dcr_1.validateClient)(request.client_id, request.client_secret);
|
|
59
|
+
if (!client) {
|
|
60
|
+
return {
|
|
61
|
+
error: 'invalid_client',
|
|
62
|
+
error_description: 'Client authentication failed',
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
// Exchange authorization code
|
|
66
|
+
const codeResult = (0, authorize_1.exchangeAuthorizationCode)(request.code, request.client_id, request.redirect_uri, request.code_verifier);
|
|
67
|
+
if ('error' in codeResult) {
|
|
68
|
+
return {
|
|
69
|
+
error: codeResult.error,
|
|
70
|
+
error_description: codeResult.errorDescription,
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
// Create access token
|
|
74
|
+
return createTokenResponse(codeResult, request.client_id);
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Handle refresh_token grant type.
|
|
78
|
+
* For now, we don't support refresh tokens on the Sub Bridge side.
|
|
79
|
+
* Users need to re-authenticate when tokens expire.
|
|
80
|
+
*/
|
|
81
|
+
function handleRefreshTokenGrant(_request) {
|
|
82
|
+
// TODO: Implement refresh token support
|
|
83
|
+
// This would require storing refresh tokens securely
|
|
84
|
+
return {
|
|
85
|
+
error: 'unsupported_grant_type',
|
|
86
|
+
error_description: 'Refresh tokens are not yet supported. Please re-authenticate.',
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Create a token response from an authorization code.
|
|
91
|
+
*/
|
|
92
|
+
function createTokenResponse(authCode, clientId) {
|
|
93
|
+
const now = Math.floor(Date.now() / 1000);
|
|
94
|
+
const expiresAt = now + ACCESS_TOKEN_TTL_SECONDS;
|
|
95
|
+
const payload = {
|
|
96
|
+
iss: 'sub-bridge',
|
|
97
|
+
sub: authCode.userId,
|
|
98
|
+
aud: clientId,
|
|
99
|
+
exp: expiresAt,
|
|
100
|
+
iat: now,
|
|
101
|
+
providers: authCode.providers,
|
|
102
|
+
};
|
|
103
|
+
const accessToken = (0, crypto_1.createAccessToken)(payload);
|
|
104
|
+
return {
|
|
105
|
+
access_token: accessToken,
|
|
106
|
+
token_type: 'Bearer',
|
|
107
|
+
expires_in: ACCESS_TOKEN_TTL_SECONDS,
|
|
108
|
+
scope: 'openid profile',
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Check if a response is an error.
|
|
113
|
+
*/
|
|
114
|
+
function isTokenError(response) {
|
|
115
|
+
return 'error' in response;
|
|
116
|
+
}
|
|
117
|
+
//# sourceMappingURL=token.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"token.js","sourceRoot":"","sources":["../../src/oauth/token.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"chat.d.ts","sourceRoot":"","sources":["../../src/routes/chat.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,EAAE,IAAI,EAAW,MAAM,MAAM,CAAA;
|
|
1
|
+
{"version":3,"file":"chat.d.ts","sourceRoot":"","sources":["../../src/routes/chat.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,EAAE,IAAI,EAAW,MAAM,MAAM,CAAA;AA67BpC,wBAAgB,gBAAgB,+EAyB/B"}
|