@q32/core 0.1.14 → 0.1.16
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/dist/oauth.d.ts +130 -0
- package/dist/oauth.d.ts.map +1 -1
- package/dist/oauth.js +395 -1
- package/dist/oauth.js.map +1 -1
- package/package.json +1 -1
- package/src/oauth.ts +641 -1
package/dist/oauth.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { FetchLike } from "./http.js";
|
|
2
|
+
import type { D1DatabaseLike } from "./d1.js";
|
|
2
3
|
export type OAuthMetadataOptions = {
|
|
3
4
|
issuer: string;
|
|
4
5
|
authorizationPath?: string;
|
|
@@ -42,6 +43,79 @@ export type GitHubUserProfile = {
|
|
|
42
43
|
name: string | null;
|
|
43
44
|
avatar_url: string | null;
|
|
44
45
|
};
|
|
46
|
+
export type OAuthClientInformation = {
|
|
47
|
+
client_id: string;
|
|
48
|
+
client_secret?: string;
|
|
49
|
+
client_name?: string;
|
|
50
|
+
redirect_uris?: string[];
|
|
51
|
+
scope?: string;
|
|
52
|
+
grant_types?: string[];
|
|
53
|
+
response_types?: string[];
|
|
54
|
+
token_endpoint_auth_method?: string;
|
|
55
|
+
client_uri?: string;
|
|
56
|
+
logo_uri?: string;
|
|
57
|
+
contacts?: string[];
|
|
58
|
+
tos_uri?: string;
|
|
59
|
+
policy_uri?: string;
|
|
60
|
+
software_id?: string;
|
|
61
|
+
software_version?: string;
|
|
62
|
+
client_id_issued_at?: number;
|
|
63
|
+
client_secret_expires_at?: number;
|
|
64
|
+
};
|
|
65
|
+
export type OAuthClientRegistration = Omit<OAuthClientInformation, "client_id" | "client_id_issued_at">;
|
|
66
|
+
export type OAuthTokens = {
|
|
67
|
+
access_token: string;
|
|
68
|
+
token_type: "Bearer";
|
|
69
|
+
expires_in: number;
|
|
70
|
+
refresh_token?: string;
|
|
71
|
+
scope?: string;
|
|
72
|
+
};
|
|
73
|
+
export type McpOAuthSubjectColumn = {
|
|
74
|
+
field: string;
|
|
75
|
+
column: string;
|
|
76
|
+
};
|
|
77
|
+
export type McpOAuthRepositoryOptions = {
|
|
78
|
+
defaultScope?: string;
|
|
79
|
+
tokenEndpointAuthMethod?: string;
|
|
80
|
+
subjectColumns?: McpOAuthSubjectColumn[];
|
|
81
|
+
refreshRotationColumns?: boolean;
|
|
82
|
+
};
|
|
83
|
+
export type McpAuthorizationCodeRow = {
|
|
84
|
+
authorizationCodeId: string;
|
|
85
|
+
clientId: string;
|
|
86
|
+
redirectUri: string;
|
|
87
|
+
scopesJson: string;
|
|
88
|
+
resource: string | null;
|
|
89
|
+
codeChallenge: string;
|
|
90
|
+
expiresAt: number;
|
|
91
|
+
usedAt: string | null;
|
|
92
|
+
subject: Record<string, string>;
|
|
93
|
+
};
|
|
94
|
+
export type McpTokenRow = {
|
|
95
|
+
tokenId: string;
|
|
96
|
+
clientId: string;
|
|
97
|
+
scopesJson: string;
|
|
98
|
+
resource: string | null;
|
|
99
|
+
accessExpiresAt: number;
|
|
100
|
+
refreshExpiresAt: number | null;
|
|
101
|
+
revokedAt: string | null;
|
|
102
|
+
subject: Record<string, string>;
|
|
103
|
+
rotatedToTokenId?: string | null;
|
|
104
|
+
refreshReuseExpiresAt?: number | null;
|
|
105
|
+
rotatedResponseCiphertext?: string | null;
|
|
106
|
+
rotatedResponseNonce?: string | null;
|
|
107
|
+
};
|
|
108
|
+
export type McpIssuedTokenSet = {
|
|
109
|
+
tokenId: string;
|
|
110
|
+
tokens: OAuthTokens;
|
|
111
|
+
accessExpiresAt: number;
|
|
112
|
+
refreshExpiresAt: number | null;
|
|
113
|
+
};
|
|
114
|
+
export type McpOAuthTokenOptions = {
|
|
115
|
+
accessTokenTtlSeconds?: number;
|
|
116
|
+
refreshTokenTtlSeconds?: number;
|
|
117
|
+
now?: () => number;
|
|
118
|
+
};
|
|
45
119
|
export declare class GoogleOAuthClient {
|
|
46
120
|
private readonly fetchImpl;
|
|
47
121
|
constructor(fetchImpl?: FetchLike);
|
|
@@ -67,4 +141,60 @@ export declare class GitHubOAuthClient {
|
|
|
67
141
|
fetchPrimaryEmail(accessToken: string): Promise<string | null>;
|
|
68
142
|
private githubHeaders;
|
|
69
143
|
}
|
|
144
|
+
export declare class McpOAuthRepository {
|
|
145
|
+
private readonly db;
|
|
146
|
+
private readonly options;
|
|
147
|
+
constructor(db: D1DatabaseLike, options?: McpOAuthRepositoryOptions);
|
|
148
|
+
getClient(clientId: string): Promise<OAuthClientInformation | undefined>;
|
|
149
|
+
registerClient(client: OAuthClientRegistration): Promise<OAuthClientInformation>;
|
|
150
|
+
createAuthorizationCode(input: {
|
|
151
|
+
code: string;
|
|
152
|
+
clientId: string;
|
|
153
|
+
subject: Record<string, string>;
|
|
154
|
+
redirectUri: string;
|
|
155
|
+
scopes: string[];
|
|
156
|
+
resource: string | null;
|
|
157
|
+
codeChallenge: string;
|
|
158
|
+
expiresAt?: number;
|
|
159
|
+
}): Promise<void>;
|
|
160
|
+
getAuthorizationCode(code: string): Promise<McpAuthorizationCodeRow | null>;
|
|
161
|
+
consumeAuthorizationCode(authorizationCodeId: string): Promise<void>;
|
|
162
|
+
createTokenSet(input: {
|
|
163
|
+
clientId: string;
|
|
164
|
+
subject: Record<string, string>;
|
|
165
|
+
accessToken: string;
|
|
166
|
+
refreshToken: string | null;
|
|
167
|
+
scopes: string[];
|
|
168
|
+
resource: string | null;
|
|
169
|
+
accessExpiresAt: number;
|
|
170
|
+
refreshExpiresAt: number | null;
|
|
171
|
+
}): Promise<string>;
|
|
172
|
+
getTokenByAccessToken(token: string): Promise<McpTokenRow | null>;
|
|
173
|
+
getTokenByRefreshToken(token: string): Promise<McpTokenRow | null>;
|
|
174
|
+
revokeToken(tokenId: string): Promise<void>;
|
|
175
|
+
touchAccessToken(tokenId: string): Promise<void>;
|
|
176
|
+
markTokenRotated(input: {
|
|
177
|
+
tokenId: string;
|
|
178
|
+
rotatedToTokenId: string;
|
|
179
|
+
refreshReuseExpiresAt: number;
|
|
180
|
+
rotatedResponseCiphertext: string;
|
|
181
|
+
rotatedResponseNonce: string;
|
|
182
|
+
}): Promise<boolean>;
|
|
183
|
+
private getTokenByHashColumn;
|
|
184
|
+
private subjectSelectSql;
|
|
185
|
+
}
|
|
186
|
+
export declare function issueMcpOAuthTokenSet(repository: McpOAuthRepository, input: {
|
|
187
|
+
clientId: string;
|
|
188
|
+
subject: Record<string, string>;
|
|
189
|
+
scopes: string[];
|
|
190
|
+
resource?: URL | null;
|
|
191
|
+
}, options?: McpOAuthTokenOptions): Promise<McpIssuedTokenSet>;
|
|
192
|
+
export declare function parseOAuthScope(scope: string | null | undefined): string[];
|
|
193
|
+
export declare function parseOAuthJsonArray(value: string | null | undefined): string[];
|
|
194
|
+
export declare function createOAuthRedirect(redirectUri: string, params: Record<string, string | undefined>): string;
|
|
195
|
+
export declare function encryptOAuthTokenResponse(tokens: OAuthTokens, secret: string): Promise<{
|
|
196
|
+
ciphertext: string;
|
|
197
|
+
nonce: string;
|
|
198
|
+
}>;
|
|
199
|
+
export declare function decryptOAuthTokenResponse(ciphertext: string, nonce: string, secret: string): Promise<OAuthTokens | null>;
|
|
70
200
|
//# sourceMappingURL=oauth.d.ts.map
|
package/dist/oauth.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"oauth.d.ts","sourceRoot":"","sources":["../src/oauth.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"oauth.d.ts","sourceRoot":"","sources":["../src/oauth.ts"],"names":[],"mappings":"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"}
|
package/dist/oauth.js
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { defaultFetch } from "./http.js";
|
|
2
|
+
import { sha256Hex } from "./hash.js";
|
|
3
|
+
import { createId, createToken, fromBase64Url, toBase64Url } from "./ids.js";
|
|
2
4
|
export function oauthAuthorizationServerMetadata(options) {
|
|
3
5
|
const issuer = options.issuer.replace(/\/$/, "");
|
|
4
6
|
return {
|
|
@@ -31,6 +33,10 @@ export function mcpServerCard(options) {
|
|
|
31
33
|
transport: "http",
|
|
32
34
|
};
|
|
33
35
|
}
|
|
36
|
+
const DEFAULT_MCP_SUBJECT_COLUMNS = [{ field: "accountId", column: "account_id" }];
|
|
37
|
+
const DEFAULT_AUTH_CODE_TTL_SECONDS = 10 * 60;
|
|
38
|
+
const DEFAULT_ACCESS_TOKEN_TTL_SECONDS = 60 * 60;
|
|
39
|
+
const DEFAULT_REFRESH_TOKEN_TTL_SECONDS = 30 * 24 * 60 * 60;
|
|
34
40
|
export class GoogleOAuthClient {
|
|
35
41
|
fetchImpl;
|
|
36
42
|
constructor(fetchImpl = defaultFetch) {
|
|
@@ -81,7 +87,7 @@ export class GoogleOAuthClient {
|
|
|
81
87
|
if (!profile.sub || !profile.email)
|
|
82
88
|
throw new Error("Google profile missing identity fields");
|
|
83
89
|
if (options.requireVerifiedEmail && profile.email_verified === false)
|
|
84
|
-
throw new Error("Google profile
|
|
90
|
+
throw new Error("Google profile missing verified identity fields");
|
|
85
91
|
return profile;
|
|
86
92
|
}
|
|
87
93
|
}
|
|
@@ -148,10 +154,398 @@ export class GitHubOAuthClient {
|
|
|
148
154
|
};
|
|
149
155
|
}
|
|
150
156
|
}
|
|
157
|
+
export class McpOAuthRepository {
|
|
158
|
+
db;
|
|
159
|
+
options;
|
|
160
|
+
constructor(db, options = {}) {
|
|
161
|
+
this.db = db;
|
|
162
|
+
this.options = {
|
|
163
|
+
defaultScope: options.defaultScope ?? "mcp:read",
|
|
164
|
+
tokenEndpointAuthMethod: options.tokenEndpointAuthMethod ?? "none",
|
|
165
|
+
subjectColumns: options.subjectColumns ?? DEFAULT_MCP_SUBJECT_COLUMNS,
|
|
166
|
+
refreshRotationColumns: options.refreshRotationColumns ?? false,
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
async getClient(clientId) {
|
|
170
|
+
const row = await this.db
|
|
171
|
+
.prepare(`
|
|
172
|
+
SELECT
|
|
173
|
+
client_id AS clientId,
|
|
174
|
+
client_secret AS clientSecret,
|
|
175
|
+
client_name AS clientName,
|
|
176
|
+
redirect_uris_json AS redirectUrisJson,
|
|
177
|
+
scope,
|
|
178
|
+
grant_types_json AS grantTypesJson,
|
|
179
|
+
response_types_json AS responseTypesJson,
|
|
180
|
+
token_endpoint_auth_method AS tokenEndpointAuthMethod,
|
|
181
|
+
client_uri AS clientUri,
|
|
182
|
+
logo_uri AS logoUri,
|
|
183
|
+
contacts_json AS contactsJson,
|
|
184
|
+
tos_uri AS tosUri,
|
|
185
|
+
policy_uri AS policyUri,
|
|
186
|
+
software_id AS softwareId,
|
|
187
|
+
software_version AS softwareVersion,
|
|
188
|
+
client_id_issued_at AS clientIdIssuedAt,
|
|
189
|
+
client_secret_expires_at AS clientSecretExpiresAt
|
|
190
|
+
FROM mcp_oauth_clients
|
|
191
|
+
WHERE client_id = ?
|
|
192
|
+
LIMIT 1
|
|
193
|
+
`)
|
|
194
|
+
.bind(clientId)
|
|
195
|
+
.first();
|
|
196
|
+
if (!row)
|
|
197
|
+
return undefined;
|
|
198
|
+
return clientFromRow(row);
|
|
199
|
+
}
|
|
200
|
+
async registerClient(client) {
|
|
201
|
+
const clientId = createId("mcpcli");
|
|
202
|
+
const clientIdIssuedAt = nowEpochSeconds();
|
|
203
|
+
await this.db
|
|
204
|
+
.prepare(`
|
|
205
|
+
INSERT INTO mcp_oauth_clients (
|
|
206
|
+
client_id,
|
|
207
|
+
client_secret,
|
|
208
|
+
client_name,
|
|
209
|
+
redirect_uris_json,
|
|
210
|
+
scope,
|
|
211
|
+
grant_types_json,
|
|
212
|
+
response_types_json,
|
|
213
|
+
token_endpoint_auth_method,
|
|
214
|
+
client_uri,
|
|
215
|
+
logo_uri,
|
|
216
|
+
contacts_json,
|
|
217
|
+
tos_uri,
|
|
218
|
+
policy_uri,
|
|
219
|
+
software_id,
|
|
220
|
+
software_version,
|
|
221
|
+
client_id_issued_at,
|
|
222
|
+
client_secret_expires_at,
|
|
223
|
+
updated_at
|
|
224
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, CURRENT_TIMESTAMP)
|
|
225
|
+
`)
|
|
226
|
+
.bind(clientId, client.client_secret ?? null, client.client_name ?? null, jsonArray(client.redirect_uris), client.scope ?? this.options.defaultScope, jsonArray(client.grant_types), jsonArray(client.response_types), client.token_endpoint_auth_method ?? this.options.tokenEndpointAuthMethod, client.client_uri ?? null, client.logo_uri ?? null, jsonArray(client.contacts), client.tos_uri ?? null, client.policy_uri ?? null, client.software_id ?? null, client.software_version ?? null, clientIdIssuedAt, client.client_secret_expires_at ?? null)
|
|
227
|
+
.run();
|
|
228
|
+
const registered = await this.getClient(clientId);
|
|
229
|
+
if (!registered)
|
|
230
|
+
throw new Error("registered OAuth client missing");
|
|
231
|
+
return registered;
|
|
232
|
+
}
|
|
233
|
+
async createAuthorizationCode(input) {
|
|
234
|
+
const subjectColumns = this.options.subjectColumns.map((column) => quoteIdentifier(column.column));
|
|
235
|
+
const subjectValues = this.options.subjectColumns.map((column) => requiredSubject(input.subject, column.field));
|
|
236
|
+
await this.db
|
|
237
|
+
.prepare(`
|
|
238
|
+
INSERT INTO mcp_oauth_authorization_codes (
|
|
239
|
+
authorization_code_id,
|
|
240
|
+
code_hash,
|
|
241
|
+
client_id,
|
|
242
|
+
${subjectColumns.join(",\n ")},
|
|
243
|
+
redirect_uri,
|
|
244
|
+
scopes_json,
|
|
245
|
+
resource,
|
|
246
|
+
code_challenge,
|
|
247
|
+
expires_at,
|
|
248
|
+
updated_at
|
|
249
|
+
) VALUES (${["?", "?", "?", ...subjectValues.map(() => "?"), "?", "?", "?", "?", "?", "CURRENT_TIMESTAMP"].join(", ")})
|
|
250
|
+
`)
|
|
251
|
+
.bind(createId("mcpac"), await sha256Hex(input.code), input.clientId, ...subjectValues, input.redirectUri, jsonArray(input.scopes), input.resource, input.codeChallenge, input.expiresAt ?? nowEpochSeconds() + DEFAULT_AUTH_CODE_TTL_SECONDS)
|
|
252
|
+
.run();
|
|
253
|
+
}
|
|
254
|
+
async getAuthorizationCode(code) {
|
|
255
|
+
const subjectSelect = this.subjectSelectSql();
|
|
256
|
+
const row = await this.db
|
|
257
|
+
.prepare(`
|
|
258
|
+
SELECT
|
|
259
|
+
authorization_code_id AS authorizationCodeId,
|
|
260
|
+
client_id AS clientId,
|
|
261
|
+
${subjectSelect}
|
|
262
|
+
redirect_uri AS redirectUri,
|
|
263
|
+
scopes_json AS scopesJson,
|
|
264
|
+
resource,
|
|
265
|
+
code_challenge AS codeChallenge,
|
|
266
|
+
expires_at AS expiresAt,
|
|
267
|
+
used_at AS usedAt
|
|
268
|
+
FROM mcp_oauth_authorization_codes
|
|
269
|
+
WHERE code_hash = ?
|
|
270
|
+
LIMIT 1
|
|
271
|
+
`)
|
|
272
|
+
.bind(await sha256Hex(code))
|
|
273
|
+
.first();
|
|
274
|
+
return row ? authorizationCodeFromRow(row, this.options.subjectColumns) : null;
|
|
275
|
+
}
|
|
276
|
+
async consumeAuthorizationCode(authorizationCodeId) {
|
|
277
|
+
await this.db
|
|
278
|
+
.prepare(`
|
|
279
|
+
UPDATE mcp_oauth_authorization_codes
|
|
280
|
+
SET used_at = CURRENT_TIMESTAMP, updated_at = CURRENT_TIMESTAMP
|
|
281
|
+
WHERE authorization_code_id = ?
|
|
282
|
+
`)
|
|
283
|
+
.bind(authorizationCodeId)
|
|
284
|
+
.run();
|
|
285
|
+
}
|
|
286
|
+
async createTokenSet(input) {
|
|
287
|
+
const subjectColumns = this.options.subjectColumns.map((column) => quoteIdentifier(column.column));
|
|
288
|
+
const subjectValues = this.options.subjectColumns.map((column) => requiredSubject(input.subject, column.field));
|
|
289
|
+
const tokenId = createId("mcptok");
|
|
290
|
+
await this.db
|
|
291
|
+
.prepare(`
|
|
292
|
+
INSERT INTO mcp_oauth_tokens (
|
|
293
|
+
token_id,
|
|
294
|
+
client_id,
|
|
295
|
+
${subjectColumns.join(",\n ")},
|
|
296
|
+
access_token_hash,
|
|
297
|
+
refresh_token_hash,
|
|
298
|
+
scopes_json,
|
|
299
|
+
resource,
|
|
300
|
+
access_expires_at,
|
|
301
|
+
refresh_expires_at,
|
|
302
|
+
updated_at
|
|
303
|
+
) VALUES (${["?", "?", ...subjectValues.map(() => "?"), "?", "?", "?", "?", "?", "?", "CURRENT_TIMESTAMP"].join(", ")})
|
|
304
|
+
`)
|
|
305
|
+
.bind(tokenId, input.clientId, ...subjectValues, await sha256Hex(input.accessToken), input.refreshToken ? await sha256Hex(input.refreshToken) : null, jsonArray(input.scopes), input.resource, input.accessExpiresAt, input.refreshExpiresAt)
|
|
306
|
+
.run();
|
|
307
|
+
return tokenId;
|
|
308
|
+
}
|
|
309
|
+
async getTokenByAccessToken(token) {
|
|
310
|
+
return this.getTokenByHashColumn("access_token_hash", token);
|
|
311
|
+
}
|
|
312
|
+
async getTokenByRefreshToken(token) {
|
|
313
|
+
return this.getTokenByHashColumn("refresh_token_hash", token);
|
|
314
|
+
}
|
|
315
|
+
async revokeToken(tokenId) {
|
|
316
|
+
await this.db
|
|
317
|
+
.prepare(`
|
|
318
|
+
UPDATE mcp_oauth_tokens
|
|
319
|
+
SET revoked_at = CURRENT_TIMESTAMP, updated_at = CURRENT_TIMESTAMP
|
|
320
|
+
WHERE token_id = ?
|
|
321
|
+
`)
|
|
322
|
+
.bind(tokenId)
|
|
323
|
+
.run();
|
|
324
|
+
}
|
|
325
|
+
async touchAccessToken(tokenId) {
|
|
326
|
+
await this.db
|
|
327
|
+
.prepare(`
|
|
328
|
+
UPDATE mcp_oauth_tokens
|
|
329
|
+
SET access_last_used_at = CURRENT_TIMESTAMP, updated_at = CURRENT_TIMESTAMP
|
|
330
|
+
WHERE token_id = ?
|
|
331
|
+
`)
|
|
332
|
+
.bind(tokenId)
|
|
333
|
+
.run();
|
|
334
|
+
}
|
|
335
|
+
async markTokenRotated(input) {
|
|
336
|
+
if (!this.options.refreshRotationColumns) {
|
|
337
|
+
await this.revokeToken(input.tokenId);
|
|
338
|
+
return true;
|
|
339
|
+
}
|
|
340
|
+
const result = await this.db
|
|
341
|
+
.prepare(`
|
|
342
|
+
UPDATE mcp_oauth_tokens
|
|
343
|
+
SET
|
|
344
|
+
revoked_at = CURRENT_TIMESTAMP,
|
|
345
|
+
rotated_to_token_id = ?,
|
|
346
|
+
refresh_reuse_expires_at = ?,
|
|
347
|
+
rotated_response_ciphertext = ?,
|
|
348
|
+
rotated_response_nonce = ?,
|
|
349
|
+
updated_at = CURRENT_TIMESTAMP
|
|
350
|
+
WHERE token_id = ? AND rotated_to_token_id IS NULL
|
|
351
|
+
`)
|
|
352
|
+
.bind(input.rotatedToTokenId, input.refreshReuseExpiresAt, input.rotatedResponseCiphertext, input.rotatedResponseNonce, input.tokenId)
|
|
353
|
+
.run();
|
|
354
|
+
return Number(result.meta.changes ?? 0) > 0;
|
|
355
|
+
}
|
|
356
|
+
async getTokenByHashColumn(column, token) {
|
|
357
|
+
const subjectSelect = this.subjectSelectSql();
|
|
358
|
+
const rotationSelect = this.options.refreshRotationColumns
|
|
359
|
+
? `
|
|
360
|
+
rotated_to_token_id AS rotatedToTokenId,
|
|
361
|
+
refresh_reuse_expires_at AS refreshReuseExpiresAt,
|
|
362
|
+
rotated_response_ciphertext AS rotatedResponseCiphertext,
|
|
363
|
+
rotated_response_nonce AS rotatedResponseNonce,
|
|
364
|
+
`
|
|
365
|
+
: "";
|
|
366
|
+
const row = await this.db
|
|
367
|
+
.prepare(`
|
|
368
|
+
SELECT
|
|
369
|
+
token_id AS tokenId,
|
|
370
|
+
client_id AS clientId,
|
|
371
|
+
${subjectSelect}
|
|
372
|
+
scopes_json AS scopesJson,
|
|
373
|
+
resource,
|
|
374
|
+
access_expires_at AS accessExpiresAt,
|
|
375
|
+
refresh_expires_at AS refreshExpiresAt,
|
|
376
|
+
revoked_at AS revokedAt,
|
|
377
|
+
${rotationSelect}
|
|
378
|
+
token_id AS tokenIdAgain
|
|
379
|
+
FROM mcp_oauth_tokens
|
|
380
|
+
WHERE ${column} = ?
|
|
381
|
+
LIMIT 1
|
|
382
|
+
`)
|
|
383
|
+
.bind(await sha256Hex(token))
|
|
384
|
+
.first();
|
|
385
|
+
return row ? tokenFromRow(row, this.options.subjectColumns) : null;
|
|
386
|
+
}
|
|
387
|
+
subjectSelectSql() {
|
|
388
|
+
return this.options.subjectColumns.map((column) => `${quoteIdentifier(column.column)} AS ${quoteIdentifier(column.field)},`).join("\n ");
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
export async function issueMcpOAuthTokenSet(repository, input, options = {}) {
|
|
392
|
+
const accessToken = `${createToken("mcpat")}.${crypto.randomUUID().replace(/-/g, "")}`;
|
|
393
|
+
const refreshToken = `${createToken("mcprt")}.${crypto.randomUUID().replace(/-/g, "")}`;
|
|
394
|
+
const issuedAt = options.now?.() ?? nowEpochSeconds();
|
|
395
|
+
const accessExpiresAt = issuedAt + (options.accessTokenTtlSeconds ?? DEFAULT_ACCESS_TOKEN_TTL_SECONDS);
|
|
396
|
+
const refreshExpiresAt = issuedAt + (options.refreshTokenTtlSeconds ?? DEFAULT_REFRESH_TOKEN_TTL_SECONDS);
|
|
397
|
+
const tokenId = await repository.createTokenSet({
|
|
398
|
+
clientId: input.clientId,
|
|
399
|
+
subject: input.subject,
|
|
400
|
+
accessToken,
|
|
401
|
+
refreshToken,
|
|
402
|
+
scopes: input.scopes,
|
|
403
|
+
resource: input.resource?.href ?? null,
|
|
404
|
+
accessExpiresAt,
|
|
405
|
+
refreshExpiresAt,
|
|
406
|
+
});
|
|
407
|
+
return {
|
|
408
|
+
tokenId,
|
|
409
|
+
accessExpiresAt,
|
|
410
|
+
refreshExpiresAt,
|
|
411
|
+
tokens: {
|
|
412
|
+
access_token: accessToken,
|
|
413
|
+
token_type: "Bearer",
|
|
414
|
+
expires_in: options.accessTokenTtlSeconds ?? DEFAULT_ACCESS_TOKEN_TTL_SECONDS,
|
|
415
|
+
refresh_token: refreshToken,
|
|
416
|
+
scope: input.scopes.join(" "),
|
|
417
|
+
},
|
|
418
|
+
};
|
|
419
|
+
}
|
|
420
|
+
export function parseOAuthScope(scope) {
|
|
421
|
+
return scope?.split(/\s+/).filter(Boolean) ?? [];
|
|
422
|
+
}
|
|
423
|
+
export function parseOAuthJsonArray(value) {
|
|
424
|
+
if (!value)
|
|
425
|
+
return [];
|
|
426
|
+
try {
|
|
427
|
+
const parsed = JSON.parse(value);
|
|
428
|
+
return Array.isArray(parsed) ? parsed.filter((item) => typeof item === "string") : [];
|
|
429
|
+
}
|
|
430
|
+
catch {
|
|
431
|
+
return [];
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
export function createOAuthRedirect(redirectUri, params) {
|
|
435
|
+
const url = new URL(redirectUri);
|
|
436
|
+
for (const [key, value] of Object.entries(params)) {
|
|
437
|
+
if (value !== undefined)
|
|
438
|
+
url.searchParams.set(key, value);
|
|
439
|
+
}
|
|
440
|
+
return url.toString();
|
|
441
|
+
}
|
|
442
|
+
export async function encryptOAuthTokenResponse(tokens, secret) {
|
|
443
|
+
const nonce = crypto.getRandomValues(new Uint8Array(12));
|
|
444
|
+
const key = await tokenResponseEncryptionKey(secret);
|
|
445
|
+
const ciphertext = await crypto.subtle.encrypt({ name: "AES-GCM", iv: nonce }, key, new TextEncoder().encode(JSON.stringify(tokens)));
|
|
446
|
+
return {
|
|
447
|
+
ciphertext: toBase64Url(new Uint8Array(ciphertext)),
|
|
448
|
+
nonce: toBase64Url(nonce),
|
|
449
|
+
};
|
|
450
|
+
}
|
|
451
|
+
export async function decryptOAuthTokenResponse(ciphertext, nonce, secret) {
|
|
452
|
+
try {
|
|
453
|
+
const key = await tokenResponseEncryptionKey(secret);
|
|
454
|
+
const plaintext = await crypto.subtle.decrypt({ name: "AES-GCM", iv: toArrayBuffer(fromBase64Url(nonce)) }, key, toArrayBuffer(fromBase64Url(ciphertext)));
|
|
455
|
+
return JSON.parse(new TextDecoder().decode(plaintext));
|
|
456
|
+
}
|
|
457
|
+
catch {
|
|
458
|
+
return null;
|
|
459
|
+
}
|
|
460
|
+
}
|
|
151
461
|
function setExtraSearchParams(url, params) {
|
|
152
462
|
for (const [key, value] of Object.entries(params ?? {})) {
|
|
153
463
|
if (value !== undefined)
|
|
154
464
|
url.searchParams.set(key, value);
|
|
155
465
|
}
|
|
156
466
|
}
|
|
467
|
+
function clientFromRow(row) {
|
|
468
|
+
return {
|
|
469
|
+
client_id: row.clientId,
|
|
470
|
+
client_secret: row.clientSecret ?? undefined,
|
|
471
|
+
client_name: row.clientName ?? undefined,
|
|
472
|
+
redirect_uris: parseOAuthJsonArray(row.redirectUrisJson),
|
|
473
|
+
scope: row.scope ?? undefined,
|
|
474
|
+
grant_types: parseOAuthJsonArray(row.grantTypesJson),
|
|
475
|
+
response_types: parseOAuthJsonArray(row.responseTypesJson),
|
|
476
|
+
token_endpoint_auth_method: row.tokenEndpointAuthMethod ?? undefined,
|
|
477
|
+
client_uri: row.clientUri ?? undefined,
|
|
478
|
+
logo_uri: row.logoUri ?? undefined,
|
|
479
|
+
contacts: parseOAuthJsonArray(row.contactsJson),
|
|
480
|
+
tos_uri: row.tosUri ?? undefined,
|
|
481
|
+
policy_uri: row.policyUri ?? undefined,
|
|
482
|
+
software_id: row.softwareId ?? undefined,
|
|
483
|
+
software_version: row.softwareVersion ?? undefined,
|
|
484
|
+
client_id_issued_at: row.clientIdIssuedAt ?? undefined,
|
|
485
|
+
client_secret_expires_at: row.clientSecretExpiresAt ?? undefined,
|
|
486
|
+
};
|
|
487
|
+
}
|
|
488
|
+
function authorizationCodeFromRow(row, subjectColumns) {
|
|
489
|
+
return {
|
|
490
|
+
authorizationCodeId: row.authorizationCodeId,
|
|
491
|
+
clientId: row.clientId,
|
|
492
|
+
redirectUri: row.redirectUri,
|
|
493
|
+
scopesJson: row.scopesJson,
|
|
494
|
+
resource: row.resource,
|
|
495
|
+
codeChallenge: row.codeChallenge,
|
|
496
|
+
expiresAt: row.expiresAt,
|
|
497
|
+
usedAt: row.usedAt,
|
|
498
|
+
subject: subjectFromRow(row, subjectColumns),
|
|
499
|
+
};
|
|
500
|
+
}
|
|
501
|
+
function tokenFromRow(row, subjectColumns) {
|
|
502
|
+
return {
|
|
503
|
+
tokenId: row.tokenId,
|
|
504
|
+
clientId: row.clientId,
|
|
505
|
+
scopesJson: row.scopesJson,
|
|
506
|
+
resource: row.resource,
|
|
507
|
+
accessExpiresAt: row.accessExpiresAt,
|
|
508
|
+
refreshExpiresAt: row.refreshExpiresAt,
|
|
509
|
+
revokedAt: row.revokedAt,
|
|
510
|
+
subject: subjectFromRow(row, subjectColumns),
|
|
511
|
+
rotatedToTokenId: row.rotatedToTokenId,
|
|
512
|
+
refreshReuseExpiresAt: row.refreshReuseExpiresAt,
|
|
513
|
+
rotatedResponseCiphertext: row.rotatedResponseCiphertext,
|
|
514
|
+
rotatedResponseNonce: row.rotatedResponseNonce,
|
|
515
|
+
};
|
|
516
|
+
}
|
|
517
|
+
function subjectFromRow(row, subjectColumns) {
|
|
518
|
+
const subject = {};
|
|
519
|
+
for (const column of subjectColumns) {
|
|
520
|
+
const value = row[column.field];
|
|
521
|
+
if (typeof value === "string")
|
|
522
|
+
subject[column.field] = value;
|
|
523
|
+
}
|
|
524
|
+
return subject;
|
|
525
|
+
}
|
|
526
|
+
function requiredSubject(subject, field) {
|
|
527
|
+
const value = subject[field];
|
|
528
|
+
if (!value)
|
|
529
|
+
throw new Error(`Missing OAuth subject field: ${field}`);
|
|
530
|
+
return value;
|
|
531
|
+
}
|
|
532
|
+
function jsonArray(value) {
|
|
533
|
+
return JSON.stringify(value ?? []);
|
|
534
|
+
}
|
|
535
|
+
function nowEpochSeconds() {
|
|
536
|
+
return Math.floor(Date.now() / 1000);
|
|
537
|
+
}
|
|
538
|
+
function quoteIdentifier(value) {
|
|
539
|
+
if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(value)) {
|
|
540
|
+
throw new Error(`Unsafe SQL identifier: ${value}`);
|
|
541
|
+
}
|
|
542
|
+
return `"${value}"`;
|
|
543
|
+
}
|
|
544
|
+
async function tokenResponseEncryptionKey(secret) {
|
|
545
|
+
const digest = await crypto.subtle.digest("SHA-256", new TextEncoder().encode(secret));
|
|
546
|
+
return crypto.subtle.importKey("raw", digest, { name: "AES-GCM" }, false, ["encrypt", "decrypt"]);
|
|
547
|
+
}
|
|
548
|
+
function toArrayBuffer(bytes) {
|
|
549
|
+
return bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength);
|
|
550
|
+
}
|
|
157
551
|
//# sourceMappingURL=oauth.js.map
|
package/dist/oauth.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"oauth.js","sourceRoot":"","sources":["../src/oauth.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"oauth.js","sourceRoot":"","sources":["../src/oauth.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AAEzC,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AACtC,OAAO,EAAE,QAAQ,EAAE,WAAW,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAY7E,MAAM,UAAU,gCAAgC,CAAC,OAA6B;IAC5E,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;IACjD,OAAO;QACL,MAAM;QACN,sBAAsB,EAAE,GAAG,MAAM,GAAG,OAAO,CAAC,iBAAiB,IAAI,YAAY,EAAE;QAC/E,cAAc,EAAE,GAAG,MAAM,GAAG,OAAO,CAAC,SAAS,IAAI,QAAQ,EAAE;QAC3D,qBAAqB,EAAE,GAAG,MAAM,GAAG,OAAO,CAAC,gBAAgB,IAAI,WAAW,EAAE;QAC5E,mBAAmB,EAAE,GAAG,MAAM,GAAG,OAAO,CAAC,cAAc,IAAI,SAAS,EAAE;QACtE,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,CAAC;QAC1C,gBAAgB,EAAE,OAAO,CAAC,MAAM,IAAI,CAAC,UAAU,EAAE,WAAW,CAAC;QAC7D,sBAAsB,EAAE,OAAO,CAAC,qBAAqB,IAAI,GAAG,MAAM,MAAM;KACzE,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,8BAA8B,CAAC,QAAgB,EAAE,mBAA2B,EAAE,MAAiB;IAC7G,OAAO;QACL,QAAQ;QACR,qBAAqB,EAAE,CAAC,mBAAmB,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;QAC/D,gBAAgB,EAAE,MAAM,IAAI,CAAC,UAAU,EAAE,WAAW,CAAC;QACrD,wBAAwB,EAAE,CAAC,QAAQ,CAAC;KACrC,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,OAA4D;IACxF,OAAO;QACL,IAAI,EAAE,OAAO,CAAC,IAAI;QAClB,WAAW,EAAE,OAAO,CAAC,WAAW;QAChC,GAAG,EAAE,OAAO,CAAC,GAAG;QAChB,SAAS,EAAE,MAAM;KAClB,CAAC;AACJ,CAAC;AAmHD,MAAM,2BAA2B,GAA4B,CAAC,EAAE,KAAK,EAAE,WAAW,EAAE,MAAM,EAAE,YAAY,EAAE,CAAC,CAAC;AAC5G,MAAM,6BAA6B,GAAG,EAAE,GAAG,EAAE,CAAC;AAC9C,MAAM,gCAAgC,GAAG,EAAE,GAAG,EAAE,CAAC;AACjD,MAAM,iCAAiC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC;AAE5D,MAAM,OAAO,iBAAiB;IACC;IAA7B,YAA6B,YAAuB,YAAY;QAAnC,cAAS,GAAT,SAAS,CAA0B;IAAG,CAAC;IAEpE,qBAAqB,CAAC,KAAiC;QACrD,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,8CAA8C,CAAC,CAAC;QACpE,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,WAAW,EAAE,KAAK,CAAC,QAAQ,CAAC,CAAC;QAClD,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,cAAc,EAAE,KAAK,CAAC,WAAW,CAAC,CAAC;QACxD,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,eAAe,EAAE,MAAM,CAAC,CAAC;QAC9C,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,OAAO,EAAE,KAAK,CAAC,KAAK,IAAI,sBAAsB,CAAC,CAAC;QACrE,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,OAAO,EAAE,KAAK,CAAC,KAAK,CAAC,CAAC;QAC3C,oBAAoB,CAAC,GAAG,EAAE,KAAK,CAAC,WAAW,CAAC,CAAC;QAC7C,OAAO,GAAG,CAAC,QAAQ,EAAE,CAAC;IACxB,CAAC;IAED,KAAK,CAAC,YAAY,CAAC,KAAuC;QACxD,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,SAAS,CAAC,qCAAqC,EAAE;YAC3E,MAAM,EAAE,MAAM;YACd,OAAO,EAAE;gBACP,MAAM,EAAE,kBAAkB;gBAC1B,cAAc,EAAE,mCAAmC;aACpD;YACD,IAAI,EAAE,IAAI,eAAe,CAAC;gBACxB,SAAS,EAAE,KAAK,CAAC,QAAQ;gBACzB,aAAa,EAAE,KAAK,CAAC,YAAY;gBACjC,IAAI,EAAE,KAAK,CAAC,IAAI;gBAChB,YAAY,EAAE,KAAK,CAAC,WAAW;gBAC/B,UAAU,EAAE,oBAAoB;aACjC,CAAC;SACH,CAAC,CAAC;QACH,IAAI,CAAC,QAAQ,CAAC,EAAE;YAAE,MAAM,IAAI,KAAK,CAAC,iCAAiC,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC;QACtF,MAAM,OAAO,GAAG,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,CAA8C,CAAC;QACrF,IAAI,CAAC,OAAO,CAAC,YAAY;YAAE,MAAM,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,IAAI,4CAA4C,CAAC,CAAC;QAC1G,OAAO,EAAE,WAAW,EAAE,OAAO,CAAC,YAAY,EAAE,CAAC;IAC/C,CAAC;IAED,KAAK,CAAC,gBAAgB,CAAC,WAAmB,EAAE,UAA8C,EAAE;QAC1F,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,SAAS,CAAC,kDAAkD,EAAE;YACxF,OAAO,EAAE;gBACP,MAAM,EAAE,kBAAkB;gBAC1B,aAAa,EAAE,UAAU,WAAW,EAAE;aACvC;SACF,CAAC,CAAC;QACH,IAAI,CAAC,QAAQ,CAAC,EAAE;YAAE,MAAM,IAAI,KAAK,CAAC,gCAAgC,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC;QACrF,MAAM,OAAO,GAAG,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAsB,CAAC;QAC7D,IAAI,CAAC,OAAO,CAAC,GAAG,IAAI,CAAC,OAAO,CAAC,KAAK;YAAE,MAAM,IAAI,KAAK,CAAC,wCAAwC,CAAC,CAAC;QAC9F,IAAI,OAAO,CAAC,oBAAoB,IAAI,OAAO,CAAC,cAAc,KAAK,KAAK;YAAE,MAAM,IAAI,KAAK,CAAC,iDAAiD,CAAC,CAAC;QACzI,OAAO,OAAO,CAAC;IACjB,CAAC;CACF;AAED,MAAM,OAAO,iBAAiB;IAET;IADnB,YACmB,UAGb,EAAE;QAHW,YAAO,GAAP,OAAO,CAGlB;IACL,CAAC;IAEJ,qBAAqB,CAAC,KAAiC;QACrD,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,0CAA0C,CAAC,CAAC;QAChE,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,WAAW,EAAE,KAAK,CAAC,QAAQ,CAAC,CAAC;QAClD,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,cAAc,EAAE,KAAK,CAAC,WAAW,CAAC,CAAC;QACxD,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,OAAO,EAAE,KAAK,CAAC,KAAK,IAAI,sBAAsB,CAAC,CAAC;QACrE,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,OAAO,EAAE,KAAK,CAAC,KAAK,CAAC,CAAC;QAC3C,oBAAoB,CAAC,GAAG,EAAE,KAAK,CAAC,WAAW,CAAC,CAAC;QAC7C,OAAO,GAAG,CAAC,QAAQ,EAAE,CAAC;IACxB,CAAC;IAED,KAAK,CAAC,YAAY,CAAC,KAA6B;QAC9C,MAAM,QAAQ,GAAG,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,KAAK,IAAI,YAAY,CAAC,CAAC,6CAA6C,EAAE;YACzG,MAAM,EAAE,MAAM;YACd,OAAO,EAAE;gBACP,MAAM,EAAE,kBAAkB;gBAC1B,cAAc,EAAE,kBAAkB;aACnC;YACD,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC;gBACnB,SAAS,EAAE,KAAK,CAAC,QAAQ;gBACzB,aAAa,EAAE,KAAK,CAAC,YAAY;gBACjC,IAAI,EAAE,KAAK,CAAC,IAAI;gBAChB,GAAG,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC,CAAC,EAAE,YAAY,EAAE,KAAK,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;aAClE,CAAC;SACH,CAAC,CAAC;QACH,IAAI,CAAC,QAAQ,CAAC,EAAE;YAAE,MAAM,IAAI,KAAK,CAAC,iCAAiC,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC;QACtF,MAAM,OAAO,GAAG,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,CAA8C,CAAC;QACrF,IAAI,CAAC,OAAO,CAAC,YAAY;YAAE,MAAM,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,IAAI,4CAA4C,CAAC,CAAC;QAC1G,OAAO,EAAE,WAAW,EAAE,OAAO,CAAC,YAAY,EAAE,CAAC;IAC/C,CAAC;IAED,KAAK,CAAC,gBAAgB,CAAC,WAAmB;QACxC,MAAM,QAAQ,GAAG,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,KAAK,IAAI,YAAY,CAAC,CAAC,6BAA6B,EAAE;YACzF,OAAO,EAAE,IAAI,CAAC,aAAa,CAAC,WAAW,CAAC;SACzC,CAAC,CAAC;QACH,IAAI,CAAC,QAAQ,CAAC,EAAE;YAAE,MAAM,IAAI,KAAK,CAAC,gCAAgC,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC;QACrF,MAAM,OAAO,GAAG,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAsB,CAAC;QAC7D,IAAI,CAAC,OAAO,CAAC,EAAE,IAAI,CAAC,OAAO,CAAC,KAAK;YAAE,MAAM,IAAI,KAAK,CAAC,wCAAwC,CAAC,CAAC;QAC7F,OAAO,OAAO,CAAC;IACjB,CAAC;IAED,KAAK,CAAC,iBAAiB,CAAC,WAAmB;QACzC,MAAM,QAAQ,GAAG,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,KAAK,IAAI,YAAY,CAAC,CAAC,oCAAoC,EAAE;YAChG,OAAO,EAAE,IAAI,CAAC,aAAa,CAAC,WAAW,CAAC;SACzC,CAAC,CAAC;QACH,IAAI,CAAC,QAAQ,CAAC,EAAE;YAAE,OAAO,IAAI,CAAC;QAC9B,MAAM,MAAM,GAAG,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,CAInC,CAAC;QACH,OAAO,MAAM,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,OAAO,IAAI,KAAK,CAAC,QAAQ,CAAC,EAAE,KAAK,IAAI,MAAM,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,QAAQ,CAAC,EAAE,KAAK,IAAI,IAAI,CAAC;IACjI,CAAC;IAEO,aAAa,CAAC,WAAmB;QACvC,OAAO;YACL,MAAM,EAAE,6BAA6B;YACrC,aAAa,EAAE,UAAU,WAAW,EAAE;YACtC,YAAY,EAAE,IAAI,CAAC,OAAO,CAAC,SAAS,IAAI,WAAW;SACpD,CAAC;IACJ,CAAC;CACF;AAED,MAAM,OAAO,kBAAkB;IAIV;IAHF,OAAO,CAAsI;IAE9J,YACmB,EAAkB,EACnC,UAAqC,EAAE;QADtB,OAAE,GAAF,EAAE,CAAgB;QAGnC,IAAI,CAAC,OAAO,GAAG;YACb,YAAY,EAAE,OAAO,CAAC,YAAY,IAAI,UAAU;YAChD,uBAAuB,EAAE,OAAO,CAAC,uBAAuB,IAAI,MAAM;YAClE,cAAc,EAAE,OAAO,CAAC,cAAc,IAAI,2BAA2B;YACrE,sBAAsB,EAAE,OAAO,CAAC,sBAAsB,IAAI,KAAK;SAChE,CAAC;IACJ,CAAC;IAED,KAAK,CAAC,SAAS,CAAC,QAAgB;QAC9B,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,EAAE;aACtB,OAAO,CACN;;;;;;;;;;;;;;;;;;;;;;OAsBD,CACA;aACA,IAAI,CAAC,QAAQ,CAAC;aACd,KAAK,EAAkB,CAAC;QAE3B,IAAI,CAAC,GAAG;YAAE,OAAO,SAAS,CAAC;QAC3B,OAAO,aAAa,CAAC,GAAG,CAAC,CAAC;IAC5B,CAAC;IAED,KAAK,CAAC,cAAc,CAAC,MAA+B;QAClD,MAAM,QAAQ,GAAG,QAAQ,CAAC,QAAQ,CAAC,CAAC;QACpC,MAAM,gBAAgB,GAAG,eAAe,EAAE,CAAC;QAC3C,MAAM,IAAI,CAAC,EAAE;aACV,OAAO,CACN;;;;;;;;;;;;;;;;;;;;;OAqBD,CACA;aACA,IAAI,CACH,QAAQ,EACR,MAAM,CAAC,aAAa,IAAI,IAAI,EAC5B,MAAM,CAAC,WAAW,IAAI,IAAI,EAC1B,SAAS,CAAC,MAAM,CAAC,aAAa,CAAC,EAC/B,MAAM,CAAC,KAAK,IAAI,IAAI,CAAC,OAAO,CAAC,YAAY,EACzC,SAAS,CAAC,MAAM,CAAC,WAAW,CAAC,EAC7B,SAAS,CAAC,MAAM,CAAC,cAAc,CAAC,EAChC,MAAM,CAAC,0BAA0B,IAAI,IAAI,CAAC,OAAO,CAAC,uBAAuB,EACzE,MAAM,CAAC,UAAU,IAAI,IAAI,EACzB,MAAM,CAAC,QAAQ,IAAI,IAAI,EACvB,SAAS,CAAC,MAAM,CAAC,QAAQ,CAAC,EAC1B,MAAM,CAAC,OAAO,IAAI,IAAI,EACtB,MAAM,CAAC,UAAU,IAAI,IAAI,EACzB,MAAM,CAAC,WAAW,IAAI,IAAI,EAC1B,MAAM,CAAC,gBAAgB,IAAI,IAAI,EAC/B,gBAAgB,EAChB,MAAM,CAAC,wBAAwB,IAAI,IAAI,CACxC;aACA,GAAG,EAAE,CAAC;QAET,MAAM,UAAU,GAAG,MAAM,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC;QAClD,IAAI,CAAC,UAAU;YAAE,MAAM,IAAI,KAAK,CAAC,iCAAiC,CAAC,CAAC;QACpE,OAAO,UAAU,CAAC;IACpB,CAAC;IAED,KAAK,CAAC,uBAAuB,CAAC,KAS7B;QACC,MAAM,cAAc,GAAG,IAAI,CAAC,OAAO,CAAC,cAAc,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,eAAe,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC;QACnG,MAAM,aAAa,GAAG,IAAI,CAAC,OAAO,CAAC,cAAc,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,eAAe,CAAC,KAAK,CAAC,OAAO,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;QAChH,MAAM,IAAI,CAAC,EAAE;aACV,OAAO,CACN;;;;;YAKI,cAAc,CAAC,IAAI,CAAC,eAAe,CAAC;;;;;;;oBAO5B,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,aAAa,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,mBAAmB,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC;OACtH,CACA;aACA,IAAI,CACH,QAAQ,CAAC,OAAO,CAAC,EACjB,MAAM,SAAS,CAAC,KAAK,CAAC,IAAI,CAAC,EAC3B,KAAK,CAAC,QAAQ,EACd,GAAG,aAAa,EAChB,KAAK,CAAC,WAAW,EACjB,SAAS,CAAC,KAAK,CAAC,MAAM,CAAC,EACvB,KAAK,CAAC,QAAQ,EACd,KAAK,CAAC,aAAa,EACnB,KAAK,CAAC,SAAS,IAAI,eAAe,EAAE,GAAG,6BAA6B,CACrE;aACA,GAAG,EAAE,CAAC;IACX,CAAC;IAED,KAAK,CAAC,oBAAoB,CAAC,IAAY;QACrC,MAAM,aAAa,GAAG,IAAI,CAAC,gBAAgB,EAAE,CAAC;QAC9C,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,EAAE;aACtB,OAAO,CACN;;;;YAII,aAAa;;;;;;;;;;OAUlB,CACA;aACA,IAAI,CAAC,MAAM,SAAS,CAAC,IAAI,CAAC,CAAC;aAC3B,KAAK,EAA+B,CAAC;QACxC,OAAO,GAAG,CAAC,CAAC,CAAC,wBAAwB,CAAC,GAAG,EAAE,IAAI,CAAC,OAAO,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IACjF,CAAC;IAED,KAAK,CAAC,wBAAwB,CAAC,mBAA2B;QACxD,MAAM,IAAI,CAAC,EAAE;aACV,OAAO,CACN;;;;OAID,CACA;aACA,IAAI,CAAC,mBAAmB,CAAC;aACzB,GAAG,EAAE,CAAC;IACX,CAAC;IAED,KAAK,CAAC,cAAc,CAAC,KASpB;QACC,MAAM,cAAc,GAAG,IAAI,CAAC,OAAO,CAAC,cAAc,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,eAAe,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC;QACnG,MAAM,aAAa,GAAG,IAAI,CAAC,OAAO,CAAC,cAAc,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,eAAe,CAAC,KAAK,CAAC,OAAO,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;QAChH,MAAM,OAAO,GAAG,QAAQ,CAAC,QAAQ,CAAC,CAAC;QACnC,MAAM,IAAI,CAAC,EAAE;aACV,OAAO,CACN;;;;YAII,cAAc,CAAC,IAAI,CAAC,eAAe,CAAC;;;;;;;;oBAQ5B,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,aAAa,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,mBAAmB,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC;OACtH,CACA;aACA,IAAI,CACH,OAAO,EACP,KAAK,CAAC,QAAQ,EACd,GAAG,aAAa,EAChB,MAAM,SAAS,CAAC,KAAK,CAAC,WAAW,CAAC,EAClC,KAAK,CAAC,YAAY,CAAC,CAAC,CAAC,MAAM,SAAS,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,IAAI,EAC/D,SAAS,CAAC,KAAK,CAAC,MAAM,CAAC,EACvB,KAAK,CAAC,QAAQ,EACd,KAAK,CAAC,eAAe,EACrB,KAAK,CAAC,gBAAgB,CACvB;aACA,GAAG,EAAE,CAAC;QACT,OAAO,OAAO,CAAC;IACjB,CAAC;IAED,KAAK,CAAC,qBAAqB,CAAC,KAAa;QACvC,OAAO,IAAI,CAAC,oBAAoB,CAAC,mBAAmB,EAAE,KAAK,CAAC,CAAC;IAC/D,CAAC;IAED,KAAK,CAAC,sBAAsB,CAAC,KAAa;QACxC,OAAO,IAAI,CAAC,oBAAoB,CAAC,oBAAoB,EAAE,KAAK,CAAC,CAAC;IAChE,CAAC;IAED,KAAK,CAAC,WAAW,CAAC,OAAe;QAC/B,MAAM,IAAI,CAAC,EAAE;aACV,OAAO,CACN;;;;OAID,CACA;aACA,IAAI,CAAC,OAAO,CAAC;aACb,GAAG,EAAE,CAAC;IACX,CAAC;IAED,KAAK,CAAC,gBAAgB,CAAC,OAAe;QACpC,MAAM,IAAI,CAAC,EAAE;aACV,OAAO,CACN;;;;OAID,CACA;aACA,IAAI,CAAC,OAAO,CAAC;aACb,GAAG,EAAE,CAAC;IACX,CAAC;IAED,KAAK,CAAC,gBAAgB,CAAC,KAMtB;QACC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,sBAAsB,EAAE,CAAC;YACzC,MAAM,IAAI,CAAC,WAAW,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;YACtC,OAAO,IAAI,CAAC;QACd,CAAC;QACD,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,EAAE;aACzB,OAAO,CACN;;;;;;;;;;OAUD,CACA;aACA,IAAI,CAAC,KAAK,CAAC,gBAAgB,EAAE,KAAK,CAAC,qBAAqB,EAAE,KAAK,CAAC,yBAAyB,EAAE,KAAK,CAAC,oBAAoB,EAAE,KAAK,CAAC,OAAO,CAAC;aACrI,GAAG,EAAE,CAAC;QACT,OAAO,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC;IAC9C,CAAC;IAEO,KAAK,CAAC,oBAAoB,CAAC,MAAkD,EAAE,KAAa;QAClG,MAAM,aAAa,GAAG,IAAI,CAAC,gBAAgB,EAAE,CAAC;QAC9C,MAAM,cAAc,GAAG,IAAI,CAAC,OAAO,CAAC,sBAAsB;YACxD,CAAC,CAAC;;;;;SAKC;YACH,CAAC,CAAC,EAAE,CAAC;QACP,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,EAAE;aACtB,OAAO,CACN;;;;YAII,aAAa;;;;;;YAMb,cAAc;;;gBAGV,MAAM;;OAEf,CACA;aACA,IAAI,CAAC,MAAM,SAAS,CAAC,KAAK,CAAC,CAAC;aAC5B,KAAK,EAAmB,CAAC;QAC5B,OAAO,GAAG,CAAC,CAAC,CAAC,YAAY,CAAC,GAAG,EAAE,IAAI,CAAC,OAAO,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IACrE,CAAC;IAEO,gBAAgB;QACtB,OAAO,IAAI,CAAC,OAAO,CAAC,cAAc,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,GAAG,eAAe,CAAC,MAAM,CAAC,MAAM,CAAC,OAAO,eAAe,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;IACpJ,CAAC;CACF;AAED,MAAM,CAAC,KAAK,UAAU,qBAAqB,CACzC,UAA8B,EAC9B,KAKC,EACD,UAAgC,EAAE;IAElC,MAAM,WAAW,GAAG,GAAG,WAAW,CAAC,OAAO,CAAC,IAAI,MAAM,CAAC,UAAU,EAAE,CAAC,OAAO,CAAC,IAAI,EAAE,EAAE,CAAC,EAAE,CAAC;IACvF,MAAM,YAAY,GAAG,GAAG,WAAW,CAAC,OAAO,CAAC,IAAI,MAAM,CAAC,UAAU,EAAE,CAAC,OAAO,CAAC,IAAI,EAAE,EAAE,CAAC,EAAE,CAAC;IACxF,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,EAAE,EAAE,IAAI,eAAe,EAAE,CAAC;IACtD,MAAM,eAAe,GAAG,QAAQ,GAAG,CAAC,OAAO,CAAC,qBAAqB,IAAI,gCAAgC,CAAC,CAAC;IACvG,MAAM,gBAAgB,GAAG,QAAQ,GAAG,CAAC,OAAO,CAAC,sBAAsB,IAAI,iCAAiC,CAAC,CAAC;IAC1G,MAAM,OAAO,GAAG,MAAM,UAAU,CAAC,cAAc,CAAC;QAC9C,QAAQ,EAAE,KAAK,CAAC,QAAQ;QACxB,OAAO,EAAE,KAAK,CAAC,OAAO;QACtB,WAAW;QACX,YAAY;QACZ,MAAM,EAAE,KAAK,CAAC,MAAM;QACpB,QAAQ,EAAE,KAAK,CAAC,QAAQ,EAAE,IAAI,IAAI,IAAI;QACtC,eAAe;QACf,gBAAgB;KACjB,CAAC,CAAC;IACH,OAAO;QACL,OAAO;QACP,eAAe;QACf,gBAAgB;QAChB,MAAM,EAAE;YACN,YAAY,EAAE,WAAW;YACzB,UAAU,EAAE,QAAQ;YACpB,UAAU,EAAE,OAAO,CAAC,qBAAqB,IAAI,gCAAgC;YAC7E,aAAa,EAAE,YAAY;YAC3B,KAAK,EAAE,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC;SAC9B;KACF,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,KAAgC;IAC9D,OAAO,KAAK,EAAE,KAAK,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC;AACnD,CAAC;AAED,MAAM,UAAU,mBAAmB,CAAC,KAAgC;IAClE,IAAI,CAAC,KAAK;QAAE,OAAO,EAAE,CAAC;IACtB,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAY,CAAC;QAC5C,OAAO,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,EAAkB,EAAE,CAAC,OAAO,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IACxG,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAC;IACZ,CAAC;AACH,CAAC;AAED,MAAM,UAAU,mBAAmB,CAAC,WAAmB,EAAE,MAA0C;IACjG,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,WAAW,CAAC,CAAC;IACjC,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;QAClD,IAAI,KAAK,KAAK,SAAS;YAAE,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;IAC5D,CAAC;IACD,OAAO,GAAG,CAAC,QAAQ,EAAE,CAAC;AACxB,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,yBAAyB,CAAC,MAAmB,EAAE,MAAc;IACjF,MAAM,KAAK,GAAG,MAAM,CAAC,eAAe,CAAC,IAAI,UAAU,CAAC,EAAE,CAAC,CAAC,CAAC;IACzD,MAAM,GAAG,GAAG,MAAM,0BAA0B,CAAC,MAAM,CAAC,CAAC;IACrD,MAAM,UAAU,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,GAAG,EAAE,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;IACtI,OAAO;QACL,UAAU,EAAE,WAAW,CAAC,IAAI,UAAU,CAAC,UAAU,CAAC,CAAC;QACnD,KAAK,EAAE,WAAW,CAAC,KAAK,CAAC;KAC1B,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,yBAAyB,CAAC,UAAkB,EAAE,KAAa,EAAE,MAAc;IAC/F,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,0BAA0B,CAAC,MAAM,CAAC,CAAC;QACrD,MAAM,SAAS,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,EAAE,EAAE,aAAa,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,GAAG,EAAE,aAAa,CAAC,aAAa,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;QAC3J,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,SAAS,CAAC,CAAgB,CAAC;IACxE,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED,SAAS,oBAAoB,CAAC,GAAQ,EAAE,MAAsD;IAC5F,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,IAAI,EAAE,CAAC,EAAE,CAAC;QACxD,IAAI,KAAK,KAAK,SAAS;YAAE,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;IAC5D,CAAC;AACH,CAAC;AA+CD,SAAS,aAAa,CAAC,GAAmB;IACxC,OAAO;QACL,SAAS,EAAE,GAAG,CAAC,QAAQ;QACvB,aAAa,EAAE,GAAG,CAAC,YAAY,IAAI,SAAS;QAC5C,WAAW,EAAE,GAAG,CAAC,UAAU,IAAI,SAAS;QACxC,aAAa,EAAE,mBAAmB,CAAC,GAAG,CAAC,gBAAgB,CAAC;QACxD,KAAK,EAAE,GAAG,CAAC,KAAK,IAAI,SAAS;QAC7B,WAAW,EAAE,mBAAmB,CAAC,GAAG,CAAC,cAAc,CAAC;QACpD,cAAc,EAAE,mBAAmB,CAAC,GAAG,CAAC,iBAAiB,CAAC;QAC1D,0BAA0B,EAAE,GAAG,CAAC,uBAAuB,IAAI,SAAS;QACpE,UAAU,EAAE,GAAG,CAAC,SAAS,IAAI,SAAS;QACtC,QAAQ,EAAE,GAAG,CAAC,OAAO,IAAI,SAAS;QAClC,QAAQ,EAAE,mBAAmB,CAAC,GAAG,CAAC,YAAY,CAAC;QAC/C,OAAO,EAAE,GAAG,CAAC,MAAM,IAAI,SAAS;QAChC,UAAU,EAAE,GAAG,CAAC,SAAS,IAAI,SAAS;QACtC,WAAW,EAAE,GAAG,CAAC,UAAU,IAAI,SAAS;QACxC,gBAAgB,EAAE,GAAG,CAAC,eAAe,IAAI,SAAS;QAClD,mBAAmB,EAAE,GAAG,CAAC,gBAAgB,IAAI,SAAS;QACtD,wBAAwB,EAAE,GAAG,CAAC,qBAAqB,IAAI,SAAS;KACjE,CAAC;AACJ,CAAC;AAED,SAAS,wBAAwB,CAAC,GAAgC,EAAE,cAAuC;IACzG,OAAO;QACL,mBAAmB,EAAE,GAAG,CAAC,mBAAmB;QAC5C,QAAQ,EAAE,GAAG,CAAC,QAAQ;QACtB,WAAW,EAAE,GAAG,CAAC,WAAW;QAC5B,UAAU,EAAE,GAAG,CAAC,UAAU;QAC1B,QAAQ,EAAE,GAAG,CAAC,QAAQ;QACtB,aAAa,EAAE,GAAG,CAAC,aAAa;QAChC,SAAS,EAAE,GAAG,CAAC,SAAS;QACxB,MAAM,EAAE,GAAG,CAAC,MAAM;QAClB,OAAO,EAAE,cAAc,CAAC,GAAG,EAAE,cAAc,CAAC;KAC7C,CAAC;AACJ,CAAC;AAED,SAAS,YAAY,CAAC,GAAoB,EAAE,cAAuC;IACjF,OAAO;QACL,OAAO,EAAE,GAAG,CAAC,OAAO;QACpB,QAAQ,EAAE,GAAG,CAAC,QAAQ;QACtB,UAAU,EAAE,GAAG,CAAC,UAAU;QAC1B,QAAQ,EAAE,GAAG,CAAC,QAAQ;QACtB,eAAe,EAAE,GAAG,CAAC,eAAe;QACpC,gBAAgB,EAAE,GAAG,CAAC,gBAAgB;QACtC,SAAS,EAAE,GAAG,CAAC,SAAS;QACxB,OAAO,EAAE,cAAc,CAAC,GAAG,EAAE,cAAc,CAAC;QAC5C,gBAAgB,EAAE,GAAG,CAAC,gBAAgB;QACtC,qBAAqB,EAAE,GAAG,CAAC,qBAAqB;QAChD,yBAAyB,EAAE,GAAG,CAAC,yBAAyB;QACxD,oBAAoB,EAAE,GAAG,CAAC,oBAAoB;KAC/C,CAAC;AACJ,CAAC;AAED,SAAS,cAAc,CAAC,GAAuD,EAAE,cAAuC;IACtH,MAAM,OAAO,GAA2B,EAAE,CAAC;IAC3C,KAAK,MAAM,MAAM,IAAI,cAAc,EAAE,CAAC;QACpC,MAAM,KAAK,GAAG,GAAG,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QAChC,IAAI,OAAO,KAAK,KAAK,QAAQ;YAAE,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,KAAK,CAAC;IAC/D,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,SAAS,eAAe,CAAC,OAA+B,EAAE,KAAa;IACrE,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC;IAC7B,IAAI,CAAC,KAAK;QAAE,MAAM,IAAI,KAAK,CAAC,gCAAgC,KAAK,EAAE,CAAC,CAAC;IACrE,OAAO,KAAK,CAAC;AACf,CAAC;AAED,SAAS,SAAS,CAAC,KAAkC;IACnD,OAAO,IAAI,CAAC,SAAS,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC;AACrC,CAAC;AAED,SAAS,eAAe;IACtB,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC;AACvC,CAAC;AAED,SAAS,eAAe,CAAC,KAAa;IACpC,IAAI,CAAC,0BAA0B,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;QAC5C,MAAM,IAAI,KAAK,CAAC,0BAA0B,KAAK,EAAE,CAAC,CAAC;IACrD,CAAC;IACD,OAAO,IAAI,KAAK,GAAG,CAAC;AACtB,CAAC;AAED,KAAK,UAAU,0BAA0B,CAAC,MAAc;IACtD,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,SAAS,EAAE,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC;IACvF,OAAO,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,EAAE,KAAK,EAAE,CAAC,SAAS,EAAE,SAAS,CAAC,CAAC,CAAC;AACpG,CAAC;AAED,SAAS,aAAa,CAAC,KAAiB;IACtC,OAAO,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,UAAU,EAAE,KAAK,CAAC,UAAU,GAAG,KAAK,CAAC,UAAU,CAAgB,CAAC;AAClG,CAAC"}
|
package/package.json
CHANGED
package/src/oauth.ts
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import type { FetchLike } from "./http.js";
|
|
2
2
|
import { defaultFetch } from "./http.js";
|
|
3
|
+
import type { D1DatabaseLike } from "./d1.js";
|
|
4
|
+
import { sha256Hex } from "./hash.js";
|
|
5
|
+
import { createId, createToken, fromBase64Url, toBase64Url } from "./ids.js";
|
|
3
6
|
|
|
4
7
|
export type OAuthMetadataOptions = {
|
|
5
8
|
issuer: string;
|
|
@@ -77,6 +80,93 @@ export type GitHubUserProfile = {
|
|
|
77
80
|
avatar_url: string | null;
|
|
78
81
|
};
|
|
79
82
|
|
|
83
|
+
export type OAuthClientInformation = {
|
|
84
|
+
client_id: string;
|
|
85
|
+
client_secret?: string;
|
|
86
|
+
client_name?: string;
|
|
87
|
+
redirect_uris?: string[];
|
|
88
|
+
scope?: string;
|
|
89
|
+
grant_types?: string[];
|
|
90
|
+
response_types?: string[];
|
|
91
|
+
token_endpoint_auth_method?: string;
|
|
92
|
+
client_uri?: string;
|
|
93
|
+
logo_uri?: string;
|
|
94
|
+
contacts?: string[];
|
|
95
|
+
tos_uri?: string;
|
|
96
|
+
policy_uri?: string;
|
|
97
|
+
software_id?: string;
|
|
98
|
+
software_version?: string;
|
|
99
|
+
client_id_issued_at?: number;
|
|
100
|
+
client_secret_expires_at?: number;
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
export type OAuthClientRegistration = Omit<OAuthClientInformation, "client_id" | "client_id_issued_at">;
|
|
104
|
+
|
|
105
|
+
export type OAuthTokens = {
|
|
106
|
+
access_token: string;
|
|
107
|
+
token_type: "Bearer";
|
|
108
|
+
expires_in: number;
|
|
109
|
+
refresh_token?: string;
|
|
110
|
+
scope?: string;
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
export type McpOAuthSubjectColumn = {
|
|
114
|
+
field: string;
|
|
115
|
+
column: string;
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
export type McpOAuthRepositoryOptions = {
|
|
119
|
+
defaultScope?: string;
|
|
120
|
+
tokenEndpointAuthMethod?: string;
|
|
121
|
+
subjectColumns?: McpOAuthSubjectColumn[];
|
|
122
|
+
refreshRotationColumns?: boolean;
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
export type McpAuthorizationCodeRow = {
|
|
126
|
+
authorizationCodeId: string;
|
|
127
|
+
clientId: string;
|
|
128
|
+
redirectUri: string;
|
|
129
|
+
scopesJson: string;
|
|
130
|
+
resource: string | null;
|
|
131
|
+
codeChallenge: string;
|
|
132
|
+
expiresAt: number;
|
|
133
|
+
usedAt: string | null;
|
|
134
|
+
subject: Record<string, string>;
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
export type McpTokenRow = {
|
|
138
|
+
tokenId: string;
|
|
139
|
+
clientId: string;
|
|
140
|
+
scopesJson: string;
|
|
141
|
+
resource: string | null;
|
|
142
|
+
accessExpiresAt: number;
|
|
143
|
+
refreshExpiresAt: number | null;
|
|
144
|
+
revokedAt: string | null;
|
|
145
|
+
subject: Record<string, string>;
|
|
146
|
+
rotatedToTokenId?: string | null;
|
|
147
|
+
refreshReuseExpiresAt?: number | null;
|
|
148
|
+
rotatedResponseCiphertext?: string | null;
|
|
149
|
+
rotatedResponseNonce?: string | null;
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
export type McpIssuedTokenSet = {
|
|
153
|
+
tokenId: string;
|
|
154
|
+
tokens: OAuthTokens;
|
|
155
|
+
accessExpiresAt: number;
|
|
156
|
+
refreshExpiresAt: number | null;
|
|
157
|
+
};
|
|
158
|
+
|
|
159
|
+
export type McpOAuthTokenOptions = {
|
|
160
|
+
accessTokenTtlSeconds?: number;
|
|
161
|
+
refreshTokenTtlSeconds?: number;
|
|
162
|
+
now?: () => number;
|
|
163
|
+
};
|
|
164
|
+
|
|
165
|
+
const DEFAULT_MCP_SUBJECT_COLUMNS: McpOAuthSubjectColumn[] = [{ field: "accountId", column: "account_id" }];
|
|
166
|
+
const DEFAULT_AUTH_CODE_TTL_SECONDS = 10 * 60;
|
|
167
|
+
const DEFAULT_ACCESS_TOKEN_TTL_SECONDS = 60 * 60;
|
|
168
|
+
const DEFAULT_REFRESH_TOKEN_TTL_SECONDS = 30 * 24 * 60 * 60;
|
|
169
|
+
|
|
80
170
|
export class GoogleOAuthClient {
|
|
81
171
|
constructor(private readonly fetchImpl: FetchLike = defaultFetch) {}
|
|
82
172
|
|
|
@@ -122,7 +212,7 @@ export class GoogleOAuthClient {
|
|
|
122
212
|
if (!response.ok) throw new Error(`Google profile fetch failed: ${response.status}`);
|
|
123
213
|
const profile = (await response.json()) as GoogleUserProfile;
|
|
124
214
|
if (!profile.sub || !profile.email) throw new Error("Google profile missing identity fields");
|
|
125
|
-
if (options.requireVerifiedEmail && profile.email_verified === false) throw new Error("Google profile
|
|
215
|
+
if (options.requireVerifiedEmail && profile.email_verified === false) throw new Error("Google profile missing verified identity fields");
|
|
126
216
|
return profile;
|
|
127
217
|
}
|
|
128
218
|
}
|
|
@@ -197,8 +287,558 @@ export class GitHubOAuthClient {
|
|
|
197
287
|
}
|
|
198
288
|
}
|
|
199
289
|
|
|
290
|
+
export class McpOAuthRepository {
|
|
291
|
+
private readonly options: Required<Pick<McpOAuthRepositoryOptions, "defaultScope" | "tokenEndpointAuthMethod" | "subjectColumns" | "refreshRotationColumns">>;
|
|
292
|
+
|
|
293
|
+
constructor(
|
|
294
|
+
private readonly db: D1DatabaseLike,
|
|
295
|
+
options: McpOAuthRepositoryOptions = {},
|
|
296
|
+
) {
|
|
297
|
+
this.options = {
|
|
298
|
+
defaultScope: options.defaultScope ?? "mcp:read",
|
|
299
|
+
tokenEndpointAuthMethod: options.tokenEndpointAuthMethod ?? "none",
|
|
300
|
+
subjectColumns: options.subjectColumns ?? DEFAULT_MCP_SUBJECT_COLUMNS,
|
|
301
|
+
refreshRotationColumns: options.refreshRotationColumns ?? false,
|
|
302
|
+
};
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
async getClient(clientId: string): Promise<OAuthClientInformation | undefined> {
|
|
306
|
+
const row = await this.db
|
|
307
|
+
.prepare(
|
|
308
|
+
`
|
|
309
|
+
SELECT
|
|
310
|
+
client_id AS clientId,
|
|
311
|
+
client_secret AS clientSecret,
|
|
312
|
+
client_name AS clientName,
|
|
313
|
+
redirect_uris_json AS redirectUrisJson,
|
|
314
|
+
scope,
|
|
315
|
+
grant_types_json AS grantTypesJson,
|
|
316
|
+
response_types_json AS responseTypesJson,
|
|
317
|
+
token_endpoint_auth_method AS tokenEndpointAuthMethod,
|
|
318
|
+
client_uri AS clientUri,
|
|
319
|
+
logo_uri AS logoUri,
|
|
320
|
+
contacts_json AS contactsJson,
|
|
321
|
+
tos_uri AS tosUri,
|
|
322
|
+
policy_uri AS policyUri,
|
|
323
|
+
software_id AS softwareId,
|
|
324
|
+
software_version AS softwareVersion,
|
|
325
|
+
client_id_issued_at AS clientIdIssuedAt,
|
|
326
|
+
client_secret_expires_at AS clientSecretExpiresAt
|
|
327
|
+
FROM mcp_oauth_clients
|
|
328
|
+
WHERE client_id = ?
|
|
329
|
+
LIMIT 1
|
|
330
|
+
`,
|
|
331
|
+
)
|
|
332
|
+
.bind(clientId)
|
|
333
|
+
.first<OAuthClientRow>();
|
|
334
|
+
|
|
335
|
+
if (!row) return undefined;
|
|
336
|
+
return clientFromRow(row);
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
async registerClient(client: OAuthClientRegistration): Promise<OAuthClientInformation> {
|
|
340
|
+
const clientId = createId("mcpcli");
|
|
341
|
+
const clientIdIssuedAt = nowEpochSeconds();
|
|
342
|
+
await this.db
|
|
343
|
+
.prepare(
|
|
344
|
+
`
|
|
345
|
+
INSERT INTO mcp_oauth_clients (
|
|
346
|
+
client_id,
|
|
347
|
+
client_secret,
|
|
348
|
+
client_name,
|
|
349
|
+
redirect_uris_json,
|
|
350
|
+
scope,
|
|
351
|
+
grant_types_json,
|
|
352
|
+
response_types_json,
|
|
353
|
+
token_endpoint_auth_method,
|
|
354
|
+
client_uri,
|
|
355
|
+
logo_uri,
|
|
356
|
+
contacts_json,
|
|
357
|
+
tos_uri,
|
|
358
|
+
policy_uri,
|
|
359
|
+
software_id,
|
|
360
|
+
software_version,
|
|
361
|
+
client_id_issued_at,
|
|
362
|
+
client_secret_expires_at,
|
|
363
|
+
updated_at
|
|
364
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, CURRENT_TIMESTAMP)
|
|
365
|
+
`,
|
|
366
|
+
)
|
|
367
|
+
.bind(
|
|
368
|
+
clientId,
|
|
369
|
+
client.client_secret ?? null,
|
|
370
|
+
client.client_name ?? null,
|
|
371
|
+
jsonArray(client.redirect_uris),
|
|
372
|
+
client.scope ?? this.options.defaultScope,
|
|
373
|
+
jsonArray(client.grant_types),
|
|
374
|
+
jsonArray(client.response_types),
|
|
375
|
+
client.token_endpoint_auth_method ?? this.options.tokenEndpointAuthMethod,
|
|
376
|
+
client.client_uri ?? null,
|
|
377
|
+
client.logo_uri ?? null,
|
|
378
|
+
jsonArray(client.contacts),
|
|
379
|
+
client.tos_uri ?? null,
|
|
380
|
+
client.policy_uri ?? null,
|
|
381
|
+
client.software_id ?? null,
|
|
382
|
+
client.software_version ?? null,
|
|
383
|
+
clientIdIssuedAt,
|
|
384
|
+
client.client_secret_expires_at ?? null,
|
|
385
|
+
)
|
|
386
|
+
.run();
|
|
387
|
+
|
|
388
|
+
const registered = await this.getClient(clientId);
|
|
389
|
+
if (!registered) throw new Error("registered OAuth client missing");
|
|
390
|
+
return registered;
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
async createAuthorizationCode(input: {
|
|
394
|
+
code: string;
|
|
395
|
+
clientId: string;
|
|
396
|
+
subject: Record<string, string>;
|
|
397
|
+
redirectUri: string;
|
|
398
|
+
scopes: string[];
|
|
399
|
+
resource: string | null;
|
|
400
|
+
codeChallenge: string;
|
|
401
|
+
expiresAt?: number;
|
|
402
|
+
}): Promise<void> {
|
|
403
|
+
const subjectColumns = this.options.subjectColumns.map((column) => quoteIdentifier(column.column));
|
|
404
|
+
const subjectValues = this.options.subjectColumns.map((column) => requiredSubject(input.subject, column.field));
|
|
405
|
+
await this.db
|
|
406
|
+
.prepare(
|
|
407
|
+
`
|
|
408
|
+
INSERT INTO mcp_oauth_authorization_codes (
|
|
409
|
+
authorization_code_id,
|
|
410
|
+
code_hash,
|
|
411
|
+
client_id,
|
|
412
|
+
${subjectColumns.join(",\n ")},
|
|
413
|
+
redirect_uri,
|
|
414
|
+
scopes_json,
|
|
415
|
+
resource,
|
|
416
|
+
code_challenge,
|
|
417
|
+
expires_at,
|
|
418
|
+
updated_at
|
|
419
|
+
) VALUES (${["?", "?", "?", ...subjectValues.map(() => "?"), "?", "?", "?", "?", "?", "CURRENT_TIMESTAMP"].join(", ")})
|
|
420
|
+
`,
|
|
421
|
+
)
|
|
422
|
+
.bind(
|
|
423
|
+
createId("mcpac"),
|
|
424
|
+
await sha256Hex(input.code),
|
|
425
|
+
input.clientId,
|
|
426
|
+
...subjectValues,
|
|
427
|
+
input.redirectUri,
|
|
428
|
+
jsonArray(input.scopes),
|
|
429
|
+
input.resource,
|
|
430
|
+
input.codeChallenge,
|
|
431
|
+
input.expiresAt ?? nowEpochSeconds() + DEFAULT_AUTH_CODE_TTL_SECONDS,
|
|
432
|
+
)
|
|
433
|
+
.run();
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
async getAuthorizationCode(code: string): Promise<McpAuthorizationCodeRow | null> {
|
|
437
|
+
const subjectSelect = this.subjectSelectSql();
|
|
438
|
+
const row = await this.db
|
|
439
|
+
.prepare(
|
|
440
|
+
`
|
|
441
|
+
SELECT
|
|
442
|
+
authorization_code_id AS authorizationCodeId,
|
|
443
|
+
client_id AS clientId,
|
|
444
|
+
${subjectSelect}
|
|
445
|
+
redirect_uri AS redirectUri,
|
|
446
|
+
scopes_json AS scopesJson,
|
|
447
|
+
resource,
|
|
448
|
+
code_challenge AS codeChallenge,
|
|
449
|
+
expires_at AS expiresAt,
|
|
450
|
+
used_at AS usedAt
|
|
451
|
+
FROM mcp_oauth_authorization_codes
|
|
452
|
+
WHERE code_hash = ?
|
|
453
|
+
LIMIT 1
|
|
454
|
+
`,
|
|
455
|
+
)
|
|
456
|
+
.bind(await sha256Hex(code))
|
|
457
|
+
.first<AuthorizationCodeStorageRow>();
|
|
458
|
+
return row ? authorizationCodeFromRow(row, this.options.subjectColumns) : null;
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
async consumeAuthorizationCode(authorizationCodeId: string): Promise<void> {
|
|
462
|
+
await this.db
|
|
463
|
+
.prepare(
|
|
464
|
+
`
|
|
465
|
+
UPDATE mcp_oauth_authorization_codes
|
|
466
|
+
SET used_at = CURRENT_TIMESTAMP, updated_at = CURRENT_TIMESTAMP
|
|
467
|
+
WHERE authorization_code_id = ?
|
|
468
|
+
`,
|
|
469
|
+
)
|
|
470
|
+
.bind(authorizationCodeId)
|
|
471
|
+
.run();
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
async createTokenSet(input: {
|
|
475
|
+
clientId: string;
|
|
476
|
+
subject: Record<string, string>;
|
|
477
|
+
accessToken: string;
|
|
478
|
+
refreshToken: string | null;
|
|
479
|
+
scopes: string[];
|
|
480
|
+
resource: string | null;
|
|
481
|
+
accessExpiresAt: number;
|
|
482
|
+
refreshExpiresAt: number | null;
|
|
483
|
+
}): Promise<string> {
|
|
484
|
+
const subjectColumns = this.options.subjectColumns.map((column) => quoteIdentifier(column.column));
|
|
485
|
+
const subjectValues = this.options.subjectColumns.map((column) => requiredSubject(input.subject, column.field));
|
|
486
|
+
const tokenId = createId("mcptok");
|
|
487
|
+
await this.db
|
|
488
|
+
.prepare(
|
|
489
|
+
`
|
|
490
|
+
INSERT INTO mcp_oauth_tokens (
|
|
491
|
+
token_id,
|
|
492
|
+
client_id,
|
|
493
|
+
${subjectColumns.join(",\n ")},
|
|
494
|
+
access_token_hash,
|
|
495
|
+
refresh_token_hash,
|
|
496
|
+
scopes_json,
|
|
497
|
+
resource,
|
|
498
|
+
access_expires_at,
|
|
499
|
+
refresh_expires_at,
|
|
500
|
+
updated_at
|
|
501
|
+
) VALUES (${["?", "?", ...subjectValues.map(() => "?"), "?", "?", "?", "?", "?", "?", "CURRENT_TIMESTAMP"].join(", ")})
|
|
502
|
+
`,
|
|
503
|
+
)
|
|
504
|
+
.bind(
|
|
505
|
+
tokenId,
|
|
506
|
+
input.clientId,
|
|
507
|
+
...subjectValues,
|
|
508
|
+
await sha256Hex(input.accessToken),
|
|
509
|
+
input.refreshToken ? await sha256Hex(input.refreshToken) : null,
|
|
510
|
+
jsonArray(input.scopes),
|
|
511
|
+
input.resource,
|
|
512
|
+
input.accessExpiresAt,
|
|
513
|
+
input.refreshExpiresAt,
|
|
514
|
+
)
|
|
515
|
+
.run();
|
|
516
|
+
return tokenId;
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
async getTokenByAccessToken(token: string): Promise<McpTokenRow | null> {
|
|
520
|
+
return this.getTokenByHashColumn("access_token_hash", token);
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
async getTokenByRefreshToken(token: string): Promise<McpTokenRow | null> {
|
|
524
|
+
return this.getTokenByHashColumn("refresh_token_hash", token);
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
async revokeToken(tokenId: string): Promise<void> {
|
|
528
|
+
await this.db
|
|
529
|
+
.prepare(
|
|
530
|
+
`
|
|
531
|
+
UPDATE mcp_oauth_tokens
|
|
532
|
+
SET revoked_at = CURRENT_TIMESTAMP, updated_at = CURRENT_TIMESTAMP
|
|
533
|
+
WHERE token_id = ?
|
|
534
|
+
`,
|
|
535
|
+
)
|
|
536
|
+
.bind(tokenId)
|
|
537
|
+
.run();
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
async touchAccessToken(tokenId: string): Promise<void> {
|
|
541
|
+
await this.db
|
|
542
|
+
.prepare(
|
|
543
|
+
`
|
|
544
|
+
UPDATE mcp_oauth_tokens
|
|
545
|
+
SET access_last_used_at = CURRENT_TIMESTAMP, updated_at = CURRENT_TIMESTAMP
|
|
546
|
+
WHERE token_id = ?
|
|
547
|
+
`,
|
|
548
|
+
)
|
|
549
|
+
.bind(tokenId)
|
|
550
|
+
.run();
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
async markTokenRotated(input: {
|
|
554
|
+
tokenId: string;
|
|
555
|
+
rotatedToTokenId: string;
|
|
556
|
+
refreshReuseExpiresAt: number;
|
|
557
|
+
rotatedResponseCiphertext: string;
|
|
558
|
+
rotatedResponseNonce: string;
|
|
559
|
+
}): Promise<boolean> {
|
|
560
|
+
if (!this.options.refreshRotationColumns) {
|
|
561
|
+
await this.revokeToken(input.tokenId);
|
|
562
|
+
return true;
|
|
563
|
+
}
|
|
564
|
+
const result = await this.db
|
|
565
|
+
.prepare(
|
|
566
|
+
`
|
|
567
|
+
UPDATE mcp_oauth_tokens
|
|
568
|
+
SET
|
|
569
|
+
revoked_at = CURRENT_TIMESTAMP,
|
|
570
|
+
rotated_to_token_id = ?,
|
|
571
|
+
refresh_reuse_expires_at = ?,
|
|
572
|
+
rotated_response_ciphertext = ?,
|
|
573
|
+
rotated_response_nonce = ?,
|
|
574
|
+
updated_at = CURRENT_TIMESTAMP
|
|
575
|
+
WHERE token_id = ? AND rotated_to_token_id IS NULL
|
|
576
|
+
`,
|
|
577
|
+
)
|
|
578
|
+
.bind(input.rotatedToTokenId, input.refreshReuseExpiresAt, input.rotatedResponseCiphertext, input.rotatedResponseNonce, input.tokenId)
|
|
579
|
+
.run();
|
|
580
|
+
return Number(result.meta.changes ?? 0) > 0;
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
private async getTokenByHashColumn(column: "access_token_hash" | "refresh_token_hash", token: string): Promise<McpTokenRow | null> {
|
|
584
|
+
const subjectSelect = this.subjectSelectSql();
|
|
585
|
+
const rotationSelect = this.options.refreshRotationColumns
|
|
586
|
+
? `
|
|
587
|
+
rotated_to_token_id AS rotatedToTokenId,
|
|
588
|
+
refresh_reuse_expires_at AS refreshReuseExpiresAt,
|
|
589
|
+
rotated_response_ciphertext AS rotatedResponseCiphertext,
|
|
590
|
+
rotated_response_nonce AS rotatedResponseNonce,
|
|
591
|
+
`
|
|
592
|
+
: "";
|
|
593
|
+
const row = await this.db
|
|
594
|
+
.prepare(
|
|
595
|
+
`
|
|
596
|
+
SELECT
|
|
597
|
+
token_id AS tokenId,
|
|
598
|
+
client_id AS clientId,
|
|
599
|
+
${subjectSelect}
|
|
600
|
+
scopes_json AS scopesJson,
|
|
601
|
+
resource,
|
|
602
|
+
access_expires_at AS accessExpiresAt,
|
|
603
|
+
refresh_expires_at AS refreshExpiresAt,
|
|
604
|
+
revoked_at AS revokedAt,
|
|
605
|
+
${rotationSelect}
|
|
606
|
+
token_id AS tokenIdAgain
|
|
607
|
+
FROM mcp_oauth_tokens
|
|
608
|
+
WHERE ${column} = ?
|
|
609
|
+
LIMIT 1
|
|
610
|
+
`,
|
|
611
|
+
)
|
|
612
|
+
.bind(await sha256Hex(token))
|
|
613
|
+
.first<TokenStorageRow>();
|
|
614
|
+
return row ? tokenFromRow(row, this.options.subjectColumns) : null;
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
private subjectSelectSql(): string {
|
|
618
|
+
return this.options.subjectColumns.map((column) => `${quoteIdentifier(column.column)} AS ${quoteIdentifier(column.field)},`).join("\n ");
|
|
619
|
+
}
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
export async function issueMcpOAuthTokenSet(
|
|
623
|
+
repository: McpOAuthRepository,
|
|
624
|
+
input: {
|
|
625
|
+
clientId: string;
|
|
626
|
+
subject: Record<string, string>;
|
|
627
|
+
scopes: string[];
|
|
628
|
+
resource?: URL | null;
|
|
629
|
+
},
|
|
630
|
+
options: McpOAuthTokenOptions = {},
|
|
631
|
+
): Promise<McpIssuedTokenSet> {
|
|
632
|
+
const accessToken = `${createToken("mcpat")}.${crypto.randomUUID().replace(/-/g, "")}`;
|
|
633
|
+
const refreshToken = `${createToken("mcprt")}.${crypto.randomUUID().replace(/-/g, "")}`;
|
|
634
|
+
const issuedAt = options.now?.() ?? nowEpochSeconds();
|
|
635
|
+
const accessExpiresAt = issuedAt + (options.accessTokenTtlSeconds ?? DEFAULT_ACCESS_TOKEN_TTL_SECONDS);
|
|
636
|
+
const refreshExpiresAt = issuedAt + (options.refreshTokenTtlSeconds ?? DEFAULT_REFRESH_TOKEN_TTL_SECONDS);
|
|
637
|
+
const tokenId = await repository.createTokenSet({
|
|
638
|
+
clientId: input.clientId,
|
|
639
|
+
subject: input.subject,
|
|
640
|
+
accessToken,
|
|
641
|
+
refreshToken,
|
|
642
|
+
scopes: input.scopes,
|
|
643
|
+
resource: input.resource?.href ?? null,
|
|
644
|
+
accessExpiresAt,
|
|
645
|
+
refreshExpiresAt,
|
|
646
|
+
});
|
|
647
|
+
return {
|
|
648
|
+
tokenId,
|
|
649
|
+
accessExpiresAt,
|
|
650
|
+
refreshExpiresAt,
|
|
651
|
+
tokens: {
|
|
652
|
+
access_token: accessToken,
|
|
653
|
+
token_type: "Bearer",
|
|
654
|
+
expires_in: options.accessTokenTtlSeconds ?? DEFAULT_ACCESS_TOKEN_TTL_SECONDS,
|
|
655
|
+
refresh_token: refreshToken,
|
|
656
|
+
scope: input.scopes.join(" "),
|
|
657
|
+
},
|
|
658
|
+
};
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
export function parseOAuthScope(scope: string | null | undefined): string[] {
|
|
662
|
+
return scope?.split(/\s+/).filter(Boolean) ?? [];
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
export function parseOAuthJsonArray(value: string | null | undefined): string[] {
|
|
666
|
+
if (!value) return [];
|
|
667
|
+
try {
|
|
668
|
+
const parsed = JSON.parse(value) as unknown;
|
|
669
|
+
return Array.isArray(parsed) ? parsed.filter((item): item is string => typeof item === "string") : [];
|
|
670
|
+
} catch {
|
|
671
|
+
return [];
|
|
672
|
+
}
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
export function createOAuthRedirect(redirectUri: string, params: Record<string, string | undefined>): string {
|
|
676
|
+
const url = new URL(redirectUri);
|
|
677
|
+
for (const [key, value] of Object.entries(params)) {
|
|
678
|
+
if (value !== undefined) url.searchParams.set(key, value);
|
|
679
|
+
}
|
|
680
|
+
return url.toString();
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
export async function encryptOAuthTokenResponse(tokens: OAuthTokens, secret: string): Promise<{ ciphertext: string; nonce: string }> {
|
|
684
|
+
const nonce = crypto.getRandomValues(new Uint8Array(12));
|
|
685
|
+
const key = await tokenResponseEncryptionKey(secret);
|
|
686
|
+
const ciphertext = await crypto.subtle.encrypt({ name: "AES-GCM", iv: nonce }, key, new TextEncoder().encode(JSON.stringify(tokens)));
|
|
687
|
+
return {
|
|
688
|
+
ciphertext: toBase64Url(new Uint8Array(ciphertext)),
|
|
689
|
+
nonce: toBase64Url(nonce),
|
|
690
|
+
};
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
export async function decryptOAuthTokenResponse(ciphertext: string, nonce: string, secret: string): Promise<OAuthTokens | null> {
|
|
694
|
+
try {
|
|
695
|
+
const key = await tokenResponseEncryptionKey(secret);
|
|
696
|
+
const plaintext = await crypto.subtle.decrypt({ name: "AES-GCM", iv: toArrayBuffer(fromBase64Url(nonce)) }, key, toArrayBuffer(fromBase64Url(ciphertext)));
|
|
697
|
+
return JSON.parse(new TextDecoder().decode(plaintext)) as OAuthTokens;
|
|
698
|
+
} catch {
|
|
699
|
+
return null;
|
|
700
|
+
}
|
|
701
|
+
}
|
|
702
|
+
|
|
200
703
|
function setExtraSearchParams(url: URL, params: Record<string, string | undefined> | undefined): void {
|
|
201
704
|
for (const [key, value] of Object.entries(params ?? {})) {
|
|
202
705
|
if (value !== undefined) url.searchParams.set(key, value);
|
|
203
706
|
}
|
|
204
707
|
}
|
|
708
|
+
|
|
709
|
+
type OAuthClientRow = {
|
|
710
|
+
clientId: string;
|
|
711
|
+
clientSecret: string | null;
|
|
712
|
+
clientName: string | null;
|
|
713
|
+
redirectUrisJson: string;
|
|
714
|
+
scope: string | null;
|
|
715
|
+
grantTypesJson: string | null;
|
|
716
|
+
responseTypesJson: string | null;
|
|
717
|
+
tokenEndpointAuthMethod: string | null;
|
|
718
|
+
clientUri: string | null;
|
|
719
|
+
logoUri: string | null;
|
|
720
|
+
contactsJson: string | null;
|
|
721
|
+
tosUri: string | null;
|
|
722
|
+
policyUri: string | null;
|
|
723
|
+
softwareId: string | null;
|
|
724
|
+
softwareVersion: string | null;
|
|
725
|
+
clientIdIssuedAt: number | null;
|
|
726
|
+
clientSecretExpiresAt: number | null;
|
|
727
|
+
};
|
|
728
|
+
|
|
729
|
+
type AuthorizationCodeStorageRow = {
|
|
730
|
+
authorizationCodeId: string;
|
|
731
|
+
clientId: string;
|
|
732
|
+
redirectUri: string;
|
|
733
|
+
scopesJson: string;
|
|
734
|
+
resource: string | null;
|
|
735
|
+
codeChallenge: string;
|
|
736
|
+
expiresAt: number;
|
|
737
|
+
usedAt: string | null;
|
|
738
|
+
} & Record<string, string | number | null>;
|
|
739
|
+
|
|
740
|
+
type TokenStorageRow = {
|
|
741
|
+
tokenId: string;
|
|
742
|
+
clientId: string;
|
|
743
|
+
scopesJson: string;
|
|
744
|
+
resource: string | null;
|
|
745
|
+
accessExpiresAt: number;
|
|
746
|
+
refreshExpiresAt: number | null;
|
|
747
|
+
revokedAt: string | null;
|
|
748
|
+
rotatedToTokenId?: string | null;
|
|
749
|
+
refreshReuseExpiresAt?: number | null;
|
|
750
|
+
rotatedResponseCiphertext?: string | null;
|
|
751
|
+
rotatedResponseNonce?: string | null;
|
|
752
|
+
} & Record<string, string | number | null | undefined>;
|
|
753
|
+
|
|
754
|
+
function clientFromRow(row: OAuthClientRow): OAuthClientInformation {
|
|
755
|
+
return {
|
|
756
|
+
client_id: row.clientId,
|
|
757
|
+
client_secret: row.clientSecret ?? undefined,
|
|
758
|
+
client_name: row.clientName ?? undefined,
|
|
759
|
+
redirect_uris: parseOAuthJsonArray(row.redirectUrisJson),
|
|
760
|
+
scope: row.scope ?? undefined,
|
|
761
|
+
grant_types: parseOAuthJsonArray(row.grantTypesJson),
|
|
762
|
+
response_types: parseOAuthJsonArray(row.responseTypesJson),
|
|
763
|
+
token_endpoint_auth_method: row.tokenEndpointAuthMethod ?? undefined,
|
|
764
|
+
client_uri: row.clientUri ?? undefined,
|
|
765
|
+
logo_uri: row.logoUri ?? undefined,
|
|
766
|
+
contacts: parseOAuthJsonArray(row.contactsJson),
|
|
767
|
+
tos_uri: row.tosUri ?? undefined,
|
|
768
|
+
policy_uri: row.policyUri ?? undefined,
|
|
769
|
+
software_id: row.softwareId ?? undefined,
|
|
770
|
+
software_version: row.softwareVersion ?? undefined,
|
|
771
|
+
client_id_issued_at: row.clientIdIssuedAt ?? undefined,
|
|
772
|
+
client_secret_expires_at: row.clientSecretExpiresAt ?? undefined,
|
|
773
|
+
};
|
|
774
|
+
}
|
|
775
|
+
|
|
776
|
+
function authorizationCodeFromRow(row: AuthorizationCodeStorageRow, subjectColumns: McpOAuthSubjectColumn[]): McpAuthorizationCodeRow {
|
|
777
|
+
return {
|
|
778
|
+
authorizationCodeId: row.authorizationCodeId,
|
|
779
|
+
clientId: row.clientId,
|
|
780
|
+
redirectUri: row.redirectUri,
|
|
781
|
+
scopesJson: row.scopesJson,
|
|
782
|
+
resource: row.resource,
|
|
783
|
+
codeChallenge: row.codeChallenge,
|
|
784
|
+
expiresAt: row.expiresAt,
|
|
785
|
+
usedAt: row.usedAt,
|
|
786
|
+
subject: subjectFromRow(row, subjectColumns),
|
|
787
|
+
};
|
|
788
|
+
}
|
|
789
|
+
|
|
790
|
+
function tokenFromRow(row: TokenStorageRow, subjectColumns: McpOAuthSubjectColumn[]): McpTokenRow {
|
|
791
|
+
return {
|
|
792
|
+
tokenId: row.tokenId,
|
|
793
|
+
clientId: row.clientId,
|
|
794
|
+
scopesJson: row.scopesJson,
|
|
795
|
+
resource: row.resource,
|
|
796
|
+
accessExpiresAt: row.accessExpiresAt,
|
|
797
|
+
refreshExpiresAt: row.refreshExpiresAt,
|
|
798
|
+
revokedAt: row.revokedAt,
|
|
799
|
+
subject: subjectFromRow(row, subjectColumns),
|
|
800
|
+
rotatedToTokenId: row.rotatedToTokenId,
|
|
801
|
+
refreshReuseExpiresAt: row.refreshReuseExpiresAt,
|
|
802
|
+
rotatedResponseCiphertext: row.rotatedResponseCiphertext,
|
|
803
|
+
rotatedResponseNonce: row.rotatedResponseNonce,
|
|
804
|
+
};
|
|
805
|
+
}
|
|
806
|
+
|
|
807
|
+
function subjectFromRow(row: Record<string, string | number | null | undefined>, subjectColumns: McpOAuthSubjectColumn[]): Record<string, string> {
|
|
808
|
+
const subject: Record<string, string> = {};
|
|
809
|
+
for (const column of subjectColumns) {
|
|
810
|
+
const value = row[column.field];
|
|
811
|
+
if (typeof value === "string") subject[column.field] = value;
|
|
812
|
+
}
|
|
813
|
+
return subject;
|
|
814
|
+
}
|
|
815
|
+
|
|
816
|
+
function requiredSubject(subject: Record<string, string>, field: string): string {
|
|
817
|
+
const value = subject[field];
|
|
818
|
+
if (!value) throw new Error(`Missing OAuth subject field: ${field}`);
|
|
819
|
+
return value;
|
|
820
|
+
}
|
|
821
|
+
|
|
822
|
+
function jsonArray(value: string[] | null | undefined): string {
|
|
823
|
+
return JSON.stringify(value ?? []);
|
|
824
|
+
}
|
|
825
|
+
|
|
826
|
+
function nowEpochSeconds(): number {
|
|
827
|
+
return Math.floor(Date.now() / 1000);
|
|
828
|
+
}
|
|
829
|
+
|
|
830
|
+
function quoteIdentifier(value: string): string {
|
|
831
|
+
if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(value)) {
|
|
832
|
+
throw new Error(`Unsafe SQL identifier: ${value}`);
|
|
833
|
+
}
|
|
834
|
+
return `"${value}"`;
|
|
835
|
+
}
|
|
836
|
+
|
|
837
|
+
async function tokenResponseEncryptionKey(secret: string): Promise<CryptoKey> {
|
|
838
|
+
const digest = await crypto.subtle.digest("SHA-256", new TextEncoder().encode(secret));
|
|
839
|
+
return crypto.subtle.importKey("raw", digest, { name: "AES-GCM" }, false, ["encrypt", "decrypt"]);
|
|
840
|
+
}
|
|
841
|
+
|
|
842
|
+
function toArrayBuffer(bytes: Uint8Array): ArrayBuffer {
|
|
843
|
+
return bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength) as ArrayBuffer;
|
|
844
|
+
}
|