@frontmcp/plugin-approval 0.0.1 → 0.7.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.
Files changed (46) hide show
  1. package/approval/errors.d.ts +149 -0
  2. package/approval/errors.d.ts.map +1 -0
  3. package/approval/factories.d.ts +251 -0
  4. package/approval/factories.d.ts.map +1 -0
  5. package/approval/guards.d.ts +61 -0
  6. package/approval/guards.d.ts.map +1 -0
  7. package/approval/index.d.ts +43 -0
  8. package/approval/index.d.ts.map +1 -0
  9. package/approval/schemas.d.ts +179 -0
  10. package/approval/schemas.d.ts.map +1 -0
  11. package/approval/types.d.ts +252 -0
  12. package/approval/types.d.ts.map +1 -0
  13. package/approval.context-extension.d.ts +21 -0
  14. package/approval.context-extension.d.ts.map +1 -0
  15. package/approval.plugin.d.ts +128 -0
  16. package/approval.plugin.d.ts.map +1 -0
  17. package/approval.symbols.d.ts +22 -0
  18. package/approval.symbols.d.ts.map +1 -0
  19. package/esm/index.mjs +1228 -0
  20. package/esm/package.json +66 -0
  21. package/flows/index.d.ts +9 -0
  22. package/flows/index.d.ts.map +1 -0
  23. package/hooks/approval-check.hook.d.ts +25 -0
  24. package/hooks/approval-check.hook.d.ts.map +1 -0
  25. package/hooks/index.d.ts +7 -0
  26. package/hooks/index.d.ts.map +1 -0
  27. package/index.d.ts +44 -0
  28. package/index.d.ts.map +1 -0
  29. package/index.js +1279 -0
  30. package/package.json +1 -1
  31. package/services/approval.service.d.ts +85 -0
  32. package/services/approval.service.d.ts.map +1 -0
  33. package/services/challenge.service.d.ts +115 -0
  34. package/services/challenge.service.d.ts.map +1 -0
  35. package/services/index.d.ts +8 -0
  36. package/services/index.d.ts.map +1 -0
  37. package/stores/approval-storage.store.d.ts +71 -0
  38. package/stores/approval-storage.store.d.ts.map +1 -0
  39. package/stores/approval-store.interface.d.ts +121 -0
  40. package/stores/approval-store.interface.d.ts.map +1 -0
  41. package/stores/index.d.ts +8 -0
  42. package/stores/index.d.ts.map +1 -0
  43. package/types/approval.types.d.ts +98 -0
  44. package/types/approval.types.d.ts.map +1 -0
  45. package/types/index.d.ts +7 -0
  46. package/types/index.d.ts.map +1 -0
package/esm/index.mjs ADDED
@@ -0,0 +1,1228 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
3
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
4
+ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
5
+ get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
6
+ }) : x)(function(x) {
7
+ if (typeof require !== "undefined") return require.apply(this, arguments);
8
+ throw Error('Dynamic require of "' + x + '" is not supported');
9
+ });
10
+ var __decorateClass = (decorators, target, key, kind) => {
11
+ var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
12
+ for (var i = decorators.length - 1, decorator; i >= 0; i--)
13
+ if (decorator = decorators[i])
14
+ result = (kind ? decorator(target, key, result) : decorator(result)) || result;
15
+ if (kind && result) __defProp(target, key, result);
16
+ return result;
17
+ };
18
+ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
19
+
20
+ // plugins/plugin-approval/src/approval.plugin.ts
21
+ import { DynamicPlugin as DynamicPlugin2, Plugin as Plugin2, ProviderScope as ProviderScope4, FRONTMCP_CONTEXT } from "@frontmcp/sdk";
22
+
23
+ // plugins/plugin-approval/src/stores/approval-storage.store.ts
24
+ import { Provider, ProviderScope } from "@frontmcp/sdk";
25
+ import {
26
+ createStorage,
27
+ createMemoryStorage
28
+ } from "@frontmcp/utils";
29
+
30
+ // plugins/plugin-approval/src/approval/types.ts
31
+ var ApprovalScope = /* @__PURE__ */ ((ApprovalScope2) => {
32
+ ApprovalScope2["SESSION"] = "session";
33
+ ApprovalScope2["USER"] = "user";
34
+ ApprovalScope2["TIME_LIMITED"] = "time_limited";
35
+ ApprovalScope2["TOOL_SPECIFIC"] = "tool_specific";
36
+ ApprovalScope2["CONTEXT_SPECIFIC"] = "context_specific";
37
+ return ApprovalScope2;
38
+ })(ApprovalScope || {});
39
+ var ApprovalState = /* @__PURE__ */ ((ApprovalState2) => {
40
+ ApprovalState2["PENDING"] = "pending";
41
+ ApprovalState2["APPROVED"] = "approved";
42
+ ApprovalState2["DENIED"] = "denied";
43
+ ApprovalState2["EXPIRED"] = "expired";
44
+ return ApprovalState2;
45
+ })(ApprovalState || {});
46
+
47
+ // plugins/plugin-approval/src/approval/schemas.ts
48
+ import { z } from "zod";
49
+ var approvalScopeSchema = z.nativeEnum(ApprovalScope);
50
+ var approvalStateSchema = z.nativeEnum(ApprovalState);
51
+ var approvalMethodSchema = z.enum(["interactive", "implicit", "delegation", "batch", "api"]);
52
+ var approvalSourceTypeSchema = z.string().min(1);
53
+ var revocationMethodSchema = z.enum(["interactive", "implicit", "policy", "expiry"]);
54
+ var approvalCategorySchema = z.enum(["read", "write", "delete", "execute", "admin"]);
55
+ var riskLevelSchema = z.enum(["low", "medium", "high", "critical"]);
56
+ var approvalContextSchema = z.object({
57
+ type: z.string().min(1),
58
+ identifier: z.string().min(1),
59
+ metadata: z.record(z.string(), z.unknown()).optional()
60
+ });
61
+ var delegationContextSchema = z.object({
62
+ delegatorId: z.string().min(1),
63
+ delegateId: z.string().min(1),
64
+ purpose: z.string().optional(),
65
+ constraints: z.record(z.string(), z.unknown()).optional()
66
+ });
67
+ var approvalGrantorSchema = z.object({
68
+ source: approvalSourceTypeSchema,
69
+ identifier: z.string().optional(),
70
+ displayName: z.string().optional(),
71
+ method: approvalMethodSchema.optional(),
72
+ origin: z.string().optional(),
73
+ delegationContext: delegationContextSchema.optional()
74
+ });
75
+ var approvalRevokerSchema = z.object({
76
+ source: z.string().min(1),
77
+ identifier: z.string().optional(),
78
+ displayName: z.string().optional(),
79
+ method: revocationMethodSchema.optional()
80
+ });
81
+ var approvalRecordSchema = z.object({
82
+ toolId: z.string().min(1),
83
+ state: approvalStateSchema,
84
+ scope: approvalScopeSchema,
85
+ grantedAt: z.number(),
86
+ expiresAt: z.number().optional(),
87
+ ttlMs: z.number().optional(),
88
+ sessionId: z.string().optional(),
89
+ userId: z.string().optional(),
90
+ context: approvalContextSchema.optional(),
91
+ grantedBy: approvalGrantorSchema,
92
+ approvalChain: z.array(approvalGrantorSchema).optional(),
93
+ reason: z.string().optional(),
94
+ metadata: z.record(z.string(), z.unknown()).optional(),
95
+ revokedAt: z.number().optional(),
96
+ revokedBy: approvalRevokerSchema.optional(),
97
+ revocationReason: z.string().optional()
98
+ });
99
+ var toolApprovalRequirementSchema = z.object({
100
+ required: z.boolean().optional(),
101
+ defaultScope: approvalScopeSchema.optional(),
102
+ allowedScopes: z.array(approvalScopeSchema).optional(),
103
+ maxTtlMs: z.number().positive().optional(),
104
+ alwaysPrompt: z.boolean().optional(),
105
+ skipApproval: z.boolean().optional(),
106
+ approvalMessage: z.string().optional(),
107
+ category: approvalCategorySchema.optional(),
108
+ riskLevel: riskLevelSchema.optional(),
109
+ preApprovedContexts: z.array(approvalContextSchema).optional()
110
+ });
111
+
112
+ // plugins/plugin-approval/src/approval/factories.ts
113
+ function userGrantor(userId, displayName, options) {
114
+ return {
115
+ source: "user",
116
+ identifier: userId,
117
+ displayName,
118
+ method: options?.method ?? "interactive",
119
+ origin: options?.origin
120
+ };
121
+ }
122
+ function policyGrantor(policyId, policyName) {
123
+ return {
124
+ source: "policy",
125
+ identifier: policyId,
126
+ displayName: policyName,
127
+ method: "implicit"
128
+ };
129
+ }
130
+ function adminGrantor(adminId, displayName, options) {
131
+ return {
132
+ source: "admin",
133
+ identifier: adminId,
134
+ displayName,
135
+ method: options?.method ?? "interactive",
136
+ origin: options?.origin
137
+ };
138
+ }
139
+ function systemGrantor(systemId = "system") {
140
+ return {
141
+ source: "system",
142
+ identifier: systemId,
143
+ method: "implicit"
144
+ };
145
+ }
146
+ function agentGrantor(agentId, delegationContext, displayName) {
147
+ return {
148
+ source: "agent",
149
+ identifier: agentId,
150
+ displayName,
151
+ method: "delegation",
152
+ delegationContext
153
+ };
154
+ }
155
+ function apiGrantor(apiKeyPrefix, serviceName) {
156
+ return {
157
+ source: "api",
158
+ identifier: apiKeyPrefix,
159
+ displayName: serviceName,
160
+ method: "api",
161
+ origin: "api"
162
+ };
163
+ }
164
+ function oauthGrantor(tokenId, provider) {
165
+ return {
166
+ source: "oauth",
167
+ identifier: tokenId,
168
+ displayName: provider,
169
+ method: "api",
170
+ origin: "oauth"
171
+ };
172
+ }
173
+ function testGrantor() {
174
+ return {
175
+ source: "test",
176
+ identifier: "test",
177
+ method: "implicit"
178
+ };
179
+ }
180
+ function customGrantor(source, identifier, options) {
181
+ return {
182
+ source,
183
+ identifier,
184
+ displayName: options?.displayName,
185
+ method: options?.method,
186
+ origin: options?.origin,
187
+ delegationContext: options?.delegationContext
188
+ };
189
+ }
190
+ function userRevoker(userId, displayName) {
191
+ return {
192
+ source: "user",
193
+ identifier: userId,
194
+ displayName,
195
+ method: "interactive"
196
+ };
197
+ }
198
+ function adminRevoker(adminId, displayName) {
199
+ return {
200
+ source: "admin",
201
+ identifier: adminId,
202
+ displayName,
203
+ method: "interactive"
204
+ };
205
+ }
206
+ function expiryRevoker() {
207
+ return {
208
+ source: "expiry",
209
+ method: "expiry"
210
+ };
211
+ }
212
+ function sessionEndRevoker(sessionId) {
213
+ return {
214
+ source: "session_end",
215
+ identifier: sessionId,
216
+ method: "implicit"
217
+ };
218
+ }
219
+ function policyRevoker(policyId, policyName) {
220
+ return {
221
+ source: "policy",
222
+ identifier: policyId,
223
+ displayName: policyName,
224
+ method: "policy"
225
+ };
226
+ }
227
+ function normalizeGrantor(input) {
228
+ if (!input) {
229
+ return { source: "user" };
230
+ }
231
+ if (typeof input === "string") {
232
+ return { source: input };
233
+ }
234
+ return input;
235
+ }
236
+ function normalizeRevoker(input) {
237
+ if (!input) {
238
+ return { source: "user" };
239
+ }
240
+ if (typeof input === "string") {
241
+ return { source: input };
242
+ }
243
+ return input;
244
+ }
245
+
246
+ // plugins/plugin-approval/src/approval/guards.ts
247
+ function isGrantorSource(grantor, source) {
248
+ return grantor.source === source;
249
+ }
250
+ function isHumanGrantor(grantor) {
251
+ return grantor.source === "user" || grantor.source === "admin";
252
+ }
253
+ function isAutoGrantor(grantor) {
254
+ return grantor.source === "policy" || grantor.source === "system" || grantor.source === "test";
255
+ }
256
+ function isDelegatedGrantor(grantor) {
257
+ return grantor.source === "agent" && !!grantor.delegationContext;
258
+ }
259
+ function isApiGrantor(grantor) {
260
+ return grantor.source === "api" || grantor.source === "oauth";
261
+ }
262
+
263
+ // plugins/plugin-approval/src/approval/errors.ts
264
+ var ApprovalError = class extends Error {
265
+ constructor(message) {
266
+ super(message);
267
+ this.name = "ApprovalError";
268
+ }
269
+ };
270
+ var ApprovalRequiredError = class extends ApprovalError {
271
+ constructor(details) {
272
+ super(details.message);
273
+ this.details = details;
274
+ this.name = "ApprovalRequiredError";
275
+ }
276
+ /**
277
+ * Convert to a JSON-RPC compatible error structure.
278
+ */
279
+ toJsonRpcError() {
280
+ return {
281
+ code: -32600,
282
+ // Invalid Request
283
+ message: this.details.message,
284
+ data: {
285
+ type: "approval_required",
286
+ toolId: this.details.toolId,
287
+ state: this.details.state,
288
+ options: this.details.approvalOptions
289
+ }
290
+ };
291
+ }
292
+ };
293
+ var ApprovalOperationError = class extends ApprovalError {
294
+ constructor(operation, reason) {
295
+ super(`Approval ${operation} failed: ${reason}`);
296
+ this.operation = operation;
297
+ this.reason = reason;
298
+ this.name = "ApprovalOperationError";
299
+ }
300
+ /**
301
+ * Convert to a JSON-RPC compatible error structure.
302
+ */
303
+ toJsonRpcError() {
304
+ return {
305
+ code: -32603,
306
+ // Internal Error
307
+ message: "Approval operation failed",
308
+ data: {
309
+ type: "approval_operation_error",
310
+ operation: this.operation
311
+ }
312
+ };
313
+ }
314
+ };
315
+ var ApprovalScopeNotAllowedError = class extends ApprovalError {
316
+ constructor(requestedScope, allowedScopes) {
317
+ super(
318
+ `Approval scope '${requestedScope}' is not allowed for this tool. Allowed scopes: ${allowedScopes.join(", ")}`
319
+ );
320
+ this.requestedScope = requestedScope;
321
+ this.allowedScopes = allowedScopes;
322
+ this.name = "ApprovalScopeNotAllowedError";
323
+ }
324
+ /**
325
+ * Convert to a JSON-RPC compatible error structure.
326
+ */
327
+ toJsonRpcError() {
328
+ return {
329
+ code: -32602,
330
+ // Invalid Params
331
+ message: this.message,
332
+ data: {
333
+ type: "approval_scope_not_allowed",
334
+ requestedScope: this.requestedScope,
335
+ allowedScopes: this.allowedScopes
336
+ }
337
+ };
338
+ }
339
+ };
340
+ var ApprovalExpiredError = class extends ApprovalError {
341
+ constructor(toolId, expiredAt) {
342
+ super(`Approval for tool '${toolId}' expired at ${new Date(expiredAt).toISOString()}`);
343
+ this.toolId = toolId;
344
+ this.expiredAt = expiredAt;
345
+ this.name = "ApprovalExpiredError";
346
+ }
347
+ /**
348
+ * Convert to a JSON-RPC compatible error structure.
349
+ */
350
+ toJsonRpcError() {
351
+ return {
352
+ code: -32600,
353
+ // Invalid Request
354
+ message: this.message,
355
+ data: {
356
+ type: "approval_expired",
357
+ toolId: this.toolId,
358
+ expiredAt: this.expiredAt
359
+ }
360
+ };
361
+ }
362
+ };
363
+ var ChallengeValidationError = class extends ApprovalError {
364
+ constructor(reason = "invalid", message) {
365
+ super(message ?? `PKCE challenge validation failed: ${reason}`);
366
+ this.reason = reason;
367
+ this.name = "ChallengeValidationError";
368
+ }
369
+ /**
370
+ * Convert to a JSON-RPC compatible error structure.
371
+ */
372
+ toJsonRpcError() {
373
+ return {
374
+ code: -32600,
375
+ // Invalid Request
376
+ message: this.message,
377
+ data: {
378
+ type: "challenge_validation_error",
379
+ reason: this.reason
380
+ }
381
+ };
382
+ }
383
+ };
384
+
385
+ // plugins/plugin-approval/src/stores/approval-storage.store.ts
386
+ function escapePattern(str) {
387
+ return str.replace(/[*?[\]\\]/g, "\\$&");
388
+ }
389
+ var ApprovalStorageStore = class {
390
+ storage;
391
+ options;
392
+ cleanupInterval;
393
+ initialized = false;
394
+ ownedStorage = false;
395
+ constructor(options = {}) {
396
+ this.options = {
397
+ storage: options.storage ?? { type: "auto" },
398
+ storageInstance: options.storageInstance,
399
+ namespace: options.namespace ?? "approval",
400
+ cleanupIntervalSeconds: options.cleanupIntervalSeconds ?? 60
401
+ };
402
+ }
403
+ /**
404
+ * Initialize the storage connection.
405
+ */
406
+ async initialize() {
407
+ if (this.initialized) {
408
+ return;
409
+ }
410
+ if (this.options.storageInstance) {
411
+ this.storage = this.options.storageInstance.namespace(this.options.namespace);
412
+ this.ownedStorage = false;
413
+ } else {
414
+ const rootStorage = await createStorage(this.options.storage);
415
+ this.storage = rootStorage.namespace(this.options.namespace);
416
+ this.ownedStorage = true;
417
+ }
418
+ if (this.options.cleanupIntervalSeconds > 0) {
419
+ this.cleanupInterval = setInterval(() => {
420
+ void this.clearExpiredApprovals();
421
+ }, this.options.cleanupIntervalSeconds * 1e3);
422
+ this.cleanupInterval.unref?.();
423
+ }
424
+ this.initialized = true;
425
+ }
426
+ ensureInitialized() {
427
+ if (!this.initialized) {
428
+ throw new Error("ApprovalStorageStore not initialized. Call initialize() first.");
429
+ }
430
+ }
431
+ buildKey(toolId, sessionId, userId, context) {
432
+ const parts = [toolId];
433
+ if (sessionId) parts.push(`session:${sessionId}`);
434
+ if (userId) parts.push(`user:${userId}`);
435
+ if (context) parts.push(`ctx:${context.type}:${context.identifier}`);
436
+ return parts.join(":");
437
+ }
438
+ parseRecord(value) {
439
+ if (!value) return void 0;
440
+ try {
441
+ const parsed = JSON.parse(value);
442
+ const result = approvalRecordSchema.safeParse(parsed);
443
+ if (!result.success) {
444
+ return void 0;
445
+ }
446
+ return result.data;
447
+ } catch {
448
+ return void 0;
449
+ }
450
+ }
451
+ isExpired(approval) {
452
+ return approval.expiresAt !== void 0 && Date.now() > approval.expiresAt;
453
+ }
454
+ async getApproval(toolId, sessionId, userId) {
455
+ this.ensureInitialized();
456
+ const sessionKey = this.buildKey(toolId, sessionId);
457
+ const sessionValue = await this.storage.get(sessionKey);
458
+ const sessionApproval = this.parseRecord(sessionValue);
459
+ if (sessionApproval && !this.isExpired(sessionApproval)) {
460
+ return sessionApproval;
461
+ }
462
+ if (userId) {
463
+ const userKey = this.buildKey(toolId, void 0, userId);
464
+ const userValue = await this.storage.get(userKey);
465
+ const userApproval = this.parseRecord(userValue);
466
+ if (userApproval && !this.isExpired(userApproval)) {
467
+ return userApproval;
468
+ }
469
+ }
470
+ return void 0;
471
+ }
472
+ async queryApprovals(query) {
473
+ this.ensureInitialized();
474
+ const results = [];
475
+ const pattern = query.toolId ? `${query.toolId}:*` : "*";
476
+ const keys = await this.storage.keys(pattern);
477
+ const values = await this.storage.mget(keys);
478
+ for (const value of values) {
479
+ const approval = this.parseRecord(value);
480
+ if (!approval) continue;
481
+ if (!query.includeExpired && this.isExpired(approval)) {
482
+ continue;
483
+ }
484
+ if (query.toolId && approval.toolId !== query.toolId) continue;
485
+ if (query.toolIds && !query.toolIds.includes(approval.toolId)) continue;
486
+ if (query.scope && approval.scope !== query.scope) continue;
487
+ if (query.scopes && !query.scopes.includes(approval.scope)) continue;
488
+ if (query.state && approval.state !== query.state) continue;
489
+ if (query.states && !query.states.includes(approval.state)) continue;
490
+ if (query.sessionId && approval.sessionId !== query.sessionId) continue;
491
+ if (query.userId && approval.userId !== query.userId) continue;
492
+ if (query.context) {
493
+ if (!approval.context || approval.context.type !== query.context.type || approval.context.identifier !== query.context.identifier) {
494
+ continue;
495
+ }
496
+ }
497
+ results.push(approval);
498
+ }
499
+ return results;
500
+ }
501
+ async grantApproval(options) {
502
+ this.ensureInitialized();
503
+ const now = Date.now();
504
+ const expiresAt = options.ttlMs ? now + options.ttlMs : void 0;
505
+ const grantedBy = normalizeGrantor(options.grantedBy);
506
+ const record = {
507
+ toolId: options.toolId,
508
+ state: "approved" /* APPROVED */,
509
+ scope: options.scope,
510
+ grantedAt: now,
511
+ expiresAt,
512
+ ttlMs: options.ttlMs,
513
+ sessionId: options.sessionId,
514
+ userId: options.userId,
515
+ context: options.context,
516
+ grantedBy,
517
+ reason: options.reason,
518
+ metadata: options.metadata
519
+ };
520
+ const key = this.buildKey(options.toolId, options.sessionId, options.userId, options.context);
521
+ const ttlSeconds = options.ttlMs ? Math.ceil(options.ttlMs / 1e3) : void 0;
522
+ await this.storage.set(key, JSON.stringify(record), { ttlSeconds });
523
+ return record;
524
+ }
525
+ async revokeApproval(options) {
526
+ this.ensureInitialized();
527
+ const key = this.buildKey(options.toolId, options.sessionId, options.userId, options.context);
528
+ const exists = await this.storage.exists(key);
529
+ if (exists) {
530
+ await this.storage.delete(key);
531
+ return true;
532
+ }
533
+ return false;
534
+ }
535
+ async isApproved(toolId, sessionId, userId, context) {
536
+ this.ensureInitialized();
537
+ if (context) {
538
+ const contextKey = this.buildKey(toolId, sessionId, userId, context);
539
+ const contextValue = await this.storage.get(contextKey);
540
+ const contextApproval = this.parseRecord(contextValue);
541
+ if (contextApproval && contextApproval.state === "approved" /* APPROVED */ && !this.isExpired(contextApproval)) {
542
+ return true;
543
+ }
544
+ }
545
+ const sessionKey = this.buildKey(toolId, sessionId);
546
+ const sessionValue = await this.storage.get(sessionKey);
547
+ const sessionApproval = this.parseRecord(sessionValue);
548
+ if (sessionApproval && sessionApproval.state === "approved" /* APPROVED */ && !this.isExpired(sessionApproval)) {
549
+ return true;
550
+ }
551
+ if (userId) {
552
+ const userKey = this.buildKey(toolId, void 0, userId);
553
+ const userValue = await this.storage.get(userKey);
554
+ const userApproval = this.parseRecord(userValue);
555
+ if (userApproval && userApproval.state === "approved" /* APPROVED */ && !this.isExpired(userApproval)) {
556
+ return true;
557
+ }
558
+ }
559
+ return false;
560
+ }
561
+ async clearSessionApprovals(sessionId) {
562
+ this.ensureInitialized();
563
+ const escapedSessionId = escapePattern(sessionId);
564
+ const pattern = `*:session:${escapedSessionId}*`;
565
+ const keys = await this.storage.keys(pattern);
566
+ if (keys.length === 0) {
567
+ return 0;
568
+ }
569
+ return await this.storage.mdelete(keys);
570
+ }
571
+ async clearExpiredApprovals() {
572
+ this.ensureInitialized();
573
+ const now = Date.now();
574
+ const keys = await this.storage.keys("*");
575
+ const values = await this.storage.mget(keys);
576
+ const keysToDelete = [];
577
+ for (let i = 0; i < keys.length; i++) {
578
+ const approval = this.parseRecord(values[i]);
579
+ if (approval && approval.expiresAt && approval.expiresAt <= now) {
580
+ keysToDelete.push(keys[i]);
581
+ }
582
+ }
583
+ if (keysToDelete.length > 0) {
584
+ return await this.storage.mdelete(keysToDelete);
585
+ }
586
+ return 0;
587
+ }
588
+ async getStats() {
589
+ this.ensureInitialized();
590
+ const byScope = {
591
+ ["session" /* SESSION */]: 0,
592
+ ["user" /* USER */]: 0,
593
+ ["time_limited" /* TIME_LIMITED */]: 0,
594
+ ["tool_specific" /* TOOL_SPECIFIC */]: 0,
595
+ ["context_specific" /* CONTEXT_SPECIFIC */]: 0
596
+ };
597
+ const byState = {
598
+ ["pending" /* PENDING */]: 0,
599
+ ["approved" /* APPROVED */]: 0,
600
+ ["denied" /* DENIED */]: 0,
601
+ ["expired" /* EXPIRED */]: 0
602
+ };
603
+ const keys = await this.storage.keys("*");
604
+ const values = await this.storage.mget(keys);
605
+ let total = 0;
606
+ for (const value of values) {
607
+ const approval = this.parseRecord(value);
608
+ if (approval) {
609
+ total++;
610
+ byScope[approval.scope]++;
611
+ byState[approval.state]++;
612
+ }
613
+ }
614
+ return {
615
+ totalApprovals: total,
616
+ byScope,
617
+ byState
618
+ };
619
+ }
620
+ async close() {
621
+ if (this.cleanupInterval) {
622
+ clearInterval(this.cleanupInterval);
623
+ this.cleanupInterval = void 0;
624
+ }
625
+ if (this.ownedStorage && this.storage) {
626
+ await this.storage.root.disconnect();
627
+ }
628
+ this.initialized = false;
629
+ }
630
+ };
631
+ ApprovalStorageStore = __decorateClass([
632
+ Provider({
633
+ name: "provider:approval:store:storage",
634
+ description: "Storage-backed approval store (supports Memory, Redis, Vercel KV, Upstash)",
635
+ scope: ProviderScope.GLOBAL
636
+ })
637
+ ], ApprovalStorageStore);
638
+ function createApprovalMemoryStore(options = {}) {
639
+ const memoryStorage = createMemoryStorage();
640
+ return new ApprovalStorageStore({
641
+ ...options,
642
+ storageInstance: memoryStorage
643
+ });
644
+ }
645
+
646
+ // plugins/plugin-approval/src/services/approval.service.ts
647
+ import { Provider as Provider2, ProviderScope as ProviderScope2 } from "@frontmcp/sdk";
648
+ var ApprovalService = class {
649
+ constructor(store, sessionId, userId) {
650
+ this.store = store;
651
+ this.sessionId = sessionId;
652
+ this.userId = userId;
653
+ }
654
+ // ─────────────────────────────────────────────────────────────────────────────
655
+ // Query Methods
656
+ // ─────────────────────────────────────────────────────────────────────────────
657
+ /**
658
+ * Check if a tool is approved for current session/user.
659
+ */
660
+ async isApproved(toolId, context) {
661
+ return this.store.isApproved(toolId, this.sessionId, this.userId, context);
662
+ }
663
+ /**
664
+ * Get approval record for a tool.
665
+ */
666
+ async getApproval(toolId) {
667
+ return this.store.getApproval(toolId, this.sessionId, this.userId);
668
+ }
669
+ /**
670
+ * Get all approvals for current session.
671
+ */
672
+ async getSessionApprovals() {
673
+ return this.store.queryApprovals({
674
+ sessionId: this.sessionId,
675
+ states: ["approved" /* APPROVED */],
676
+ includeExpired: false
677
+ });
678
+ }
679
+ /**
680
+ * Get all approvals for current user (across sessions).
681
+ */
682
+ async getUserApprovals() {
683
+ if (!this.userId) return [];
684
+ return this.store.queryApprovals({
685
+ userId: this.userId,
686
+ scope: "user" /* USER */,
687
+ states: ["approved" /* APPROVED */],
688
+ includeExpired: false
689
+ });
690
+ }
691
+ /**
692
+ * Query approvals with custom filters.
693
+ */
694
+ async queryApprovals(query) {
695
+ return this.store.queryApprovals({
696
+ ...query,
697
+ sessionId: query.sessionId ?? this.sessionId,
698
+ userId: query.userId ?? this.userId
699
+ });
700
+ }
701
+ // ─────────────────────────────────────────────────────────────────────────────
702
+ // Grant Methods
703
+ // ─────────────────────────────────────────────────────────────────────────────
704
+ /**
705
+ * Grant session-scoped approval for a tool.
706
+ */
707
+ async grantSessionApproval(toolId, options = {}) {
708
+ return this.store.grantApproval({
709
+ toolId,
710
+ scope: "session" /* SESSION */,
711
+ sessionId: this.sessionId,
712
+ grantedBy: options.grantedBy ?? "policy",
713
+ reason: options.reason,
714
+ metadata: options.metadata
715
+ });
716
+ }
717
+ /**
718
+ * Grant user-scoped approval for a tool.
719
+ */
720
+ async grantUserApproval(toolId, options = {}) {
721
+ if (!this.userId) {
722
+ throw new Error("Cannot grant user approval without userId");
723
+ }
724
+ return this.store.grantApproval({
725
+ toolId,
726
+ scope: "user" /* USER */,
727
+ userId: this.userId,
728
+ grantedBy: options.grantedBy ?? "policy",
729
+ reason: options.reason,
730
+ metadata: options.metadata
731
+ });
732
+ }
733
+ /**
734
+ * Grant time-limited approval for a tool.
735
+ */
736
+ async grantTimeLimitedApproval(toolId, ttlMs, options = {}) {
737
+ return this.store.grantApproval({
738
+ toolId,
739
+ scope: "time_limited" /* TIME_LIMITED */,
740
+ ttlMs,
741
+ sessionId: this.sessionId,
742
+ userId: this.userId,
743
+ grantedBy: options.grantedBy ?? "policy",
744
+ reason: options.reason,
745
+ metadata: options.metadata
746
+ });
747
+ }
748
+ /**
749
+ * Grant context-specific approval for a tool.
750
+ */
751
+ async grantContextApproval(toolId, context, options = {}) {
752
+ return this.store.grantApproval({
753
+ toolId,
754
+ scope: "context_specific" /* CONTEXT_SPECIFIC */,
755
+ context,
756
+ sessionId: this.sessionId,
757
+ userId: this.userId,
758
+ grantedBy: options.grantedBy ?? "policy",
759
+ reason: options.reason,
760
+ metadata: options.metadata
761
+ });
762
+ }
763
+ // ─────────────────────────────────────────────────────────────────────────────
764
+ // Revoke Methods
765
+ // ─────────────────────────────────────────────────────────────────────────────
766
+ /**
767
+ * Revoke approval for a tool.
768
+ */
769
+ async revokeApproval(toolId, options = {}) {
770
+ return this.store.revokeApproval({
771
+ toolId,
772
+ sessionId: this.sessionId,
773
+ userId: this.userId,
774
+ revokedBy: options.revokedBy ?? "policy",
775
+ reason: options.reason
776
+ });
777
+ }
778
+ /**
779
+ * Clear all session approvals.
780
+ */
781
+ async clearSessionApprovals() {
782
+ return this.store.clearSessionApprovals(this.sessionId);
783
+ }
784
+ };
785
+ ApprovalService = __decorateClass([
786
+ Provider2({
787
+ name: "provider:approval:service",
788
+ description: "Service for managing tool approvals",
789
+ scope: ProviderScope2.CONTEXT
790
+ })
791
+ ], ApprovalService);
792
+ function createApprovalService(store, sessionId, userId) {
793
+ return new ApprovalService(store, sessionId, userId);
794
+ }
795
+
796
+ // plugins/plugin-approval/src/services/challenge.service.ts
797
+ import { Provider as Provider3, ProviderScope as ProviderScope3 } from "@frontmcp/sdk";
798
+ import {
799
+ generatePkcePair,
800
+ generateCodeChallenge,
801
+ createStorage as createStorage2,
802
+ createMemoryStorage as createMemoryStorage2
803
+ } from "@frontmcp/utils";
804
+ var ChallengeService = class {
805
+ storage;
806
+ options;
807
+ initialized = false;
808
+ ownedStorage = false;
809
+ constructor(options = {}) {
810
+ this.options = {
811
+ storage: options.storage ?? { type: "auto" },
812
+ storageInstance: options.storageInstance,
813
+ namespace: options.namespace ?? "approval:challenge",
814
+ defaultTtlSeconds: options.defaultTtlSeconds ?? 300
815
+ };
816
+ }
817
+ /**
818
+ * Initialize the service.
819
+ */
820
+ async initialize() {
821
+ if (this.initialized) return;
822
+ if (this.options.storageInstance) {
823
+ this.storage = this.options.storageInstance.namespace(this.options.namespace);
824
+ this.ownedStorage = false;
825
+ } else {
826
+ const rootStorage = await createStorage2(this.options.storage);
827
+ this.storage = rootStorage.namespace(this.options.namespace);
828
+ this.ownedStorage = true;
829
+ }
830
+ this.initialized = true;
831
+ }
832
+ ensureInitialized() {
833
+ if (!this.initialized) {
834
+ throw new Error("ChallengeService not initialized. Call initialize() first.");
835
+ }
836
+ }
837
+ /**
838
+ * Create a new PKCE challenge for a tool approval request.
839
+ *
840
+ * @returns Object containing code_verifier (keep secret) and code_challenge (send to webhook)
841
+ */
842
+ async createChallenge(options) {
843
+ this.ensureInitialized();
844
+ const { codeVerifier, codeChallenge } = generatePkcePair();
845
+ const now = Date.now();
846
+ const ttlSeconds = options.ttlSeconds ?? this.options.defaultTtlSeconds;
847
+ const expiresAt = now + ttlSeconds * 1e3;
848
+ const record = {
849
+ toolId: options.toolId,
850
+ sessionId: options.sessionId,
851
+ userId: options.userId,
852
+ requestedScope: options.requestedScope,
853
+ requestInfo: options.requestInfo,
854
+ createdAt: now,
855
+ expiresAt,
856
+ webhookSent: false
857
+ };
858
+ await this.storage.set(codeChallenge, JSON.stringify(record), { ttlSeconds });
859
+ return { codeVerifier, codeChallenge, expiresAt };
860
+ }
861
+ /**
862
+ * Verify a code verifier and retrieve the challenge record.
863
+ *
864
+ * @throws ChallengeValidationError if verification fails
865
+ */
866
+ async verifyAndConsume(codeVerifier) {
867
+ this.ensureInitialized();
868
+ const { codeChallenge } = generatePkcePairFromVerifier(codeVerifier);
869
+ const recordJson = await this.storage.get(codeChallenge);
870
+ if (!recordJson) {
871
+ throw new ChallengeValidationError("not_found", "Invalid or expired challenge");
872
+ }
873
+ const record = JSON.parse(recordJson);
874
+ if (Date.now() > record.expiresAt) {
875
+ await this.storage.delete(codeChallenge);
876
+ throw new ChallengeValidationError("expired", "Challenge expired");
877
+ }
878
+ await this.storage.delete(codeChallenge);
879
+ return record;
880
+ }
881
+ /**
882
+ * Mark a challenge as having been sent to webhook.
883
+ */
884
+ async markWebhookSent(codeChallenge) {
885
+ this.ensureInitialized();
886
+ const recordJson = await this.storage.get(codeChallenge);
887
+ if (!recordJson) return false;
888
+ const record = JSON.parse(recordJson);
889
+ record.webhookSent = true;
890
+ const remainingMs = record.expiresAt - Date.now();
891
+ if (remainingMs <= 0) {
892
+ await this.storage.delete(codeChallenge);
893
+ return false;
894
+ }
895
+ const ttlSeconds = Math.ceil(remainingMs / 1e3);
896
+ await this.storage.set(codeChallenge, JSON.stringify(record), { ttlSeconds });
897
+ return true;
898
+ }
899
+ /**
900
+ * Get a challenge record without consuming it.
901
+ */
902
+ async getChallenge(codeChallenge) {
903
+ this.ensureInitialized();
904
+ const recordJson = await this.storage.get(codeChallenge);
905
+ if (!recordJson) return null;
906
+ const record = JSON.parse(recordJson);
907
+ if (Date.now() > record.expiresAt) {
908
+ await this.storage.delete(codeChallenge);
909
+ return null;
910
+ }
911
+ return record;
912
+ }
913
+ /**
914
+ * Delete a challenge.
915
+ */
916
+ async deleteChallenge(codeChallenge) {
917
+ this.ensureInitialized();
918
+ const exists = await this.storage.exists(codeChallenge);
919
+ if (exists) {
920
+ await this.storage.delete(codeChallenge);
921
+ return true;
922
+ }
923
+ return false;
924
+ }
925
+ /**
926
+ * Close the service.
927
+ */
928
+ async close() {
929
+ if (this.ownedStorage && this.storage) {
930
+ await this.storage.root.disconnect();
931
+ }
932
+ this.initialized = false;
933
+ }
934
+ };
935
+ ChallengeService = __decorateClass([
936
+ Provider3({
937
+ name: "provider:approval:challenge-service",
938
+ description: "PKCE challenge service for webhook approval flows",
939
+ scope: ProviderScope3.GLOBAL
940
+ })
941
+ ], ChallengeService);
942
+ function generatePkcePairFromVerifier(codeVerifier) {
943
+ return {
944
+ codeVerifier,
945
+ codeChallenge: generateCodeChallenge(codeVerifier)
946
+ };
947
+ }
948
+ function createMemoryChallengeService(options = {}) {
949
+ const memoryStorage = createMemoryStorage2();
950
+ return new ChallengeService({
951
+ ...options,
952
+ storageInstance: memoryStorage
953
+ });
954
+ }
955
+
956
+ // plugins/plugin-approval/src/approval.symbols.ts
957
+ var ApprovalStoreToken = /* @__PURE__ */ Symbol.for(
958
+ "plugin:approval:store"
959
+ );
960
+ var ApprovalServiceToken = /* @__PURE__ */ Symbol.for(
961
+ "plugin:approval:service"
962
+ );
963
+ var ChallengeServiceToken = /* @__PURE__ */ Symbol.for(
964
+ "plugin:approval:challenge-service"
965
+ );
966
+
967
+ // plugins/plugin-approval/src/hooks/approval-check.hook.ts
968
+ import { DynamicPlugin, Plugin, ToolHook } from "@frontmcp/sdk";
969
+ var ApprovalCheckPlugin = class extends DynamicPlugin {
970
+ async checkApproval(flowCtx) {
971
+ const { tool, toolContext } = flowCtx.state;
972
+ if (!tool || !toolContext) return;
973
+ const metadata = tool.metadata;
974
+ const approvalConfig = this.resolveApprovalConfig(
975
+ metadata["approval"]
976
+ );
977
+ if (!approvalConfig.required) {
978
+ return;
979
+ }
980
+ if (approvalConfig.skipApproval) {
981
+ return;
982
+ }
983
+ const ctx = toolContext.tryGetContext?.();
984
+ const sessionId = ctx?.sessionId ?? "unknown";
985
+ const userId = this.getStringExtra(ctx?.authInfo?.extra, "userId") ?? this.getStringExtra(ctx?.authInfo?.extra, "sub") ?? ctx?.authInfo?.clientId;
986
+ const currentContext = this.getCurrentContext(flowCtx);
987
+ if (this.isPreApprovedContext(approvalConfig, currentContext)) {
988
+ return;
989
+ }
990
+ const approvalStore = this.get(ApprovalStoreToken);
991
+ const approval = await approvalStore.getApproval(tool.fullName, sessionId, userId);
992
+ if (approvalConfig.alwaysPrompt) {
993
+ await this.handleApprovalRequired(flowCtx, approvalConfig, approval);
994
+ return;
995
+ }
996
+ if (approval?.state === "approved" /* APPROVED */) {
997
+ if (!this.isExpired(approval)) {
998
+ return;
999
+ }
1000
+ }
1001
+ if (approval?.state === "denied" /* DENIED */) {
1002
+ throw new ApprovalRequiredError({
1003
+ toolId: tool.fullName,
1004
+ state: "denied",
1005
+ message: `Tool "${tool.fullName}" execution denied.`
1006
+ });
1007
+ }
1008
+ await this.handleApprovalRequired(flowCtx, approvalConfig, approval);
1009
+ }
1010
+ resolveApprovalConfig(config) {
1011
+ if (config === true) {
1012
+ return { required: true, defaultScope: "session" /* SESSION */ };
1013
+ }
1014
+ if (config === false || config === void 0) {
1015
+ return { required: false };
1016
+ }
1017
+ return {
1018
+ ...config,
1019
+ required: config.required ?? true,
1020
+ defaultScope: config.defaultScope ?? "session" /* SESSION */
1021
+ };
1022
+ }
1023
+ isExpired(approval) {
1024
+ if (!approval.expiresAt) return false;
1025
+ return Date.now() > approval.expiresAt;
1026
+ }
1027
+ getCurrentContext(flowCtx) {
1028
+ const { toolContext } = flowCtx.state;
1029
+ const ctx = toolContext?.tryGetContext?.();
1030
+ const inputContext = toolContext?.input?.["context"];
1031
+ const contextFromInput = this.isApprovalContext(inputContext) ? inputContext : void 0;
1032
+ const sessionContext = ctx?.authInfo?.extra?.["approvalContext"];
1033
+ const contextFromSession = this.isApprovalContext(sessionContext) ? sessionContext : void 0;
1034
+ return contextFromInput ?? contextFromSession;
1035
+ }
1036
+ isApprovalContext(value) {
1037
+ return typeof value === "object" && value !== null && "type" in value && "identifier" in value && typeof value.type === "string" && typeof value.identifier === "string";
1038
+ }
1039
+ getStringExtra(extra, key) {
1040
+ if (!extra) return void 0;
1041
+ const value = extra[key];
1042
+ return typeof value === "string" ? value : void 0;
1043
+ }
1044
+ isPreApprovedContext(config, currentContext) {
1045
+ if (!currentContext || !config.preApprovedContexts?.length) {
1046
+ return false;
1047
+ }
1048
+ return config.preApprovedContexts.some(
1049
+ (preApproved) => preApproved.type === currentContext.type && preApproved.identifier === currentContext.identifier
1050
+ );
1051
+ }
1052
+ async handleApprovalRequired(flowCtx, config, existingApproval) {
1053
+ const { tool } = flowCtx.state;
1054
+ const message = config.approvalMessage ?? `Tool "${tool?.fullName}" requires approval to execute. Allow?`;
1055
+ const isExpiredApproval = existingApproval ? this.isExpired(existingApproval) : false;
1056
+ throw new ApprovalRequiredError({
1057
+ toolId: tool?.fullName ?? "unknown",
1058
+ state: isExpiredApproval ? "expired" : "pending",
1059
+ message,
1060
+ approvalOptions: {
1061
+ allowedScopes: config.allowedScopes,
1062
+ defaultScope: config.defaultScope,
1063
+ maxTtlMs: config.maxTtlMs,
1064
+ category: config.category,
1065
+ riskLevel: config.riskLevel
1066
+ }
1067
+ });
1068
+ }
1069
+ };
1070
+ __decorateClass([
1071
+ ToolHook.Will("execute", { priority: 100 })
1072
+ ], ApprovalCheckPlugin.prototype, "checkApproval", 1);
1073
+ ApprovalCheckPlugin = __decorateClass([
1074
+ Plugin({
1075
+ name: "approval:check",
1076
+ description: "Checks tool approval state before execution"
1077
+ })
1078
+ ], ApprovalCheckPlugin);
1079
+
1080
+ // plugins/plugin-approval/src/approval.plugin.ts
1081
+ var ApprovalPlugin = class extends DynamicPlugin2 {
1082
+ options;
1083
+ constructor(options = {}) {
1084
+ super();
1085
+ this.options = {
1086
+ ...ApprovalPlugin.defaultOptions,
1087
+ ...options
1088
+ };
1089
+ }
1090
+ /**
1091
+ * Get plugin metadata including nested plugins.
1092
+ */
1093
+ static getPluginMetadata(_options) {
1094
+ return { plugins: [ApprovalCheckPlugin] };
1095
+ }
1096
+ };
1097
+ __publicField(ApprovalPlugin, "defaultOptions", {
1098
+ namespace: "approval",
1099
+ mode: "recheck",
1100
+ enableAudit: true,
1101
+ maxDelegationDepth: 3,
1102
+ cleanupIntervalSeconds: 60
1103
+ });
1104
+ /**
1105
+ * Dynamic providers based on plugin options.
1106
+ */
1107
+ __publicField(ApprovalPlugin, "dynamicProviders", (options) => {
1108
+ const providers = [];
1109
+ const config = {
1110
+ ...ApprovalPlugin.defaultOptions,
1111
+ ...options
1112
+ };
1113
+ providers.push({
1114
+ name: "approval:store",
1115
+ provide: ApprovalStoreToken,
1116
+ inject: () => [],
1117
+ useFactory: async () => {
1118
+ const store = new ApprovalStorageStore({
1119
+ storage: config.storage,
1120
+ storageInstance: config.storageInstance,
1121
+ namespace: config.namespace,
1122
+ cleanupIntervalSeconds: config.cleanupIntervalSeconds
1123
+ });
1124
+ await store.initialize();
1125
+ return store;
1126
+ }
1127
+ });
1128
+ if (config.mode === "webhook") {
1129
+ providers.push({
1130
+ name: "approval:challenge-service",
1131
+ provide: ChallengeServiceToken,
1132
+ inject: () => [],
1133
+ useFactory: async () => {
1134
+ const service = new ChallengeService({
1135
+ storage: config.storage,
1136
+ storageInstance: config.storageInstance,
1137
+ namespace: `${config.namespace}:challenge`,
1138
+ defaultTtlSeconds: config.webhook?.challengeTtl ?? 300
1139
+ });
1140
+ await service.initialize();
1141
+ return service;
1142
+ }
1143
+ });
1144
+ }
1145
+ providers.push({
1146
+ name: "approval:service",
1147
+ provide: ApprovalServiceToken,
1148
+ scope: ProviderScope4.CONTEXT,
1149
+ inject: () => [ApprovalStoreToken, FRONTMCP_CONTEXT],
1150
+ useFactory: (store, ctx) => {
1151
+ const userId = ctx.authInfo?.extra?.["userId"] ?? ctx.authInfo?.extra?.["sub"] ?? ctx.authInfo?.clientId;
1152
+ return createApprovalService(store, ctx.sessionId, userId);
1153
+ }
1154
+ });
1155
+ return providers;
1156
+ });
1157
+ ApprovalPlugin = __decorateClass([
1158
+ Plugin2({
1159
+ name: "approval",
1160
+ description: "Tool authorization workflow with PKCE webhook security",
1161
+ contextExtensions: [
1162
+ {
1163
+ property: "approval",
1164
+ token: ApprovalServiceToken,
1165
+ errorMessage: "ApprovalPlugin is not installed. Add ApprovalPlugin.init() to your plugins array."
1166
+ }
1167
+ ]
1168
+ })
1169
+ ], ApprovalPlugin);
1170
+
1171
+ // plugins/plugin-approval/src/approval.context-extension.ts
1172
+ var installed = false;
1173
+ function installApprovalContextExtension() {
1174
+ if (installed) return;
1175
+ const { ExecutionContextBase } = __require("@frontmcp/sdk");
1176
+ Object.defineProperty(ExecutionContextBase.prototype, "approval", {
1177
+ get: function() {
1178
+ return this.get(ApprovalServiceToken);
1179
+ },
1180
+ configurable: true,
1181
+ enumerable: false
1182
+ });
1183
+ installed = true;
1184
+ }
1185
+ export {
1186
+ ApprovalCheckPlugin,
1187
+ ApprovalError,
1188
+ ApprovalExpiredError,
1189
+ ApprovalOperationError,
1190
+ ApprovalPlugin,
1191
+ ApprovalPlugin as ApprovalPluginClass,
1192
+ ApprovalRequiredError,
1193
+ ApprovalScope,
1194
+ ApprovalScopeNotAllowedError,
1195
+ ApprovalService,
1196
+ ApprovalServiceToken,
1197
+ ApprovalState,
1198
+ ApprovalStorageStore,
1199
+ ApprovalStoreToken,
1200
+ ChallengeService,
1201
+ ChallengeServiceToken,
1202
+ ChallengeValidationError,
1203
+ adminGrantor,
1204
+ adminRevoker,
1205
+ agentGrantor,
1206
+ apiGrantor,
1207
+ createApprovalMemoryStore,
1208
+ createApprovalService,
1209
+ createMemoryChallengeService,
1210
+ customGrantor,
1211
+ expiryRevoker,
1212
+ installApprovalContextExtension,
1213
+ isApiGrantor,
1214
+ isAutoGrantor,
1215
+ isDelegatedGrantor,
1216
+ isGrantorSource,
1217
+ isHumanGrantor,
1218
+ normalizeGrantor,
1219
+ normalizeRevoker,
1220
+ oauthGrantor,
1221
+ policyGrantor,
1222
+ policyRevoker,
1223
+ sessionEndRevoker,
1224
+ systemGrantor,
1225
+ testGrantor,
1226
+ userGrantor,
1227
+ userRevoker
1228
+ };