@forestadmin/workflow-executor 1.30.2 → 1.31.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/http/mcp-oauth-credentials.d.ts +2 -1
- package/dist/http/mcp-oauth-credentials.js +17 -5
- package/dist/oauth/token-service.d.ts +1 -0
- package/dist/oauth/token-service.js +33 -17
- package/dist/ports/mcp-oauth-credentials-store.d.ts +2 -1
- package/dist/stores/database-mcp-oauth-credentials-store.js +110 -60
- package/package.json +1 -1
|
@@ -3,7 +3,8 @@ import type { McpOAuthCredentialInput } from '../ports/mcp-oauth-credentials-sto
|
|
|
3
3
|
import { z } from 'zod';
|
|
4
4
|
export declare const depositCredentialsBodySchema: z.ZodObject<{
|
|
5
5
|
mcpServerId: z.ZodString;
|
|
6
|
-
refreshToken: z.ZodString
|
|
6
|
+
refreshToken: z.ZodOptional<z.ZodString>;
|
|
7
|
+
accessToken: z.ZodOptional<z.ZodString>;
|
|
7
8
|
clientId: z.ZodOptional<z.ZodString>;
|
|
8
9
|
clientSecret: z.ZodOptional<z.ZodString>;
|
|
9
10
|
clientSecretExpiresAt: z.ZodOptional<z.ZodString>;
|
|
@@ -13,7 +13,8 @@ const token_endpoint_url_1 = __importDefault(require("../oauth/token-endpoint-ur
|
|
|
13
13
|
exports.depositCredentialsBodySchema = zod_1.z
|
|
14
14
|
.object({
|
|
15
15
|
mcpServerId: zod_1.z.string().min(1).max(255),
|
|
16
|
-
refreshToken: zod_1.z.string().min(1),
|
|
16
|
+
refreshToken: zod_1.z.string().min(1).optional(),
|
|
17
|
+
accessToken: zod_1.z.string().min(1).optional(),
|
|
17
18
|
clientId: zod_1.z.string().min(1).max(255).optional(),
|
|
18
19
|
clientSecret: zod_1.z.string().min(1).optional(),
|
|
19
20
|
clientSecretExpiresAt: zod_1.z
|
|
@@ -46,6 +47,15 @@ exports.depositCredentialsBodySchema = zod_1.z
|
|
|
46
47
|
})
|
|
47
48
|
.strict()
|
|
48
49
|
.superRefine((body, ctx) => {
|
|
50
|
+
// A refresh token is optional in OAuth 2.0 (RFC 6749 §5.1): a provider that issues none leaves
|
|
51
|
+
// only its access token to store.
|
|
52
|
+
if (!body.refreshToken === !body.accessToken) {
|
|
53
|
+
ctx.addIssue({
|
|
54
|
+
code: zod_1.z.ZodIssueCode.custom,
|
|
55
|
+
path: ['refreshToken'],
|
|
56
|
+
message: 'exactly one of refreshToken or accessToken is required',
|
|
57
|
+
});
|
|
58
|
+
}
|
|
49
59
|
// RFC 6749 requires client_id alongside client_secret; a secret with no id can never
|
|
50
60
|
// authenticate, so reject it here instead of persisting an unusable credential.
|
|
51
61
|
if (body.clientSecret && !body.clientId) {
|
|
@@ -56,16 +66,18 @@ exports.depositCredentialsBodySchema = zod_1.z
|
|
|
56
66
|
});
|
|
57
67
|
}
|
|
58
68
|
});
|
|
59
|
-
// Translates a validated deposit body into the at-rest record: encrypts the
|
|
69
|
+
// Translates a validated deposit body into the at-rest record: encrypts the deposited token (and
|
|
60
70
|
// client secret when present) and maps optional fields to their nullable columns. encrypt() throws
|
|
61
71
|
// ExecutorEncryptionKeyMissingError when the key is unset; the caller maps that to a 503.
|
|
62
72
|
function buildMcpOAuthCredentialInput({ body, userId, encryption, }) {
|
|
63
|
-
const refreshToken = encryption.encrypt(body.refreshToken);
|
|
73
|
+
const refreshToken = body.refreshToken ? encryption.encrypt(body.refreshToken) : null;
|
|
74
|
+
const accessToken = body.accessToken ? encryption.encrypt(body.accessToken) : null;
|
|
64
75
|
const clientSecret = body.clientSecret ? encryption.encrypt(body.clientSecret) : null;
|
|
65
76
|
return {
|
|
66
77
|
userId,
|
|
67
78
|
mcpServerId: body.mcpServerId,
|
|
68
|
-
refreshTokenEnc: refreshToken
|
|
79
|
+
refreshTokenEnc: refreshToken?.ciphertext ?? null,
|
|
80
|
+
accessTokenEnc: accessToken?.ciphertext ?? null,
|
|
69
81
|
clientId: body.clientId ?? null,
|
|
70
82
|
clientSecretEnc: clientSecret?.ciphertext ?? null,
|
|
71
83
|
clientSecretExpiresAt: body.clientSecretExpiresAt ? new Date(body.clientSecretExpiresAt) : null,
|
|
@@ -74,4 +86,4 @@ function buildMcpOAuthCredentialInput({ body, userId, encryption, }) {
|
|
|
74
86
|
scopes: body.scopes ?? null,
|
|
75
87
|
};
|
|
76
88
|
}
|
|
77
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
89
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibWNwLW9hdXRoLWNyZWRlbnRpYWxzLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL2h0dHAvbWNwLW9hdXRoLWNyZWRlbnRpYWxzLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7OztBQXdFQSxvRUF5QkM7QUE5RkQsNkJBQXdCO0FBRXhCLHFGQUFrRTtBQUVsRSxvR0FBb0c7QUFDcEcsa0dBQWtHO0FBQ2xHLG9DQUFvQztBQUN2QixRQUFBLDRCQUE0QixHQUFHLE9BQUM7S0FDMUMsTUFBTSxDQUFDO0lBQ04sV0FBVyxFQUFFLE9BQUMsQ0FBQyxNQUFNLEVBQUUsQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDLENBQUMsR0FBRyxDQUFDLEdBQUcsQ0FBQztJQUN2QyxZQUFZLEVBQUUsT0FBQyxDQUFDLE1BQU0sRUFBRSxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUMsQ0FBQyxRQUFRLEVBQUU7SUFDMUMsV0FBVyxFQUFFLE9BQUMsQ0FBQyxNQUFNLEVBQUUsQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDLENBQUMsUUFBUSxFQUFFO0lBQ3pDLFFBQVEsRUFBRSxPQUFDLENBQUMsTUFBTSxFQUFFLENBQUMsR0FBRyxDQUFDLENBQUMsQ0FBQyxDQUFDLEdBQUcsQ0FBQyxHQUFHLENBQUMsQ0FBQyxRQUFRLEVBQUU7SUFDL0MsWUFBWSxFQUFFLE9BQUMsQ0FBQyxNQUFNLEVBQUUsQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDLENBQUMsUUFBUSxFQUFFO0lBQzFDLHFCQUFxQixFQUFFLE9BQUM7U0FDckIsTUFBTSxFQUFFO1NBQ1IsTUFBTSxDQUFDLEtBQUssQ0FBQyxFQUFFLENBQUMsQ0FBQyxNQUFNLENBQUMsS0FBSyxDQUFDLElBQUksQ0FBQyxLQUFLLENBQUMsS0FBSyxDQUFDLENBQUMsRUFBRSxFQUFFLE9BQU8sRUFBRSwwQkFBMEIsRUFBRSxDQUFDO1NBQzFGLFFBQVEsRUFBRTtJQUNiLGFBQWEsRUFBRSxPQUFDO1NBQ2IsTUFBTSxF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
|
|
@@ -41,7 +41,7 @@ class OAuthTokenService {
|
|
|
41
41
|
if (cached)
|
|
42
42
|
return cached;
|
|
43
43
|
}
|
|
44
|
-
return this.refreshAndCache(userId, mcpServerId,
|
|
44
|
+
return this.refreshAndCache(userId, mcpServerId, forceRefresh);
|
|
45
45
|
});
|
|
46
46
|
}
|
|
47
47
|
// Drop the cached access token for a (user, server). Called on credential delete so a disconnect
|
|
@@ -62,11 +62,20 @@ class OAuthTokenService {
|
|
|
62
62
|
}
|
|
63
63
|
return entry.accessToken;
|
|
64
64
|
}
|
|
65
|
-
async refreshAndCache(userId, mcpServerId,
|
|
65
|
+
async refreshAndCache(userId, mcpServerId, forceRefresh) {
|
|
66
|
+
const key = `${userId}:${mcpServerId}`;
|
|
66
67
|
const epochAtStart = this.evictionEpoch.get(key) ?? 0;
|
|
67
68
|
const credential = await this.store.get(userId, mcpServerId);
|
|
68
69
|
if (!credential)
|
|
69
70
|
throw new errors_1.OAuthReauthRequiredError(mcpServerId);
|
|
71
|
+
// No refresh token means nothing to renew with: the stored access token is used as is, and a
|
|
72
|
+
// forced refresh (the provider rejected it) can only be answered by re-authenticating. It has no
|
|
73
|
+
// known expiry, so it is never cached.
|
|
74
|
+
if (credential.accessTokenEnc) {
|
|
75
|
+
if (forceRefresh)
|
|
76
|
+
throw new errors_1.OAuthReauthRequiredError(mcpServerId);
|
|
77
|
+
return this.decryptOrReauth(credential, credential.accessTokenEnc);
|
|
78
|
+
}
|
|
70
79
|
const { result, credential: grantedCredential } = await this.runGrantWithRotationRetry(credential, userId, mcpServerId);
|
|
71
80
|
const evictedDuringRefresh = (this.evictionEpoch.get(key) ?? 0) !== epochAtStart;
|
|
72
81
|
// Skip caching if the credential was disconnected mid-refresh (would serve a token for a deleted
|
|
@@ -99,8 +108,9 @@ class OAuthTokenService {
|
|
|
99
108
|
throw error;
|
|
100
109
|
// A peer rotated the refresh token iff the stored value changed since we read it.
|
|
101
110
|
const latest = await this.store.get(userId, mcpServerId);
|
|
102
|
-
const wasRotated = latest
|
|
103
|
-
latest.refreshTokenEnc.toString('base64') !==
|
|
111
|
+
const wasRotated = latest?.refreshTokenEnc != null &&
|
|
112
|
+
latest.refreshTokenEnc.toString('base64') !==
|
|
113
|
+
credential.refreshTokenEnc?.toString('base64');
|
|
104
114
|
if (!latest || !wasRotated) {
|
|
105
115
|
throw new errors_1.OAuthReauthRequiredError(mcpServerId);
|
|
106
116
|
}
|
|
@@ -116,23 +126,28 @@ class OAuthTokenService {
|
|
|
116
126
|
}
|
|
117
127
|
}
|
|
118
128
|
}
|
|
119
|
-
|
|
129
|
+
toGrantParams(credential) {
|
|
130
|
+
if (!credential.refreshTokenEnc)
|
|
131
|
+
throw new errors_1.OAuthReauthRequiredError(credential.mcpServerId);
|
|
132
|
+
return {
|
|
133
|
+
tokenEndpoint: credential.tokenEndpoint,
|
|
134
|
+
refreshToken: this.decryptOrReauth(credential, credential.refreshTokenEnc),
|
|
135
|
+
clientId: credential.clientId,
|
|
136
|
+
clientSecret: credential.clientSecretEnc
|
|
137
|
+
? this.decryptOrReauth(credential, credential.clientSecretEnc)
|
|
138
|
+
: null,
|
|
139
|
+
tokenEndpointAuthMethod: credential.tokenEndpointAuthMethod,
|
|
140
|
+
scopes: credential.scopes,
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
// A decrypt failure with the key PRESENT (auth-tag mismatch — the row was
|
|
120
144
|
// encrypted under a since-rotated/hard-swapped key, or is corrupt) is recoverable: re-consent
|
|
121
145
|
// re-deposits under the current key, so surface it as needs-oauth-reauth. A missing key
|
|
122
146
|
// (ExecutorEncryptionKeyMissingError) is an operator misconfig, not re-consent-resolvable, so it
|
|
123
147
|
// propagates as a terminal error (a re-deposit would just 503 at the deposit endpoint).
|
|
124
|
-
|
|
148
|
+
decryptOrReauth(credential, ciphertext) {
|
|
125
149
|
try {
|
|
126
|
-
return
|
|
127
|
-
tokenEndpoint: credential.tokenEndpoint,
|
|
128
|
-
refreshToken: this.encryption.decrypt(credential.refreshTokenEnc),
|
|
129
|
-
clientId: credential.clientId,
|
|
130
|
-
clientSecret: credential.clientSecretEnc
|
|
131
|
-
? this.encryption.decrypt(credential.clientSecretEnc)
|
|
132
|
-
: null,
|
|
133
|
-
tokenEndpointAuthMethod: credential.tokenEndpointAuthMethod,
|
|
134
|
-
scopes: credential.scopes,
|
|
135
|
-
};
|
|
150
|
+
return this.encryption.decrypt(ciphertext);
|
|
136
151
|
}
|
|
137
152
|
catch (error) {
|
|
138
153
|
if (error instanceof errors_1.ExecutorEncryptionKeyMissingError)
|
|
@@ -149,6 +164,7 @@ class OAuthTokenService {
|
|
|
149
164
|
userId: credential.userId,
|
|
150
165
|
mcpServerId: credential.mcpServerId,
|
|
151
166
|
refreshTokenEnc: encrypted.ciphertext,
|
|
167
|
+
accessTokenEnc: null,
|
|
152
168
|
clientId: credential.clientId,
|
|
153
169
|
clientSecretEnc: credential.clientSecretEnc,
|
|
154
170
|
clientSecretExpiresAt: credential.clientSecretExpiresAt,
|
|
@@ -169,4 +185,4 @@ class OAuthTokenService {
|
|
|
169
185
|
}
|
|
170
186
|
}
|
|
171
187
|
exports.default = OAuthTokenService;
|
|
172
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
188
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidG9rZW4tc2VydmljZS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9vYXV0aC90b2tlbi1zZXJ2aWNlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7O0FBUUEsMENBQTBEO0FBQzFELHNDQUltQjtBQUNuQixnRUFBdUM7QUFDdkMsb0VBQXdEO0FBZ0J4RCxnR0FBZ0c7QUFDaEcsa0dBQWtHO0FBQ2xHLGtHQUFrRztBQUNsRywrRUFBK0U7QUFDL0UscUVBQXFFO0FBQ3JFLE1BQXFCLGlCQUFpQjtJQWFwQyxZQUFZLE9BQWlDO1FBTjVCLFVBQUssR0FBRyxJQUFJLEdBQUcsRUFBdUIsQ0FBQztRQUN2QyxVQUFLLEdBQUcsSUFBSSxxQkFBVSxFQUFFLENBQUM7UUFDMUMsaUdBQWlHO1FBQ2pHLDhFQUE4RTtRQUM3RCxrQkFBYSxHQUFHLElBQUksR0FBRyxFQUFrQixDQUFDO1FBR3pELElBQUksQ0FBQyxLQUFLLEdBQUcsT0FBTyxDQUFDLEtBQUssQ0FBQztRQUMzQixJQUFJLENBQUMsVUFBVSxHQUFHLE9BQU8sQ0FBQyxVQUFVLENBQUM7UUFDckMsSUFBSSxDQUFDLE1BQU0sR0FBRyxPQUFPLENBQUMsTUFBTSxDQUFDO1FBQzdCLElBQUksQ0FBQyxZQUFZLEdBQUcsQ0FBQyxPQUFPLENBQUMsV0FBVyxJQUFJLHNDQUEyQixDQUFDLEdBQUcsSUFBSSxDQUFDO1FBQ2hGLElBQUksQ0FBQyxrQkFBa0IsR0FBRyxPQUFPLENBQUMsa0JBQWtCLElBQUksdUJBQXlCLENBQUM7UUFDbEYsSUFBSSxDQUFDLEdBQUcsR0FBRyxPQUFPLENBQUMsR0FBRyxJQUFJLElBQUksQ0FBQyxHQUFHLENBQUM7SUFDckMsQ0FBQztJQUVELEtBQUssQ0FBQyxjQUFjLENBQ2xCLE1BQWMsRUFDZCxXQUFtQixFQUNuQixPQUFvQztRQUVwQyxNQUFNLEdBQUcsR0FBRyxHQUFHLE1BQU0sSUFBSSxXQUFXLEVBQUUsQ0FBQztRQUN2QyxNQUFNLFlBQVksR0FBRyxPQUFPLEVBQUUsWUFBWSxJQUFJLEtBQUssQ0FBQztRQUVwRCxJQUFJLENBQUMsWUFBWSxFQUFFLENBQUM7WUFDbEIsTUFBTSxNQUFNLEdBQUcsSUFBSSxDQUFDLFNBQVMsQ0FBQyxHQUFHLENBQUMsQ0FBQztZQUNuQyxJQUFJLE1BQU07Z0JBQUUsT0FBTyxNQUFNLENBQUM7UUFDNUIsQ0FBQztRQUVELE9BQU8sSUFBSSxDQUFDLEtBQUssQ0FBQyxZQUFZLENBQUMsR0FBRyxFQUFFLEtBQUssSUFBSSxFQUFFO1lBQzdDLHNGQUFzRjtZQUN0RixJQUFJLENBQUMsWUFBWSxFQUFFLENBQUM7Z0JBQ2xCLE1BQU0sTUFBTSxHQUFHLElBQUksQ0FBQyxTQUFTLENBQUMsR0FBRyxDQUFDLENBQUM7Z0JBQ25DLElBQUksTUFBTTtvQkFBRSxPQUFPLE1BQU0sQ0FBQztZQUM1QixDQUFDO1lBRUQsT0FBTyxJQUFJLENBQUMsZUFBZSxDQUFDLE1BQU0sRUFBRSxXQUFXLEVBQUUsWUFBWSxDQUFDLENBQUM7UUFDakUsQ0FBQyxDQUFDLENBQUM7SUFDTCxDQUFDO0lBRUQsaUdBQWlHO0lBQ2pHLCtGQUErRjtJQUMvRixLQUFLLENBQUMsTUFBYyxFQUFFLFdBQW1CO1FBQ3ZDLE1BQU0sR0FBRyxHQUFHLEdBQUcsTUFBTSxJQUFJLFdBQVcsRUFBRSxDQUFDO1FBQ3ZDLElBQUksQ0FBQyxLQUFLLENBQUMsTUFBTSxDQUFDLEdBQUcsQ0FBQyxDQUFDO1FBQ3ZCLElBQUksQ0FBQyxhQUFhLENBQUMsR0FBRyxDQUFDLEdBQUcsRUFBRSxDQUFDLElBQUksQ0FBQyxhQUFhLENBQUMsR0FBRyxDQUFDLEdBQUcsQ0FBQyxJQUFJLENBQUMsQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDO0lBQ3RFLENBQUM7SUFFTyxTQUFTLENBQUMsR0FBVztRQUMzQixNQUFNLEtBQUssR0FBRyxJQUFJLENBQUMsS0FBSyxDQUFDLEdBQUcsQ0FBQyxHQUFHLENBQUMsQ0FBQztRQUNsQyxJQUFJLENBQUMsS0FBSztZQUFFLE9BQU8sU0FBUyxDQUFDO1FBRTdCLElBQUksSUFBSSxDQUFDLEdBQUcsRUFBRSxJQUFJLEtBQUssQ0FBQyxXQUFXLEdBQUcsSUFBSSxDQUFDLFlBQVksRUFBRSxDQUFDO1lBQ3hELHVGQUF1RjtZQUN2RixJQUFJLENBQUMsS0FBSyxDQUFDLE1BQU0sQ0FBQyxHQUFHLENBQUMsQ0FBQztZQUV2QixPQUFPLFNBQVMsQ0FBQztRQUNuQixDQUFDO1FBRUQsT0FBTyxLQUFLLENBQUMsV0FBVyxDQUFDO0lBQzNCLENBQUM7SUFFTyxLQUFLLENBQUMsZUFBZSxDQUMzQixNQUFjLEVBQ2QsV0FBbUIsRUFDbkIsWUFBcUI7UUFFckIsTUFBTSxHQUFHLEdBQUcsR0FBRyxNQUFNLElBQUksV0FBVyxFQUFFLENBQUM7UUFDdkMsTUFBTSxZQUFZLEdBQUcsSUFBSSxDQUFDLGFBQWEsQ0FBQyxHQUFHLENBQUMsR0FBRyxDQUFDLElBQUksQ0FBQyxDQUFDO1FBQ3RELE1BQU0sVUFBVSxHQUFHLE1BQU0sSUFBSSxDQUFDLEtBQUssQ0FBQyxHQUFHLENBQUMsTUFBTSxFQUFFLFdBQVcsQ0FBQyxDQUFDO1FBQzdELElBQUksQ0FBQyxVQUFVO1lBQUUsTUFBTSxJQUFJLGlDQUF3QixDQUFDLFdBQVcsQ0FBQyxDQUFDO1FBRWpFLDZGQUE2RjtRQUM3RixpR0FBaUc7UUFDakcsdUNBQXVDO1FBQ3ZDLElBQUksVUFBVSxDQUFDLGNBQWMsRUFBRSxDQUFDO1lBQzlCLElBQUksWUFBWTtnQkFBRSxNQUFNLElBQUksaUNBQXdCLENBQUMsV0FBVyxDQUFDLENBQUM7WUFFbEUsT0FBTyxJQUFJLENBQUMsZUFBZSxDQUFDLFVBQVUsRUFBRSxVQUFVLENBQUMsY0FBYyxDQUFDLENBQUM7UUFDckUsQ0FBQztRQUVELE1BQU0sRUFBRSxNQUFNLEVBQUUsVUFBVSxFQUFFLGlCQUFpQixFQUFFLEdBQUcsTUFBTSxJQUFJLENBQUMseUJBQXlCLENBQ3BGLFVBQVUsRUFDVixNQUFNLEVBQ04sV0FBVyxDQUNaLENBQUM7UUFFRixNQUFNLG9CQUFvQixHQUFHLENBQUMsSUFBSSxDQUFDLGFBQWEsQ0FBQyxHQUFHLENBQUMsR0FBRyxDQUFDLElBQUksQ0FBQyxDQUFDLEtBQUssWUFBWSxDQUFDO1FBRWpGLGlHQUFpRztRQUNqRyxtR0FBbUc7UUFDbkcsSUFBSSxDQUFDLG9CQUFvQixJQUFJLE1BQU0sQ0FBQyxVQUFVLEtBQUssU0FBUyxFQUFFLENBQUM7WUFDN0QsSUFBSSxDQUFDLEtBQUssQ0FBQyxHQUFHLENBQUMsR0FBRyxFQUFFO2dCQUNsQixXQUFXLEVBQUUsTUFBTSxDQUFDLFdBQVc7Z0JBQy9CLFdBQVcsRUFBRSxJQUFJLENBQUMsR0FBRyxFQUFFLEdBQUcsTUFBTSxDQUFDLFVBQVUsR0FBRyxJQUFJO2FBQ25ELENBQUMsQ0FBQztRQUNMLENBQUM7YUFBTSxDQUFDO1lBQ04sSUFBSSxDQUFDLEtBQUssQ0FBQyxNQUFNLENBQUMsR0FBRyxDQUFDLENBQUM7UUFDekIsQ0FBQztRQUVELElBQUksTUFBTSxDQUFDLFlBQVksRUFBRSxDQUFDO1lBQ3hCLE1BQU0sSUFBSSxDQUFDLDBCQUEwQixDQUFDLGlCQUFpQixFQUFFLE1BQU0sQ0FBQyxZQUFZLENBQUMsQ0FBQztRQUNoRixDQUFDO1FBRUQsT0FBTyxNQUFNLENBQUMsV0FBVyxDQUFDO0lBQzVCLENBQUM7SUFFRCxtR0FBbUc7SUFDbkcsaUdBQWlHO0lBQ2pHLGtHQUFrRztJQUNsRyx1RkFBdUY7SUFDL0UsS0FBSyxDQUFDLHlCQUF5QixDQUNyQyxVQUFvQyxFQUNwQyxNQUFjLEVBQ2QsV0FBbUI7UUFFbkIsSUFBSSxDQUFDO1lBQ0gsTUFBTSxNQUFNLEdBQUcsTUFBTSxJQUFJLENBQUMsa0JBQWtCLENBQUMsSUFBSSxDQUFDLGFBQWEsQ0FBQyxVQUFVLENBQUMsQ0FBQyxDQUFDO1lBRTdFLE9BQU8sRUFBRSxNQUFNLEVBQUUsVUFBVSxFQUFFLENBQUM7UUFDaEMsQ0FBQztRQUFDLE9BQU8sS0FBSyxFQUFFLENBQUM7WUFDZixJQUFJLENBQUMsQ0FBQyxLQUFLLFlBQVksK0JBQXNCLENBQUM7Z0JBQUUsTUFBTSxLQUFLLENBQUM7WUFFNUQsa0ZBQWtGO1lBQ2xGLE1BQU0sTUFBTSxHQUFHLE1BQU0sSUFBSSxDQUFDLEtBQUssQ0FBQyxHQUFHLENBQUMsTUFBTSxFQUFFLFdBQVcsQ0FBQyxDQUFDO1lBQ3pELE1BQU0sVUFBVSxHQUNkLE1BQU0sRUFBRSxlQUFlLElBQUksSUFBSTtnQkFDL0IsTUFBTSxDQUFDLGVBQWUsQ0FBQyxRQUFRLENBQUMsUUFBUSxDQUFDO29CQUN2QyxVQUFVLENBQUMsZUFBZSxFQUFFLFFBQVEsQ0FBQyxRQUFRLENBQUMsQ0FBQztZQUVuRCxJQUFJLENBQUMsTUFBTSxJQUFJLENBQUMsVUFBVSxFQUFFLENBQUM7Z0JBQzNCLE1BQU0sSUFBSSxpQ0FBd0IsQ0FBQyxXQUFXLENBQUMsQ0FBQztZQUNsRCxDQUFDO1lBRUQsSUFBSSxDQUFDO2dCQUNILE1BQU0sTUFBTSxHQUFHLE1BQU0sSUFBSSxDQUFDLGtCQUFrQixDQUFDLElBQUksQ0FBQyxhQUFhLENBQUMsTUFBTSxDQUFDLENBQUMsQ0FBQztnQkFFekUsT0FBTyxFQUFFLE1BQU0sRUFBRSxVQUFVLEVBQUUsTUFBTSxFQUFFLENBQUM7WUFDeEMsQ0FBQztZQUFDLE9BQU8sVUFBVSxFQUFFLENBQUM7Z0JBQ3BCLElBQUksVUFBVSxZQUFZLCtCQUFzQixFQUFFLENBQUM7b0JBQ2pELE1BQU0sSUFBSSxpQ0FBd0IsQ0FBQyxXQUFXLENBQUMsQ0FBQztnQkFDbEQsQ0FBQztnQkFFRCxNQUFNLFVBQVUsQ0FBQztZQUNuQixDQUFDO1FBQ0gsQ0FBQztJQUNILENBQUM7SUFFTyxhQUFhLENBQUMsVUFBb0M7UUFDeEQsSUFBSSxDQUFDLFVBQVUsQ0FBQyxlQUFlO1lBQUUsTUFBTSxJQUFJLGlDQUF3QixDQUFDLFVBQVUsQ0FBQyxXQUFXLENBQUMsQ0FBQztRQUU1RixPQUFPO1lBQ0wsYUFBYSxFQUFFLFVBQVUsQ0FBQyxhQUFhO1lBQ3ZDLFlBQVksRUFBRSxJQUFJLENBQUMsZUFBZSxDQUFDLFVBQVUsRUFBRSxVQUFVLENBQUMsZUFBZSxDQUFDO1lBQzFFLFFBQVEsRUFBRSxVQUFVLENBQUMsUUFBUTtZQUM3QixZQUFZLEVBQUUsVUFBVSxDQUFDLGVBQWU7Z0JBQ3RDLENBQUMsQ0FBQyxJQUFJLENBQUMsZUFBZSxDQUFDLFVBQVUsRUFBRSxVQUFVLENBQUMsZUFBZSxDQUFDO2dCQUM5RCxDQUFDLENBQUMsSUFBSTtZQUNSLHVCQUF1QixFQUFFLFVBQVUsQ0FBQyx1QkFBdUI7WUFDM0QsTUFBTSxFQUFFLFVBQVUsQ0FBQyxNQUFNO1NBQzFCLENBQUM7SUFDSixDQUFDO0lBRUQsMEVBQTBFO0lBQzFFLDhGQUE4RjtJQUM5Rix3RkFBd0Y7SUFDeEYsaUdBQWlHO0lBQ2pHLHdGQUF3RjtJQUNoRixlQUFlLENBQUMsVUFBb0MsRUFBRSxVQUFrQjtRQUM5RSxJQUFJLENBQUM7WUFDSCxPQUFPLElBQUksQ0FBQyxVQUFVLENBQUMsT0FBTyxDQUFDLFVBQVUsQ0FBQyxDQUFDO1FBQzdDLENBQUM7UUFBQyxPQUFPLEtBQUssRUFBRSxDQUFDO1lBQ2YsSUFBSSxLQUFLLFlBQVksMENBQWlDO2dCQUFFLE1BQU0sS0FBSyxDQUFDO1lBRXBFLE1BQU0sSUFBSSxpQ0FBd0IsQ0FBQyxVQUFVLENBQUMsV0FBVyxDQUFDLENBQUM7UUFDN0QsQ0FBQztJQUNILENBQUM7SUFFTyxLQUFLLENBQUMsMEJBQTBCLENBQ3RDLFVBQW9DLEVBQ3BDLFlBQW9CO1FBRXBCLElBQUksQ0FBQztZQUNILE1BQU0sU0FBUyxHQUFHLElBQUksQ0FBQyxVQUFVLENBQUMsT0FBTyxDQUFDLFlBQVksQ0FBQyxDQUFDO1lBRXhELDBGQUEwRjtZQUMxRiw2RkFBNkY7WUFDN0YsTUFBTSxJQUFJLENBQUMsS0FBSyxDQUFDLGVBQWUsQ0FBQyxVQUFVLENBQUMsRUFBRSxFQUFFO2dCQUM5QyxNQUFNLEVBQUUsVUFBVSxDQUFDLE1BQU07Z0JBQ3pCLFdBQVcsRUFBRSxVQUFVLENBQUMsV0FBVztnQkFDbkMsZUFBZSxFQUFFLFNBQVMsQ0FBQyxVQUFVO2dCQUNyQyxjQUFjLEVBQUUsSUFBSTtnQkFDcEIsUUFBUSxFQUFFLFVBQVUsQ0FBQyxRQUFRO2dCQUM3QixlQUFlLEVBQUUsVUFBVSxDQUFDLGVBQWU7Z0JBQzNDLHFCQUFxQixFQUFFLFVBQVUsQ0FBQyxxQkFBcUI7Z0JBQ3ZELGFBQWEsRUFBRSxVQUFVLENBQUMsYUFBYTtnQkFDdkMsdUJBQXVCLEVBQUUsVUFBVSxDQUFDLHVCQUF1QjtnQkFDM0QsTUFBTSxFQUFFLFVBQVUsQ0FBQyxNQUFNO2FBQzFCLENBQUMsQ0FBQztRQUNMLENBQUM7UUFBQyxPQUFPLEtBQUssRUFBRSxDQUFDO1lBQ2YsMkZBQTJGO1lBQzNGLDRGQUE0RjtZQUM1Rix3RkFBd0Y7WUFDeEYsSUFBSSxDQUFDLE1BQU0sRUFBRSxDQUFDLE9BQU8sRUFBRSxtREFBbUQsRUFBRTtnQkFDMUUsV0FBVyxFQUFFLFVBQVUsQ0FBQyxXQUFXO2dCQUNuQyxLQUFLLEVBQUUsS0FBSyxZQUFZLEtBQUssQ0FBQyxDQUFDLENBQUMsS0FBSyxDQUFDLE9BQU8sQ0FBQyxDQUFDLENBQUMsTUFBTSxDQUFDLEtBQUssQ0FBQzthQUM5RCxDQUFDLENBQUM7UUFDTCxDQUFDO0lBQ0gsQ0FBQztDQUNGO0FBdk5ELG9DQXVOQyJ9
|
|
@@ -2,7 +2,8 @@ import type { Logger } from './logger-port';
|
|
|
2
2
|
export interface McpOAuthCredentialInput {
|
|
3
3
|
userId: number;
|
|
4
4
|
mcpServerId: string;
|
|
5
|
-
refreshTokenEnc: Buffer;
|
|
5
|
+
refreshTokenEnc: Buffer | null;
|
|
6
|
+
accessTokenEnc?: Buffer | null;
|
|
6
7
|
clientId?: string | null;
|
|
7
8
|
clientSecretEnc?: Buffer | null;
|
|
8
9
|
clientSecretExpiresAt?: Date | null;
|
|
@@ -5,6 +5,99 @@ const umzug_1 = require("umzug");
|
|
|
5
5
|
const errors_1 = require("../errors");
|
|
6
6
|
const schema_migrations_1 = require("./schema-migrations");
|
|
7
7
|
const TABLE_NAME = 'ai_mcp_oauth_credentials';
|
|
8
|
+
// The table as 002 created it. 003 rebuilds it from this on SQLite, so the two must stay in step.
|
|
9
|
+
// A function, not a constant: DataTypes is read on call, never at import.
|
|
10
|
+
const credentialColumns = () => ({
|
|
11
|
+
id: { type: sequelize_1.DataTypes.INTEGER, primaryKey: true, autoIncrement: true },
|
|
12
|
+
userId: { type: sequelize_1.DataTypes.INTEGER, allowNull: false, field: 'user_id' },
|
|
13
|
+
mcpServerId: {
|
|
14
|
+
type: sequelize_1.DataTypes.STRING(255),
|
|
15
|
+
allowNull: false,
|
|
16
|
+
field: 'mcp_server_id',
|
|
17
|
+
},
|
|
18
|
+
refreshTokenEnc: {
|
|
19
|
+
type: sequelize_1.DataTypes.BLOB,
|
|
20
|
+
allowNull: false,
|
|
21
|
+
field: 'refresh_token_enc',
|
|
22
|
+
},
|
|
23
|
+
clientId: { type: sequelize_1.DataTypes.STRING(255), allowNull: true, field: 'client_id' },
|
|
24
|
+
clientSecretEnc: {
|
|
25
|
+
type: sequelize_1.DataTypes.BLOB,
|
|
26
|
+
allowNull: true,
|
|
27
|
+
field: 'client_secret_enc',
|
|
28
|
+
},
|
|
29
|
+
clientSecretExpiresAt: {
|
|
30
|
+
type: sequelize_1.DataTypes.DATE,
|
|
31
|
+
allowNull: true,
|
|
32
|
+
field: 'client_secret_expires_at',
|
|
33
|
+
},
|
|
34
|
+
tokenEndpoint: {
|
|
35
|
+
type: sequelize_1.DataTypes.STRING(2048),
|
|
36
|
+
allowNull: false,
|
|
37
|
+
field: 'token_endpoint',
|
|
38
|
+
},
|
|
39
|
+
tokenEndpointAuthMethod: {
|
|
40
|
+
type: sequelize_1.DataTypes.STRING(64),
|
|
41
|
+
allowNull: true,
|
|
42
|
+
field: 'token_endpoint_auth_method',
|
|
43
|
+
},
|
|
44
|
+
scopes: { type: sequelize_1.DataTypes.STRING(2048), allowNull: true, field: 'scopes' },
|
|
45
|
+
createdAt: {
|
|
46
|
+
type: sequelize_1.DataTypes.DATE,
|
|
47
|
+
allowNull: false,
|
|
48
|
+
defaultValue: sequelize_1.DataTypes.NOW,
|
|
49
|
+
field: 'created_at',
|
|
50
|
+
},
|
|
51
|
+
updatedAt: {
|
|
52
|
+
type: sequelize_1.DataTypes.DATE,
|
|
53
|
+
allowNull: false,
|
|
54
|
+
defaultValue: sequelize_1.DataTypes.NOW,
|
|
55
|
+
field: 'updated_at',
|
|
56
|
+
},
|
|
57
|
+
});
|
|
58
|
+
const nullableBlob = () => ({ type: sequelize_1.DataTypes.BLOB, allowNull: true });
|
|
59
|
+
const UNIQUE_KEY = ['user_id', 'mcp_server_id'];
|
|
60
|
+
const UNIQUE_INDEX = { unique: true, name: 'idx_user_id_mcp_server_id' };
|
|
61
|
+
// 003: a provider that issues no refresh token leaves an access token to store instead.
|
|
62
|
+
async function addAccessTokenColumn(context, tableId) {
|
|
63
|
+
// Atomic and idempotent, like 002: a half-applied run rolls back, a re-run finds the column.
|
|
64
|
+
const columns = await context.describeTable(tableId);
|
|
65
|
+
if (columns.access_token_enc)
|
|
66
|
+
return;
|
|
67
|
+
await context.sequelize.transaction(async (transaction) => {
|
|
68
|
+
if (context.sequelize.getDialect() !== 'sqlite') {
|
|
69
|
+
await context.changeColumn(tableId, 'refresh_token_enc', nullableBlob(), {
|
|
70
|
+
transaction,
|
|
71
|
+
});
|
|
72
|
+
await context.addColumn(tableId, 'access_token_enc', nullableBlob(), {
|
|
73
|
+
transaction,
|
|
74
|
+
});
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
// SQLite cannot drop NOT NULL in place, and changeColumn's own rebuild loses
|
|
78
|
+
// AUTOINCREMENT and splits the composite unique index, so rebuild by hand.
|
|
79
|
+
const legacyTable = `${TABLE_NAME}_002`;
|
|
80
|
+
await context.renameTable(tableId, legacyTable, { transaction });
|
|
81
|
+
await context.createTable(tableId, {
|
|
82
|
+
...credentialColumns(),
|
|
83
|
+
refreshTokenEnc: { ...nullableBlob(), field: 'refresh_token_enc' },
|
|
84
|
+
accessTokenEnc: { ...nullableBlob(), field: 'access_token_enc' },
|
|
85
|
+
}, { transaction });
|
|
86
|
+
const copied = Object.entries(credentialColumns())
|
|
87
|
+
.map(([name, definition]) => ('field' in definition ? definition.field : name))
|
|
88
|
+
.join(', ');
|
|
89
|
+
await context.sequelize.query(`INSERT INTO "${TABLE_NAME}" (${copied}) SELECT ${copied} FROM "${legacyTable}"`, { transaction });
|
|
90
|
+
// The copy only advances the sequence to the highest surviving id, so carry the old one over:
|
|
91
|
+
// updateIfPresent relies on a deleted row's id never being handed out again.
|
|
92
|
+
await context.sequelize.query(`DELETE FROM sqlite_sequence WHERE name = '${TABLE_NAME}'`, {
|
|
93
|
+
transaction,
|
|
94
|
+
});
|
|
95
|
+
await context.sequelize.query(`INSERT INTO sqlite_sequence (name, seq) ` +
|
|
96
|
+
`SELECT '${TABLE_NAME}', seq FROM sqlite_sequence WHERE name = '${legacyTable}'`, { transaction });
|
|
97
|
+
await context.dropTable(legacyTable, { transaction });
|
|
98
|
+
await context.addIndex(tableId, UNIQUE_KEY, { ...UNIQUE_INDEX, transaction });
|
|
99
|
+
});
|
|
100
|
+
}
|
|
8
101
|
class DatabaseMcpOAuthCredentialsStore {
|
|
9
102
|
constructor(options) {
|
|
10
103
|
this.sequelize = options.sequelize;
|
|
@@ -29,65 +122,18 @@ class DatabaseMcpOAuthCredentialsStore {
|
|
|
29
122
|
await context.sequelize.transaction(async (transaction) => {
|
|
30
123
|
if (await context.tableExists(tableId, { transaction }))
|
|
31
124
|
return;
|
|
32
|
-
await context.createTable(tableId, {
|
|
33
|
-
|
|
34
|
-
userId: { type: sequelize_1.DataTypes.INTEGER, allowNull: false, field: 'user_id' },
|
|
35
|
-
mcpServerId: {
|
|
36
|
-
type: sequelize_1.DataTypes.STRING(255),
|
|
37
|
-
allowNull: false,
|
|
38
|
-
field: 'mcp_server_id',
|
|
39
|
-
},
|
|
40
|
-
refreshTokenEnc: {
|
|
41
|
-
type: sequelize_1.DataTypes.BLOB,
|
|
42
|
-
allowNull: false,
|
|
43
|
-
field: 'refresh_token_enc',
|
|
44
|
-
},
|
|
45
|
-
clientId: { type: sequelize_1.DataTypes.STRING(255), allowNull: true, field: 'client_id' },
|
|
46
|
-
clientSecretEnc: {
|
|
47
|
-
type: sequelize_1.DataTypes.BLOB,
|
|
48
|
-
allowNull: true,
|
|
49
|
-
field: 'client_secret_enc',
|
|
50
|
-
},
|
|
51
|
-
clientSecretExpiresAt: {
|
|
52
|
-
type: sequelize_1.DataTypes.DATE,
|
|
53
|
-
allowNull: true,
|
|
54
|
-
field: 'client_secret_expires_at',
|
|
55
|
-
},
|
|
56
|
-
tokenEndpoint: {
|
|
57
|
-
type: sequelize_1.DataTypes.STRING(2048),
|
|
58
|
-
allowNull: false,
|
|
59
|
-
field: 'token_endpoint',
|
|
60
|
-
},
|
|
61
|
-
tokenEndpointAuthMethod: {
|
|
62
|
-
type: sequelize_1.DataTypes.STRING(64),
|
|
63
|
-
allowNull: true,
|
|
64
|
-
field: 'token_endpoint_auth_method',
|
|
65
|
-
},
|
|
66
|
-
scopes: { type: sequelize_1.DataTypes.STRING(2048), allowNull: true, field: 'scopes' },
|
|
67
|
-
createdAt: {
|
|
68
|
-
type: sequelize_1.DataTypes.DATE,
|
|
69
|
-
allowNull: false,
|
|
70
|
-
defaultValue: sequelize_1.DataTypes.NOW,
|
|
71
|
-
field: 'created_at',
|
|
72
|
-
},
|
|
73
|
-
updatedAt: {
|
|
74
|
-
type: sequelize_1.DataTypes.DATE,
|
|
75
|
-
allowNull: false,
|
|
76
|
-
defaultValue: sequelize_1.DataTypes.NOW,
|
|
77
|
-
field: 'updated_at',
|
|
78
|
-
},
|
|
79
|
-
}, { transaction });
|
|
80
|
-
await context.addIndex(tableId, ['user_id', 'mcp_server_id'], {
|
|
81
|
-
unique: true,
|
|
82
|
-
name: 'idx_user_id_mcp_server_id',
|
|
83
|
-
transaction,
|
|
84
|
-
});
|
|
125
|
+
await context.createTable(tableId, credentialColumns(), { transaction });
|
|
126
|
+
await context.addIndex(tableId, UNIQUE_KEY, { ...UNIQUE_INDEX, transaction });
|
|
85
127
|
});
|
|
86
128
|
},
|
|
87
129
|
down: async ({ context }) => {
|
|
88
130
|
await context.dropTable(tableId);
|
|
89
131
|
},
|
|
90
132
|
},
|
|
133
|
+
{
|
|
134
|
+
name: '003_add_mcp_oauth_access_token',
|
|
135
|
+
up: async ({ context }) => addAccessTokenColumn(context, tableId),
|
|
136
|
+
},
|
|
91
137
|
],
|
|
92
138
|
context: this.sequelize.getQueryInterface(),
|
|
93
139
|
storage: new umzug_1.SequelizeStorage({
|
|
@@ -116,6 +162,7 @@ class DatabaseMcpOAuthCredentialsStore {
|
|
|
116
162
|
userId: credential.userId,
|
|
117
163
|
mcpServerId: credential.mcpServerId,
|
|
118
164
|
refreshTokenEnc: credential.refreshTokenEnc,
|
|
165
|
+
accessTokenEnc: credential.accessTokenEnc ?? null,
|
|
119
166
|
clientId: credential.clientId ?? null,
|
|
120
167
|
clientSecretEnc: credential.clientSecretEnc ?? null,
|
|
121
168
|
clientSecretExpiresAt: credential.clientSecretExpiresAt ?? null,
|
|
@@ -127,10 +174,10 @@ class DatabaseMcpOAuthCredentialsStore {
|
|
|
127
174
|
// Delete + insert in transaction: dialect-agnostic upsert (avoids ON CONFLICT / ON DUPLICATE).
|
|
128
175
|
await this.sequelize.query(`DELETE FROM ${this.tableReference} WHERE user_id = :userId AND mcp_server_id = :mcpServerId`, { replacements, transaction });
|
|
129
176
|
await this.sequelize.query(`INSERT INTO ${this.tableReference} ` +
|
|
130
|
-
'(user_id, mcp_server_id, refresh_token_enc,
|
|
131
|
-
'client_secret_expires_at, token_endpoint,
|
|
132
|
-
'created_at, updated_at) VALUES ' +
|
|
133
|
-
'(:userId, :mcpServerId, :refreshTokenEnc, :clientId, :clientSecretEnc, ' +
|
|
177
|
+
'(user_id, mcp_server_id, refresh_token_enc, access_token_enc, client_id, ' +
|
|
178
|
+
'client_secret_enc, client_secret_expires_at, token_endpoint, ' +
|
|
179
|
+
'token_endpoint_auth_method, scopes, created_at, updated_at) VALUES ' +
|
|
180
|
+
'(:userId, :mcpServerId, :refreshTokenEnc, :accessTokenEnc, :clientId, :clientSecretEnc, ' +
|
|
134
181
|
':clientSecretExpiresAt, :tokenEndpoint, :tokenEndpointAuthMethod, :scopes, ' +
|
|
135
182
|
':now, :now)', { replacements, transaction });
|
|
136
183
|
});
|
|
@@ -138,7 +185,8 @@ class DatabaseMcpOAuthCredentialsStore {
|
|
|
138
185
|
async updateIfPresent(id, credential) {
|
|
139
186
|
// Single atomic UPDATE … WHERE id — affects zero rows if that row was deleted or re-created.
|
|
140
187
|
await this.sequelize.query(`UPDATE ${this.tableReference} SET ` +
|
|
141
|
-
'refresh_token_enc = :refreshTokenEnc,
|
|
188
|
+
'refresh_token_enc = :refreshTokenEnc, access_token_enc = :accessTokenEnc, ' +
|
|
189
|
+
'client_id = :clientId, ' +
|
|
142
190
|
'client_secret_enc = :clientSecretEnc, client_secret_expires_at = :clientSecretExpiresAt, ' +
|
|
143
191
|
'token_endpoint = :tokenEndpoint, token_endpoint_auth_method = :tokenEndpointAuthMethod, ' +
|
|
144
192
|
'scopes = :scopes, updated_at = :now ' +
|
|
@@ -146,6 +194,7 @@ class DatabaseMcpOAuthCredentialsStore {
|
|
|
146
194
|
replacements: {
|
|
147
195
|
id,
|
|
148
196
|
refreshTokenEnc: credential.refreshTokenEnc,
|
|
197
|
+
accessTokenEnc: credential.accessTokenEnc ?? null,
|
|
149
198
|
clientId: credential.clientId ?? null,
|
|
150
199
|
clientSecretEnc: credential.clientSecretEnc ?? null,
|
|
151
200
|
clientSecretExpiresAt: credential.clientSecretExpiresAt ?? null,
|
|
@@ -174,7 +223,8 @@ class DatabaseMcpOAuthCredentialsStore {
|
|
|
174
223
|
id: Number(row.id),
|
|
175
224
|
userId: Number(row.user_id),
|
|
176
225
|
mcpServerId: row.mcp_server_id,
|
|
177
|
-
refreshTokenEnc: row.refresh_token_enc,
|
|
226
|
+
refreshTokenEnc: row.refresh_token_enc ?? null,
|
|
227
|
+
accessTokenEnc: row.access_token_enc ?? null,
|
|
178
228
|
clientId: row.client_id ?? null,
|
|
179
229
|
clientSecretEnc: row.client_secret_enc ?? null,
|
|
180
230
|
clientSecretExpiresAt: row.client_secret_expires_at == null ? null : new Date(row.client_secret_expires_at),
|
|
@@ -185,4 +235,4 @@ class DatabaseMcpOAuthCredentialsStore {
|
|
|
185
235
|
}
|
|
186
236
|
}
|
|
187
237
|
exports.default = DatabaseMcpOAuthCredentialsStore;
|
|
188
|
-
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZGF0YWJhc2UtbWNwLW9hdXRoLWNyZWRlbnRpYWxzLXN0b3JlLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL3N0b3Jlcy9kYXRhYmFzZS1tY3Atb2F1dGgtY3JlZGVudGlhbHMtc3RvcmUudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7QUFRQSx5Q0FBc0M7QUFDdEMsaUNBQWdEO0FBRWhELHNDQUFnRDtBQUNoRCwyREFLNkI7QUFFN0IsTUFBTSxVQUFVLEdBQUcsMEJBQTBCLENBQUM7QUFvQjlDLE1BQXFCLGdDQUFnQztJQUtuRCxZQUFZLE9BQWdEO1FBQzFELElBQUksQ0FBQyxTQUFTLEdBQUcsT0FBTyxDQUFDLFNBQVMsQ0FBQztRQUNuQyxJQUFJLENBQUMsZ0JBQWdCLEdBQUcsT0FBTyxDQUFDLE1BQU0sQ0FBQztJQUN6QyxDQUFDO0lBRUQsSUFBWSxNQUFNO1FBQ2hCLE9BQU8sSUFBQSxpQ0FBYSxFQUFDLElBQUksQ0FBQyxTQUFTLEVBQUUsSUFBSSxDQUFDLGdCQUFnQixDQUFDLENBQUM7SUFDOUQsQ0FBQztJQUVELElBQVksY0FBYztRQUN4QixPQUFPLElBQUEsa0NBQWdCLEVBQUMsSUFBSSxDQUFDLE1BQU0sRUFBRSxVQUFVLENBQUMsQ0FBQztJQUNuRCxDQUFDO0lBRUQsS0FBSyxDQUFDLElBQUksQ0FBQyxNQUFlO1FBQ3hCLE1BQU0sRUFBRSxNQUFNLEVBQUUsR0FBRyxJQUFJLENBQUM7UUFDeEIsTUFBTSxPQUFPLEdBQUcsSUFBQSwyQkFBUyxFQUFDLE1BQU0sRUFBRSxVQUFVLENBQUMsQ0FBQztRQUU5QyxNQUFNLEtBQUssR0FBRyxJQUFJLGFBQUssQ0FBQztZQUN0QixVQUFVLEVBQUU7Z0JBQ1Y7b0JBQ0UsSUFBSSxFQUFFLGtDQUFrQztvQkFDeEMsRUFBRSxFQUFFLEtBQUssRUFBRSxFQUFFLE9BQU8sRUFBK0IsRUFBRSxFQUFFO3dCQUNyRCx1RkFBdUY7d0JBQ3ZGLG1CQUFtQjt3QkFDbkIsTUFBTSxPQUFP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
|
|
238
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZGF0YWJhc2UtbWNwLW9hdXRoLWNyZWRlbnRpYWxzLXN0b3JlLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL3N0b3Jlcy9kYXRhYmFzZS1tY3Atb2F1dGgtY3JlZGVudGlhbHMtc3RvcmUudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7QUFRQSx5Q0FBc0M7QUFDdEMsaUNBQWdEO0FBRWhELHNDQUFnRDtBQUNoRCwyREFLNkI7QUFFN0IsTUFBTSxVQUFVLEdBQUcsMEJBQTBCLENBQUM7QUFFOUMsa0dBQWtHO0FBQ2xHLDBFQUEwRTtBQUMxRSxNQUFNLGlCQUFpQixHQUFHLEdBQUcsRUFBRSxDQUFDLENBQUM7SUFDL0IsRUFBRSxFQUFFLEVBQUUsSUFBSSxFQUFFLHFCQUFTLENBQUMsT0FBTyxFQUFFLFVBQVUsRUFBRSxJQUFJLEVBQUUsYUFBYSxFQUFFLElBQUksRUFBRTtJQUN0RSxNQUFNLEVBQUUsRUFBRSxJQUFJLEVBQUUscUJBQVMsQ0FBQyxPQUFPLEVBQUUsU0FBUyxFQUFFLEtBQUssRUFBRSxLQUFLLEVBQUUsU0FBUyxFQUFFO0lBQ3ZFLFdBQVcsRUFBRTtRQUNYLElBQUksRUFBRSxxQkFBUyxDQUFDLE1BQU0sQ0FBQyxHQUFHLENBQUM7UUFDM0IsU0FBUyxFQUFFLEtBQUs7UUFDaEIsS0FBSyxFQUFFLGVBQWU7S0FDdkI7SUFDRCxlQUFlLEVBQUU7UUFDZixJQUFJLEVBQUUscUJBQVMsQ0FBQyxJQUFJO1FBQ3BCLFNBQVMsRUFBRSxLQUFLO1FBQ2hCLEtBQUssRUFBRSxtQkFBbUI7S0FDM0I7SUFDRCxRQUFRLEVBQUUsRUFBRSxJQUFJLEVBQUUscUJBQVMsQ0FBQyxNQUFNLENBQUMsR0FBRyxDQUFDLEVBQUUsU0FBUyxFQUFFLElBQUksRUFBRSxLQUFLLEVBQUUsV0FBVyxFQUFFO0lBQzlFLGVBQWUsRUFBRTtRQUNmLElBQUksRUFBRSxxQkFBUyxDQUFDLElBQUk7UUFDcEIsU0FBUyxFQUFFLElBQUk7UUFDZixLQUFL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
|