@wukongcrm/mcp-server 0.1.4 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +32 -3
- package/dist/fc-entry.d.ts +1 -0
- package/dist/fc-entry.js +7 -0
- package/dist/http-entry.js +23 -2
- package/dist/http.d.ts +6 -0
- package/dist/http.js +298 -1
- package/dist/nocode.d.ts +16 -0
- package/dist/nocode.js +1086 -0
- package/dist/oauth-state.d.ts +94 -0
- package/dist/oauth-state.js +310 -0
- package/dist/oauth.d.ts +13 -25
- package/dist/oauth.js +188 -142
- package/dist/server.js +26 -6
- package/package.json +5 -3
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import type { OAuthRegisteredClientsStore } from "@modelcontextprotocol/sdk/server/auth/clients.js";
|
|
2
|
+
import type { OAuthClientInformationFull } from "@modelcontextprotocol/sdk/shared/auth.js";
|
|
3
|
+
export type RegisterableClient = Omit<OAuthClientInformationFull, "client_id" | "client_id_issued_at"> & {
|
|
4
|
+
client_id?: string;
|
|
5
|
+
client_id_issued_at?: number;
|
|
6
|
+
};
|
|
7
|
+
export interface OAuthStateStore extends OAuthRegisteredClientsStore {
|
|
8
|
+
readonly kind: "file" | "redis";
|
|
9
|
+
initialize(): Promise<void>;
|
|
10
|
+
close(): Promise<void>;
|
|
11
|
+
revoke(jti: string, expiresAt: number): Promise<void>;
|
|
12
|
+
isRevoked(jti: string): Promise<boolean>;
|
|
13
|
+
setPendingAuthorization(requestId: string, value: string, expiresAt: number): Promise<void>;
|
|
14
|
+
getPendingAuthorization(requestId: string): Promise<string | undefined>;
|
|
15
|
+
deletePendingAuthorization(requestId: string): Promise<void>;
|
|
16
|
+
setAuthorizationCode(code: string, value: string, expiresAt: number): Promise<void>;
|
|
17
|
+
getAuthorizationCode(code: string): Promise<string | undefined>;
|
|
18
|
+
consumeAuthorizationCode(code: string): Promise<string | undefined>;
|
|
19
|
+
}
|
|
20
|
+
export declare class FileOAuthState implements OAuthStateStore {
|
|
21
|
+
readonly kind: "file";
|
|
22
|
+
private readonly filePath;
|
|
23
|
+
private readonly clients;
|
|
24
|
+
private readonly revoked;
|
|
25
|
+
private readonly pendingAuthorizations;
|
|
26
|
+
private readonly authorizationCodes;
|
|
27
|
+
private readonly ready;
|
|
28
|
+
private persistQueue;
|
|
29
|
+
constructor(dataDirectory: string);
|
|
30
|
+
initialize(): Promise<void>;
|
|
31
|
+
close(): Promise<void>;
|
|
32
|
+
getClient(clientId: string): Promise<OAuthClientInformationFull | undefined>;
|
|
33
|
+
registerClient(client: RegisterableClient): Promise<OAuthClientInformationFull>;
|
|
34
|
+
revoke(jti: string, expiresAt: number): Promise<void>;
|
|
35
|
+
isRevoked(jti: string): Promise<boolean>;
|
|
36
|
+
setPendingAuthorization(requestId: string, value: string, expiresAt: number): Promise<void>;
|
|
37
|
+
getPendingAuthorization(requestId: string): Promise<string | undefined>;
|
|
38
|
+
deletePendingAuthorization(requestId: string): Promise<void>;
|
|
39
|
+
setAuthorizationCode(code: string, value: string, expiresAt: number): Promise<void>;
|
|
40
|
+
getAuthorizationCode(code: string): Promise<string | undefined>;
|
|
41
|
+
consumeAuthorizationCode(code: string): Promise<string | undefined>;
|
|
42
|
+
private setExpiringValue;
|
|
43
|
+
private getExpiringValue;
|
|
44
|
+
private deleteExpiringValue;
|
|
45
|
+
private load;
|
|
46
|
+
private loadExpiringValues;
|
|
47
|
+
private removeExpiredState;
|
|
48
|
+
private persist;
|
|
49
|
+
}
|
|
50
|
+
interface RedisSetOptions {
|
|
51
|
+
EX?: number;
|
|
52
|
+
PX?: number;
|
|
53
|
+
}
|
|
54
|
+
export interface RedisOAuthClient {
|
|
55
|
+
readonly isOpen?: boolean;
|
|
56
|
+
connect(): Promise<unknown>;
|
|
57
|
+
quit(): Promise<unknown>;
|
|
58
|
+
on(event: string, listener: (error: Error) => void): unknown;
|
|
59
|
+
get(key: string): Promise<string | null>;
|
|
60
|
+
set(key: string, value: string, options?: RedisSetOptions): Promise<unknown>;
|
|
61
|
+
del(key: string): Promise<number>;
|
|
62
|
+
sendCommand(args: string[]): Promise<unknown>;
|
|
63
|
+
}
|
|
64
|
+
export interface RedisOAuthStateOptions {
|
|
65
|
+
url: string;
|
|
66
|
+
keyPrefix?: string;
|
|
67
|
+
connectTimeoutMs?: number;
|
|
68
|
+
client?: RedisOAuthClient;
|
|
69
|
+
}
|
|
70
|
+
export declare class RedisOAuthState implements OAuthStateStore {
|
|
71
|
+
readonly kind: "redis";
|
|
72
|
+
private readonly client;
|
|
73
|
+
private readonly keyPrefix;
|
|
74
|
+
private connectPromise?;
|
|
75
|
+
constructor(options: RedisOAuthStateOptions);
|
|
76
|
+
initialize(): Promise<void>;
|
|
77
|
+
close(): Promise<void>;
|
|
78
|
+
getClient(clientId: string): Promise<OAuthClientInformationFull | undefined>;
|
|
79
|
+
registerClient(client: RegisterableClient): Promise<OAuthClientInformationFull>;
|
|
80
|
+
revoke(jti: string, expiresAt: number): Promise<void>;
|
|
81
|
+
isRevoked(jti: string): Promise<boolean>;
|
|
82
|
+
setPendingAuthorization(requestId: string, value: string, expiresAt: number): Promise<void>;
|
|
83
|
+
getPendingAuthorization(requestId: string): Promise<string | undefined>;
|
|
84
|
+
deletePendingAuthorization(requestId: string): Promise<void>;
|
|
85
|
+
setAuthorizationCode(code: string, value: string, expiresAt: number): Promise<void>;
|
|
86
|
+
getAuthorizationCode(code: string): Promise<string | undefined>;
|
|
87
|
+
consumeAuthorizationCode(code: string): Promise<string | undefined>;
|
|
88
|
+
private setExpiringValue;
|
|
89
|
+
private get;
|
|
90
|
+
private set;
|
|
91
|
+
private delete;
|
|
92
|
+
private key;
|
|
93
|
+
}
|
|
94
|
+
export {};
|
|
@@ -0,0 +1,310 @@
|
|
|
1
|
+
import { createHash, randomUUID } from "node:crypto";
|
|
2
|
+
import { mkdir, readFile, rename, writeFile } from "node:fs/promises";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { createClient } from "redis";
|
|
5
|
+
export class FileOAuthState {
|
|
6
|
+
kind = "file";
|
|
7
|
+
filePath;
|
|
8
|
+
clients = new Map();
|
|
9
|
+
revoked = new Map();
|
|
10
|
+
pendingAuthorizations = new Map();
|
|
11
|
+
authorizationCodes = new Map();
|
|
12
|
+
ready;
|
|
13
|
+
persistQueue = Promise.resolve();
|
|
14
|
+
constructor(dataDirectory) {
|
|
15
|
+
this.filePath = path.join(dataDirectory, "oauth-state.json");
|
|
16
|
+
this.ready = this.load();
|
|
17
|
+
}
|
|
18
|
+
async initialize() {
|
|
19
|
+
await this.ready;
|
|
20
|
+
}
|
|
21
|
+
async close() {
|
|
22
|
+
await this.ready;
|
|
23
|
+
await this.persistQueue;
|
|
24
|
+
}
|
|
25
|
+
async getClient(clientId) {
|
|
26
|
+
await this.ready;
|
|
27
|
+
return this.clients.get(clientId);
|
|
28
|
+
}
|
|
29
|
+
async registerClient(client) {
|
|
30
|
+
await this.ready;
|
|
31
|
+
const now = Math.floor(Date.now() / 1000);
|
|
32
|
+
const fullClient = {
|
|
33
|
+
...client,
|
|
34
|
+
client_id: client.client_id ?? randomUUID(),
|
|
35
|
+
client_id_issued_at: client.client_id_issued_at ?? now
|
|
36
|
+
};
|
|
37
|
+
this.clients.set(fullClient.client_id, fullClient);
|
|
38
|
+
await this.persist();
|
|
39
|
+
return fullClient;
|
|
40
|
+
}
|
|
41
|
+
async revoke(jti, expiresAt) {
|
|
42
|
+
await this.ready;
|
|
43
|
+
this.removeExpiredState();
|
|
44
|
+
if (expiresAt > Math.floor(Date.now() / 1000)) {
|
|
45
|
+
this.revoked.set(jti, expiresAt);
|
|
46
|
+
}
|
|
47
|
+
await this.persist();
|
|
48
|
+
}
|
|
49
|
+
async isRevoked(jti) {
|
|
50
|
+
await this.ready;
|
|
51
|
+
const changed = this.removeExpiredState();
|
|
52
|
+
if (changed) {
|
|
53
|
+
await this.persist();
|
|
54
|
+
}
|
|
55
|
+
return this.revoked.has(jti);
|
|
56
|
+
}
|
|
57
|
+
async setPendingAuthorization(requestId, value, expiresAt) {
|
|
58
|
+
await this.setExpiringValue(this.pendingAuthorizations, requestId, value, expiresAt);
|
|
59
|
+
}
|
|
60
|
+
async getPendingAuthorization(requestId) {
|
|
61
|
+
return this.getExpiringValue(this.pendingAuthorizations, requestId);
|
|
62
|
+
}
|
|
63
|
+
async deletePendingAuthorization(requestId) {
|
|
64
|
+
await this.deleteExpiringValue(this.pendingAuthorizations, requestId);
|
|
65
|
+
}
|
|
66
|
+
async setAuthorizationCode(code, value, expiresAt) {
|
|
67
|
+
await this.setExpiringValue(this.authorizationCodes, code, value, expiresAt);
|
|
68
|
+
}
|
|
69
|
+
async getAuthorizationCode(code) {
|
|
70
|
+
return this.getExpiringValue(this.authorizationCodes, code);
|
|
71
|
+
}
|
|
72
|
+
async consumeAuthorizationCode(code) {
|
|
73
|
+
await this.ready;
|
|
74
|
+
const entry = this.authorizationCodes.get(code);
|
|
75
|
+
if (!entry) {
|
|
76
|
+
return undefined;
|
|
77
|
+
}
|
|
78
|
+
this.authorizationCodes.delete(code);
|
|
79
|
+
await this.persist();
|
|
80
|
+
return entry.expiresAt > Date.now() ? entry.value : undefined;
|
|
81
|
+
}
|
|
82
|
+
async setExpiringValue(target, key, value, expiresAt) {
|
|
83
|
+
await this.ready;
|
|
84
|
+
if (expiresAt > Date.now()) {
|
|
85
|
+
target.set(key, { value, expiresAt });
|
|
86
|
+
}
|
|
87
|
+
else {
|
|
88
|
+
target.delete(key);
|
|
89
|
+
}
|
|
90
|
+
await this.persist();
|
|
91
|
+
}
|
|
92
|
+
async getExpiringValue(target, key) {
|
|
93
|
+
await this.ready;
|
|
94
|
+
const entry = target.get(key);
|
|
95
|
+
if (!entry) {
|
|
96
|
+
return undefined;
|
|
97
|
+
}
|
|
98
|
+
if (entry.expiresAt > Date.now()) {
|
|
99
|
+
return entry.value;
|
|
100
|
+
}
|
|
101
|
+
target.delete(key);
|
|
102
|
+
await this.persist();
|
|
103
|
+
return undefined;
|
|
104
|
+
}
|
|
105
|
+
async deleteExpiringValue(target, key) {
|
|
106
|
+
await this.ready;
|
|
107
|
+
if (target.delete(key)) {
|
|
108
|
+
await this.persist();
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
async load() {
|
|
112
|
+
try {
|
|
113
|
+
const parsed = JSON.parse(await readFile(this.filePath, "utf8"));
|
|
114
|
+
for (const [clientId, client] of Object.entries(parsed.clients ?? {})) {
|
|
115
|
+
if (isValidClient(client, clientId)) {
|
|
116
|
+
this.clients.set(clientId, client);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
for (const [jti, expiresAt] of Object.entries(parsed.revoked ?? {})) {
|
|
120
|
+
if (Number.isFinite(expiresAt)) {
|
|
121
|
+
this.revoked.set(jti, expiresAt);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
this.loadExpiringValues(this.pendingAuthorizations, parsed.pendingAuthorizations);
|
|
125
|
+
this.loadExpiringValues(this.authorizationCodes, parsed.authorizationCodes);
|
|
126
|
+
this.removeExpiredState();
|
|
127
|
+
}
|
|
128
|
+
catch (error) {
|
|
129
|
+
if (error.code !== "ENOENT") {
|
|
130
|
+
throw error;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
loadExpiringValues(target, values) {
|
|
135
|
+
for (const [key, entry] of Object.entries(values ?? {})) {
|
|
136
|
+
if (entry && typeof entry.value === "string" && Number.isFinite(entry.expiresAt)) {
|
|
137
|
+
target.set(key, entry);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
removeExpiredState() {
|
|
142
|
+
let changed = false;
|
|
143
|
+
const nowSeconds = Math.floor(Date.now() / 1000);
|
|
144
|
+
for (const [jti, expiresAt] of this.revoked) {
|
|
145
|
+
if (expiresAt <= nowSeconds) {
|
|
146
|
+
changed = this.revoked.delete(jti) || changed;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
const nowMilliseconds = Date.now();
|
|
150
|
+
for (const target of [this.pendingAuthorizations, this.authorizationCodes]) {
|
|
151
|
+
for (const [key, entry] of target) {
|
|
152
|
+
if (entry.expiresAt <= nowMilliseconds) {
|
|
153
|
+
changed = target.delete(key) || changed;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
return changed;
|
|
158
|
+
}
|
|
159
|
+
async persist() {
|
|
160
|
+
const operation = this.persistQueue.catch(() => undefined).then(async () => {
|
|
161
|
+
this.removeExpiredState();
|
|
162
|
+
const directory = path.dirname(this.filePath);
|
|
163
|
+
await mkdir(directory, { recursive: true });
|
|
164
|
+
const temporaryPath = `${this.filePath}.${process.pid}.tmp`;
|
|
165
|
+
const state = {
|
|
166
|
+
clients: Object.fromEntries(this.clients),
|
|
167
|
+
revoked: Object.fromEntries(this.revoked),
|
|
168
|
+
pendingAuthorizations: Object.fromEntries(this.pendingAuthorizations),
|
|
169
|
+
authorizationCodes: Object.fromEntries(this.authorizationCodes)
|
|
170
|
+
};
|
|
171
|
+
await writeFile(temporaryPath, `${JSON.stringify(state, null, 2)}\n`, {
|
|
172
|
+
encoding: "utf8",
|
|
173
|
+
mode: 0o600
|
|
174
|
+
});
|
|
175
|
+
await rename(temporaryPath, this.filePath);
|
|
176
|
+
});
|
|
177
|
+
this.persistQueue = operation;
|
|
178
|
+
await operation;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
export class RedisOAuthState {
|
|
182
|
+
kind = "redis";
|
|
183
|
+
client;
|
|
184
|
+
keyPrefix;
|
|
185
|
+
connectPromise;
|
|
186
|
+
constructor(options) {
|
|
187
|
+
const redisUrl = validateRedisUrl(options.url);
|
|
188
|
+
this.keyPrefix = normalizeKeyPrefix(options.keyPrefix ?? "wukong-mcp:oauth");
|
|
189
|
+
this.client = options.client ?? createClient({
|
|
190
|
+
url: redisUrl,
|
|
191
|
+
socket: {
|
|
192
|
+
connectTimeout: options.connectTimeoutMs ?? 5_000,
|
|
193
|
+
reconnectStrategy: (retries) => retries >= 5
|
|
194
|
+
? new Error("Redis OAuth state connection retry limit reached.")
|
|
195
|
+
: Math.min(100 * (2 ** retries), 2_000)
|
|
196
|
+
}
|
|
197
|
+
});
|
|
198
|
+
this.client.on("error", (error) => {
|
|
199
|
+
console.error("Redis OAuth state error:", error.message);
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
async initialize() {
|
|
203
|
+
if (this.client.isOpen) {
|
|
204
|
+
return;
|
|
205
|
+
}
|
|
206
|
+
this.connectPromise ??= this.client.connect().then(() => undefined);
|
|
207
|
+
await this.connectPromise;
|
|
208
|
+
}
|
|
209
|
+
async close() {
|
|
210
|
+
if (this.client.isOpen) {
|
|
211
|
+
await this.client.quit();
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
async getClient(clientId) {
|
|
215
|
+
const value = await this.get(this.key("client", clientId));
|
|
216
|
+
if (!value) {
|
|
217
|
+
return undefined;
|
|
218
|
+
}
|
|
219
|
+
const client = JSON.parse(value);
|
|
220
|
+
return isValidClient(client, clientId) ? client : undefined;
|
|
221
|
+
}
|
|
222
|
+
async registerClient(client) {
|
|
223
|
+
const now = Math.floor(Date.now() / 1000);
|
|
224
|
+
const fullClient = {
|
|
225
|
+
...client,
|
|
226
|
+
client_id: client.client_id ?? randomUUID(),
|
|
227
|
+
client_id_issued_at: client.client_id_issued_at ?? now
|
|
228
|
+
};
|
|
229
|
+
await this.set(this.key("client", fullClient.client_id), JSON.stringify(fullClient));
|
|
230
|
+
return fullClient;
|
|
231
|
+
}
|
|
232
|
+
async revoke(jti, expiresAt) {
|
|
233
|
+
const ttlSeconds = expiresAt - Math.floor(Date.now() / 1000);
|
|
234
|
+
const key = this.key("revoked", jti);
|
|
235
|
+
if (ttlSeconds <= 0) {
|
|
236
|
+
await this.delete(key);
|
|
237
|
+
return;
|
|
238
|
+
}
|
|
239
|
+
await this.set(key, "1", { EX: ttlSeconds });
|
|
240
|
+
}
|
|
241
|
+
async isRevoked(jti) {
|
|
242
|
+
return (await this.get(this.key("revoked", jti))) !== null;
|
|
243
|
+
}
|
|
244
|
+
async setPendingAuthorization(requestId, value, expiresAt) {
|
|
245
|
+
await this.setExpiringValue(this.key("pending", requestId), value, expiresAt);
|
|
246
|
+
}
|
|
247
|
+
async getPendingAuthorization(requestId) {
|
|
248
|
+
return (await this.get(this.key("pending", requestId))) ?? undefined;
|
|
249
|
+
}
|
|
250
|
+
async deletePendingAuthorization(requestId) {
|
|
251
|
+
await this.delete(this.key("pending", requestId));
|
|
252
|
+
}
|
|
253
|
+
async setAuthorizationCode(code, value, expiresAt) {
|
|
254
|
+
await this.setExpiringValue(this.key("code", code), value, expiresAt);
|
|
255
|
+
}
|
|
256
|
+
async getAuthorizationCode(code) {
|
|
257
|
+
return (await this.get(this.key("code", code))) ?? undefined;
|
|
258
|
+
}
|
|
259
|
+
async consumeAuthorizationCode(code) {
|
|
260
|
+
await this.initialize();
|
|
261
|
+
const value = await this.client.sendCommand(["GETDEL", this.key("code", code)]);
|
|
262
|
+
return typeof value === "string" ? value : undefined;
|
|
263
|
+
}
|
|
264
|
+
async setExpiringValue(key, value, expiresAt) {
|
|
265
|
+
const ttlMilliseconds = Math.ceil(expiresAt - Date.now());
|
|
266
|
+
if (ttlMilliseconds <= 0) {
|
|
267
|
+
await this.delete(key);
|
|
268
|
+
return;
|
|
269
|
+
}
|
|
270
|
+
await this.set(key, value, { PX: ttlMilliseconds });
|
|
271
|
+
}
|
|
272
|
+
async get(key) {
|
|
273
|
+
await this.initialize();
|
|
274
|
+
return this.client.get(key);
|
|
275
|
+
}
|
|
276
|
+
async set(key, value, options) {
|
|
277
|
+
await this.initialize();
|
|
278
|
+
await this.client.set(key, value, options);
|
|
279
|
+
}
|
|
280
|
+
async delete(key) {
|
|
281
|
+
await this.initialize();
|
|
282
|
+
await this.client.del(key);
|
|
283
|
+
}
|
|
284
|
+
key(type, identifier) {
|
|
285
|
+
const digest = createHash("sha256").update(identifier, "utf8").digest("base64url");
|
|
286
|
+
return `${this.keyPrefix}:${type}:${digest}`;
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
function isValidClient(value, expectedClientId) {
|
|
290
|
+
if (!value || typeof value !== "object") {
|
|
291
|
+
return false;
|
|
292
|
+
}
|
|
293
|
+
const client = value;
|
|
294
|
+
return client.client_id === expectedClientId && Array.isArray(client.redirect_uris);
|
|
295
|
+
}
|
|
296
|
+
function validateRedisUrl(value) {
|
|
297
|
+
const normalized = value.trim();
|
|
298
|
+
const url = new URL(normalized);
|
|
299
|
+
if (url.protocol !== "redis:" && url.protocol !== "rediss:") {
|
|
300
|
+
throw new Error("MCP_REDIS_URL 必须使用 redis:// 或 rediss://。");
|
|
301
|
+
}
|
|
302
|
+
return normalized;
|
|
303
|
+
}
|
|
304
|
+
function normalizeKeyPrefix(value) {
|
|
305
|
+
const normalized = value.trim().replace(/:+$/, "");
|
|
306
|
+
if (!normalized || /\s/.test(normalized)) {
|
|
307
|
+
throw new Error("MCP_REDIS_PREFIX 不能为空或包含空白字符。");
|
|
308
|
+
}
|
|
309
|
+
return normalized;
|
|
310
|
+
}
|
package/dist/oauth.d.ts
CHANGED
|
@@ -1,43 +1,28 @@
|
|
|
1
|
-
import type { OAuthRegisteredClientsStore } from "@modelcontextprotocol/sdk/server/auth/clients.js";
|
|
2
1
|
import type { OAuthServerProvider, AuthorizationParams } from "@modelcontextprotocol/sdk/server/auth/provider.js";
|
|
3
2
|
import type { AuthInfo } from "@modelcontextprotocol/sdk/server/auth/types.js";
|
|
4
3
|
import type { OAuthClientInformationFull, OAuthTokenRevocationRequest, OAuthTokens } from "@modelcontextprotocol/sdk/shared/auth.js";
|
|
5
4
|
import type { Response } from "express";
|
|
6
|
-
|
|
7
|
-
client_id?: string;
|
|
8
|
-
client_id_issued_at?: number;
|
|
9
|
-
};
|
|
10
|
-
export declare class FileOAuthState implements OAuthRegisteredClientsStore {
|
|
11
|
-
private readonly filePath;
|
|
12
|
-
private readonly clients;
|
|
13
|
-
private readonly revoked;
|
|
14
|
-
private readonly ready;
|
|
15
|
-
private persistQueue;
|
|
16
|
-
constructor(dataDirectory: string);
|
|
17
|
-
getClient(clientId: string): Promise<OAuthClientInformationFull | undefined>;
|
|
18
|
-
registerClient(client: RegisterableClient): Promise<OAuthClientInformationFull>;
|
|
19
|
-
revoke(jti: string, expiresAt: number): Promise<void>;
|
|
20
|
-
isRevoked(jti: string): Promise<boolean>;
|
|
21
|
-
private load;
|
|
22
|
-
private removeExpiredRevocations;
|
|
23
|
-
private persist;
|
|
24
|
-
}
|
|
5
|
+
import { type OAuthStateStore } from "./oauth-state.js";
|
|
25
6
|
export interface WukongOAuthProviderOptions {
|
|
26
7
|
resourceUrl: URL;
|
|
27
8
|
crmBaseUrl?: string;
|
|
28
9
|
crmBindingAuthorizeUrl: URL;
|
|
29
10
|
crmBindingExchangeUrl: URL;
|
|
11
|
+
crmBindingExchangeAllowHttp?: boolean;
|
|
30
12
|
crmBindingCallbackUrl: URL;
|
|
31
13
|
crmBindingClientId: string;
|
|
32
14
|
crmBindingClientSecret: string;
|
|
33
15
|
secret: string;
|
|
34
16
|
dataDirectory: string;
|
|
17
|
+
redisUrl?: string;
|
|
18
|
+
redisKeyPrefix?: string;
|
|
19
|
+
stateStore?: OAuthStateStore;
|
|
35
20
|
fetchImpl?: typeof fetch;
|
|
36
21
|
accessTokenTtlSeconds?: number;
|
|
37
22
|
refreshTokenTtlSeconds?: number;
|
|
38
23
|
}
|
|
39
24
|
export declare class WukongOAuthProvider implements OAuthServerProvider {
|
|
40
|
-
readonly clientsStore:
|
|
25
|
+
readonly clientsStore: OAuthStateStore;
|
|
41
26
|
private readonly resourceUrl;
|
|
42
27
|
private readonly crmBaseUrl?;
|
|
43
28
|
private readonly fetchImpl?;
|
|
@@ -49,9 +34,9 @@ export declare class WukongOAuthProvider implements OAuthServerProvider {
|
|
|
49
34
|
private readonly encryptionKey;
|
|
50
35
|
private readonly accessTokenTtlSeconds;
|
|
51
36
|
private readonly refreshTokenTtlSeconds;
|
|
52
|
-
private readonly pendingAuthorizations;
|
|
53
|
-
private readonly authorizationCodes;
|
|
54
37
|
constructor(options: WukongOAuthProviderOptions);
|
|
38
|
+
initialize(): Promise<void>;
|
|
39
|
+
close(): Promise<void>;
|
|
55
40
|
authorize(client: OAuthClientInformationFull, params: AuthorizationParams, res: Response): Promise<void>;
|
|
56
41
|
completeBindingAuthorization(requestId: string, bindingCode: string): Promise<string>;
|
|
57
42
|
private exchangeBindingCode;
|
|
@@ -61,11 +46,14 @@ export declare class WukongOAuthProvider implements OAuthServerProvider {
|
|
|
61
46
|
verifyAccessToken(token: string): Promise<AuthInfo>;
|
|
62
47
|
revokeToken(client: OAuthClientInformationFull, request: OAuthTokenRevocationRequest): Promise<void>;
|
|
63
48
|
private getAuthorizationCode;
|
|
49
|
+
private consumeAuthorizationCode;
|
|
64
50
|
private issueTokens;
|
|
65
51
|
private decodeAndVerifyToken;
|
|
66
52
|
private encryptToken;
|
|
67
53
|
private decryptToken;
|
|
54
|
+
private encryptTransientState;
|
|
55
|
+
private decryptTransientState;
|
|
56
|
+
private encryptValue;
|
|
57
|
+
private decryptValue;
|
|
68
58
|
private assertResource;
|
|
69
|
-
private cleanupTransientState;
|
|
70
59
|
}
|
|
71
|
-
export {};
|