@auth0/ai 1.0.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 (108) hide show
  1. package/LICENSE +176 -0
  2. package/README.md +44 -0
  3. package/dist/cjs/TokenResponse.js +3 -0
  4. package/dist/cjs/TokenResponse.js.map +1 -0
  5. package/dist/cjs/authorizers/ciba/CIBAAuthorizationRequest.js +3 -0
  6. package/dist/cjs/authorizers/ciba/CIBAAuthorizationRequest.js.map +1 -0
  7. package/dist/cjs/authorizers/ciba/CIBAAuthorizerBase.js +201 -0
  8. package/dist/cjs/authorizers/ciba/CIBAAuthorizerBase.js.map +1 -0
  9. package/dist/cjs/authorizers/ciba/CIBAAuthorizerParams.js +3 -0
  10. package/dist/cjs/authorizers/ciba/CIBAAuthorizerParams.js.map +1 -0
  11. package/dist/cjs/authorizers/ciba/asyncLocalStorage.js +14 -0
  12. package/dist/cjs/authorizers/ciba/asyncLocalStorage.js.map +1 -0
  13. package/dist/cjs/authorizers/ciba/index.js +9 -0
  14. package/dist/cjs/authorizers/ciba/index.js.map +1 -0
  15. package/dist/cjs/authorizers/device-authorizer.js +130 -0
  16. package/dist/cjs/authorizers/device-authorizer.js.map +1 -0
  17. package/dist/cjs/authorizers/federated-connections/asyncLocalStorage.js +14 -0
  18. package/dist/cjs/authorizers/federated-connections/asyncLocalStorage.js.map +1 -0
  19. package/dist/cjs/authorizers/federated-connections/index.js +129 -0
  20. package/dist/cjs/authorizers/federated-connections/index.js.map +1 -0
  21. package/dist/cjs/authorizers/fga/fga-client.js +30 -0
  22. package/dist/cjs/authorizers/fga/fga-client.js.map +1 -0
  23. package/dist/cjs/authorizers/fga/fga-filter.js +94 -0
  24. package/dist/cjs/authorizers/fga/fga-filter.js.map +1 -0
  25. package/dist/cjs/authorizers/fga/index.js +37 -0
  26. package/dist/cjs/authorizers/fga/index.js.map +1 -0
  27. package/dist/cjs/authorizers/index.js +3 -0
  28. package/dist/cjs/authorizers/index.js.map +1 -0
  29. package/dist/cjs/credentials.js +3 -0
  30. package/dist/cjs/credentials.js.map +1 -0
  31. package/dist/cjs/errors.js +46 -0
  32. package/dist/cjs/errors.js.map +1 -0
  33. package/dist/cjs/index.js +22 -0
  34. package/dist/cjs/index.js.map +1 -0
  35. package/dist/cjs/interrupts/Auth0Interrupt.js +46 -0
  36. package/dist/cjs/interrupts/Auth0Interrupt.js.map +1 -0
  37. package/dist/cjs/interrupts/CIBAInterrupts.js +89 -0
  38. package/dist/cjs/interrupts/CIBAInterrupts.js.map +1 -0
  39. package/dist/cjs/interrupts/FederatedConnectionInterrupt.js +31 -0
  40. package/dist/cjs/interrupts/FederatedConnectionInterrupt.js.map +1 -0
  41. package/dist/cjs/interrupts/index.js +17 -0
  42. package/dist/cjs/interrupts/index.js.map +1 -0
  43. package/dist/cjs/parameters.js +13 -0
  44. package/dist/cjs/parameters.js.map +1 -0
  45. package/dist/esm/TokenResponse.d.ts +15 -0
  46. package/dist/esm/TokenResponse.js +2 -0
  47. package/dist/esm/TokenResponse.js.map +1 -0
  48. package/dist/esm/authorizers/ciba/CIBAAuthorizationRequest.d.ts +20 -0
  49. package/dist/esm/authorizers/ciba/CIBAAuthorizationRequest.js +2 -0
  50. package/dist/esm/authorizers/ciba/CIBAAuthorizationRequest.js.map +1 -0
  51. package/dist/esm/authorizers/ciba/CIBAAuthorizerBase.d.ts +31 -0
  52. package/dist/esm/authorizers/ciba/CIBAAuthorizerBase.js +197 -0
  53. package/dist/esm/authorizers/ciba/CIBAAuthorizerBase.js.map +1 -0
  54. package/dist/esm/authorizers/ciba/CIBAAuthorizerParams.d.ts +66 -0
  55. package/dist/esm/authorizers/ciba/CIBAAuthorizerParams.js +2 -0
  56. package/dist/esm/authorizers/ciba/CIBAAuthorizerParams.js.map +1 -0
  57. package/dist/esm/authorizers/ciba/asyncLocalStorage.d.ts +16 -0
  58. package/dist/esm/authorizers/ciba/asyncLocalStorage.js +10 -0
  59. package/dist/esm/authorizers/ciba/asyncLocalStorage.js.map +1 -0
  60. package/dist/esm/authorizers/ciba/index.d.ts +4 -0
  61. package/dist/esm/authorizers/ciba/index.js +3 -0
  62. package/dist/esm/authorizers/ciba/index.js.map +1 -0
  63. package/dist/esm/authorizers/device-authorizer.d.ts +15 -0
  64. package/dist/esm/authorizers/device-authorizer.js +90 -0
  65. package/dist/esm/authorizers/device-authorizer.js.map +1 -0
  66. package/dist/esm/authorizers/federated-connections/asyncLocalStorage.d.ts +26 -0
  67. package/dist/esm/authorizers/federated-connections/asyncLocalStorage.js +10 -0
  68. package/dist/esm/authorizers/federated-connections/asyncLocalStorage.js.map +1 -0
  69. package/dist/esm/authorizers/federated-connections/index.d.ts +38 -0
  70. package/dist/esm/authorizers/federated-connections/index.js +123 -0
  71. package/dist/esm/authorizers/federated-connections/index.js.map +1 -0
  72. package/dist/esm/authorizers/fga/fga-client.d.ts +16 -0
  73. package/dist/esm/authorizers/fga/fga-client.js +27 -0
  74. package/dist/esm/authorizers/fga/fga-client.js.map +1 -0
  75. package/dist/esm/authorizers/fga/fga-filter.d.ts +63 -0
  76. package/dist/esm/authorizers/fga/fga-filter.js +90 -0
  77. package/dist/esm/authorizers/fga/fga-filter.js.map +1 -0
  78. package/dist/esm/authorizers/fga/index.d.ts +21 -0
  79. package/dist/esm/authorizers/fga/index.js +33 -0
  80. package/dist/esm/authorizers/fga/index.js.map +1 -0
  81. package/dist/esm/authorizers/index.d.ts +13 -0
  82. package/dist/esm/authorizers/index.js +2 -0
  83. package/dist/esm/authorizers/index.js.map +1 -0
  84. package/dist/esm/credentials.d.ts +10 -0
  85. package/dist/esm/credentials.js +2 -0
  86. package/dist/esm/credentials.js.map +1 -0
  87. package/dist/esm/errors.d.ts +19 -0
  88. package/dist/esm/errors.js +37 -0
  89. package/dist/esm/errors.js.map +1 -0
  90. package/dist/esm/index.d.ts +9 -0
  91. package/dist/esm/index.js +6 -0
  92. package/dist/esm/index.js.map +1 -0
  93. package/dist/esm/interrupts/Auth0Interrupt.d.ts +37 -0
  94. package/dist/esm/interrupts/Auth0Interrupt.js +42 -0
  95. package/dist/esm/interrupts/Auth0Interrupt.js.map +1 -0
  96. package/dist/esm/interrupts/CIBAInterrupts.d.ts +62 -0
  97. package/dist/esm/interrupts/CIBAInterrupts.js +79 -0
  98. package/dist/esm/interrupts/CIBAInterrupts.js.map +1 -0
  99. package/dist/esm/interrupts/FederatedConnectionInterrupt.d.ts +33 -0
  100. package/dist/esm/interrupts/FederatedConnectionInterrupt.js +26 -0
  101. package/dist/esm/interrupts/FederatedConnectionInterrupt.js.map +1 -0
  102. package/dist/esm/interrupts/index.d.ts +4 -0
  103. package/dist/esm/interrupts/index.js +4 -0
  104. package/dist/esm/interrupts/index.js.map +1 -0
  105. package/dist/esm/parameters.d.ts +3 -0
  106. package/dist/esm/parameters.js +9 -0
  107. package/dist/esm/parameters.js.map +1 -0
  108. package/package.json +76 -0
@@ -0,0 +1,16 @@
1
+ import { AsyncLocalStorage } from "async_hooks";
2
+ import { Credentials } from "../../credentials";
3
+ export type AsyncStorageValue<TContext> = {
4
+ credentials?: Credentials;
5
+ /**
6
+ * The tool execution context.
7
+ */
8
+ context: TContext;
9
+ /**
10
+ * The tool execution arguments.
11
+ */
12
+ args: any[];
13
+ };
14
+ export declare const asyncLocalStorage: AsyncLocalStorage<AsyncStorageValue<any>>;
15
+ export declare const getCIBACredentials: () => Credentials | undefined;
16
+ //# sourceMappingURL=asyncLocalStorage.d.ts.map
@@ -0,0 +1,10 @@
1
+ import { AsyncLocalStorage } from "async_hooks";
2
+ export const asyncLocalStorage = new AsyncLocalStorage();
3
+ export const getCIBACredentials = () => {
4
+ const t = asyncLocalStorage.getStore();
5
+ if (typeof t === "undefined") {
6
+ throw new Error("The tool must be wrapped with the withCIBA function.");
7
+ }
8
+ return t.credentials;
9
+ };
10
+ //# sourceMappingURL=asyncLocalStorage.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"asyncLocalStorage.js","sourceRoot":"","sources":["../../../../src/authorizers/ciba/asyncLocalStorage.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAkBhD,MAAM,CAAC,MAAM,iBAAiB,GAAG,IAAI,iBAAiB,EAEnD,CAAC;AAEJ,MAAM,CAAC,MAAM,kBAAkB,GAAG,GAAG,EAAE;IACrC,MAAM,CAAC,GAAG,iBAAiB,CAAC,QAAQ,EAAE,CAAC;IACvC,IAAI,OAAO,CAAC,KAAK,WAAW,EAAE,CAAC;QAC7B,MAAM,IAAI,KAAK,CAAC,sDAAsD,CAAC,CAAC;IAC1E,CAAC;IACD,OAAO,CAAC,CAAC,WAAW,CAAC;AACvB,CAAC,CAAC","sourcesContent":["import { AsyncLocalStorage } from \"async_hooks\";\n\nimport { Credentials } from \"../../credentials\";\n\nexport type AsyncStorageValue<TContext> = {\n credentials?: Credentials;\n\n /**\n * The tool execution context.\n */\n context: TContext;\n\n /**\n * The tool execution arguments.\n */\n args: any[];\n};\n\nexport const asyncLocalStorage = new AsyncLocalStorage<\n AsyncStorageValue<any>\n>();\n\nexport const getCIBACredentials = () => {\n const t = asyncLocalStorage.getStore();\n if (typeof t === \"undefined\") {\n throw new Error(\"The tool must be wrapped with the withCIBA function.\");\n }\n return t.credentials;\n};\n"]}
@@ -0,0 +1,4 @@
1
+ export { asyncLocalStorage, getCIBACredentials } from "./asyncLocalStorage";
2
+ export type { CIBAAuthorizationRequest } from "./CIBAAuthorizationRequest";
3
+ export { CIBAAuthorizerBase } from "./CIBAAuthorizerBase";
4
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,3 @@
1
+ export { asyncLocalStorage, getCIBACredentials } from "./asyncLocalStorage";
2
+ export { CIBAAuthorizerBase } from "./CIBAAuthorizerBase";
3
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/authorizers/ciba/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AAI5E,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC","sourcesContent":["export { asyncLocalStorage, getCIBACredentials } from \"./asyncLocalStorage\";\n\nexport type { CIBAAuthorizationRequest } from \"./CIBAAuthorizationRequest\";\n\nexport { CIBAAuthorizerBase } from \"./CIBAAuthorizerBase\";\n"]}
@@ -0,0 +1,15 @@
1
+ import { AuthorizerParams, AuthParams, ToolWithAuthHandler } from "./";
2
+ export type DeviceAuthorizerOptions = {
3
+ scope: string;
4
+ audience?: string;
5
+ };
6
+ export declare class DeviceAuthorizer {
7
+ domain: string;
8
+ clientId: string;
9
+ private constructor();
10
+ private _authorize;
11
+ static authorize(options: DeviceAuthorizerOptions, params?: AuthorizerParams): Promise<AuthParams>;
12
+ static create(params?: AuthorizerParams): DeviceFlowInstance;
13
+ }
14
+ export type DeviceFlowInstance = (options: DeviceAuthorizerOptions) => <I, O, C>(handler: ToolWithAuthHandler<I, O, C>, onError?: (error: Error) => Promise<O>) => (input: I, config?: C) => Promise<O>;
15
+ //# sourceMappingURL=device-authorizer.d.ts.map
@@ -0,0 +1,90 @@
1
+ import Enquirer from "enquirer";
2
+ import * as jose from "jose";
3
+ import open from "open";
4
+ import { discovery, initiateDeviceAuthorization, pollDeviceAuthorizationGrant, } from "openid-client";
5
+ import { AccessDeniedError, AuthorizationRequestExpiredError } from "../errors";
6
+ export class DeviceAuthorizer {
7
+ constructor(params) {
8
+ this.domain = (params === null || params === void 0 ? void 0 : params.domain) || process.env.AUTH0_DOMAIN;
9
+ this.clientId = (params === null || params === void 0 ? void 0 : params.clientId) || process.env.AUTH0_CLIENT_ID;
10
+ }
11
+ async _authorize(params) {
12
+ const config = await discovery(new URL(`https://${this.domain}`), this.clientId);
13
+ const handle = await initiateDeviceAuthorization(config, {
14
+ ...params,
15
+ });
16
+ const { verification_uri_complete, user_code, expires_in } = handle;
17
+ const enquirer = new Enquirer();
18
+ await enquirer.prompt({
19
+ type: "input",
20
+ name: "confirmation",
21
+ message: `Press Enter to open the browser to log in or press Ctrl+C to abort. You should see the following code: ${user_code}. It expires in ${expires_in % 60 === 0
22
+ ? `${expires_in / 60} minutes`
23
+ : `${expires_in} seconds`}.`,
24
+ });
25
+ await open(verification_uri_complete);
26
+ let tokens = null;
27
+ try {
28
+ tokens = await pollDeviceAuthorizationGrant(config, handle);
29
+ }
30
+ catch (err) {
31
+ switch (err.error) {
32
+ case "access_denied":
33
+ throw new AccessDeniedError(err.error_description);
34
+ case "expired_token":
35
+ throw new AuthorizationRequestExpiredError("Authorization request has expired");
36
+ default:
37
+ throw new Error(`Error: ${err.error}; Description: ${err.error_description}`);
38
+ }
39
+ }
40
+ if (!tokens) {
41
+ throw new Error("Failed to obtain tokens");
42
+ }
43
+ const credentials = {
44
+ accessToken: {
45
+ type: tokens.token_type || "bearer",
46
+ value: tokens.access_token,
47
+ },
48
+ };
49
+ if (tokens.id_token) {
50
+ credentials.idToken = {
51
+ type: tokens.token_type || "bearer",
52
+ value: tokens.id_token,
53
+ };
54
+ }
55
+ return credentials;
56
+ }
57
+ static async authorize(options, params) {
58
+ const authorizer = new DeviceAuthorizer(params);
59
+ const credentials = await authorizer._authorize(options);
60
+ let claims = {};
61
+ if (credentials.idToken) {
62
+ claims = jose.decodeJwt(credentials.idToken.value);
63
+ }
64
+ return { accessToken: credentials.accessToken.value, claims };
65
+ }
66
+ static create(params) {
67
+ const authorizer = new DeviceAuthorizer(params);
68
+ return (options) => {
69
+ return function deviceFlow(handler, onError) {
70
+ return async (input, config) => {
71
+ try {
72
+ const credentials = await authorizer._authorize(options);
73
+ let claims = {};
74
+ if (credentials.idToken) {
75
+ claims = jose.decodeJwt(credentials.idToken.value);
76
+ }
77
+ return handler({ accessToken: credentials.accessToken.value, claims }, input, config);
78
+ }
79
+ catch (e) {
80
+ if (typeof onError === "function") {
81
+ return onError(e);
82
+ }
83
+ return "Access denied.";
84
+ }
85
+ };
86
+ };
87
+ };
88
+ }
89
+ }
90
+ //# sourceMappingURL=device-authorizer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"device-authorizer.js","sourceRoot":"","sources":["../../../src/authorizers/device-authorizer.ts"],"names":[],"mappings":"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","sourcesContent":["import Enquirer from \"enquirer\";\nimport * as jose from \"jose\";\nimport open from \"open\";\nimport {\n discovery,\n initiateDeviceAuthorization,\n pollDeviceAuthorizationGrant,\n TokenEndpointResponse,\n TokenEndpointResponseHelpers,\n} from \"openid-client\";\n\nimport { Credentials } from \"../credentials\";\nimport { AccessDeniedError, AuthorizationRequestExpiredError } from \"../errors\";\nimport { AuthorizerParams, AuthParams, ToolWithAuthHandler } from \"./\";\n\nexport type DeviceAuthorizerOptions = {\n scope: string;\n audience?: string;\n};\n\nexport class DeviceAuthorizer {\n domain: string;\n clientId: string;\n\n private constructor(params?: AuthorizerParams) {\n this.domain = params?.domain || process.env.AUTH0_DOMAIN!;\n this.clientId = params?.clientId || process.env.AUTH0_CLIENT_ID!;\n }\n\n private async _authorize(\n params: DeviceAuthorizerOptions\n ): Promise<Credentials> {\n const config = await discovery(\n new URL(`https://${this.domain}`),\n this.clientId\n );\n\n const handle = await initiateDeviceAuthorization(config, {\n ...params,\n });\n\n const { verification_uri_complete, user_code, expires_in } = handle;\n\n const enquirer = new Enquirer();\n await enquirer.prompt({\n type: \"input\",\n name: \"confirmation\",\n message: `Press Enter to open the browser to log in or press Ctrl+C to abort. You should see the following code: ${user_code}. It expires in ${\n expires_in % 60 === 0\n ? `${expires_in / 60} minutes`\n : `${expires_in} seconds`\n }.`,\n });\n\n await open(verification_uri_complete!);\n\n let tokens: (TokenEndpointResponse & TokenEndpointResponseHelpers) | null =\n null;\n\n try {\n tokens = await pollDeviceAuthorizationGrant(config, handle);\n } catch (err: any) {\n switch (err.error) {\n case \"access_denied\":\n throw new AccessDeniedError(err.error_description);\n case \"expired_token\":\n throw new AuthorizationRequestExpiredError(\n \"Authorization request has expired\"\n );\n default:\n throw new Error(\n `Error: ${err.error}; Description: ${err.error_description}`\n );\n }\n }\n\n if (!tokens) {\n throw new Error(\"Failed to obtain tokens\");\n }\n\n const credentials: Credentials = {\n accessToken: {\n type: tokens.token_type || \"bearer\",\n value: tokens.access_token,\n },\n };\n\n if (tokens.id_token) {\n credentials.idToken = {\n type: tokens.token_type || \"bearer\",\n value: tokens.id_token,\n };\n }\n\n return credentials;\n }\n\n static async authorize(\n options: DeviceAuthorizerOptions,\n params?: AuthorizerParams\n ) {\n const authorizer = new DeviceAuthorizer(params);\n const credentials = await authorizer._authorize(options);\n\n let claims = {};\n\n if (credentials.idToken) {\n claims = jose.decodeJwt(credentials.idToken!.value);\n }\n\n return { accessToken: credentials.accessToken.value, claims } as AuthParams;\n }\n\n static create(params?: AuthorizerParams): DeviceFlowInstance {\n const authorizer = new DeviceAuthorizer(params);\n\n return (options: DeviceAuthorizerOptions) => {\n return function deviceFlow<I, O, C>(\n handler: ToolWithAuthHandler<I, O, C>,\n onError?: (error: Error) => Promise<O>\n ) {\n return async (input: I, config?: C): Promise<O> => {\n try {\n const credentials = await authorizer._authorize(options);\n let claims = {};\n\n if (credentials.idToken) {\n claims = jose.decodeJwt(credentials.idToken!.value);\n }\n\n return handler(\n { accessToken: credentials.accessToken.value, claims },\n input,\n config\n );\n } catch (e: any) {\n if (typeof onError === \"function\") {\n return onError(e);\n }\n\n return \"Access denied.\" as O;\n }\n };\n };\n };\n }\n}\n\nexport type DeviceFlowInstance = (\n options: DeviceAuthorizerOptions\n) => <I, O, C>(\n handler: ToolWithAuthHandler<I, O, C>,\n onError?: (error: Error) => Promise<O>\n) => (input: I, config?: C) => Promise<O>;\n"]}
@@ -0,0 +1,26 @@
1
+ import { AsyncLocalStorage } from "node:async_hooks";
2
+ export type AsyncStorageValue<TContext> = {
3
+ /**
4
+ * The Federated Connection access token.
5
+ */
6
+ accessToken?: string;
7
+ /**
8
+ * The tool execution context.
9
+ */
10
+ context: TContext;
11
+ /**
12
+ * The federated connection name.
13
+ */
14
+ connection: string;
15
+ /**
16
+ * The scopes required to access the federated connection.
17
+ */
18
+ scopes: string[];
19
+ /**
20
+ * The scopes that the current access token has.
21
+ */
22
+ currentScopes?: string[];
23
+ };
24
+ export declare const asyncLocalStorage: AsyncLocalStorage<AsyncStorageValue<any>>;
25
+ export declare const getAccessTokenForConnection: () => string | undefined;
26
+ //# sourceMappingURL=asyncLocalStorage.d.ts.map
@@ -0,0 +1,10 @@
1
+ import { AsyncLocalStorage } from "node:async_hooks";
2
+ export const asyncLocalStorage = new AsyncLocalStorage();
3
+ export const getAccessTokenForConnection = () => {
4
+ const store = asyncLocalStorage.getStore();
5
+ if (typeof store === "undefined") {
6
+ throw new Error("The tool must be wrapped with the withFederatedConnections function.");
7
+ }
8
+ return store === null || store === void 0 ? void 0 : store.accessToken;
9
+ };
10
+ //# sourceMappingURL=asyncLocalStorage.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"asyncLocalStorage.js","sourceRoot":"","sources":["../../../../src/authorizers/federated-connections/asyncLocalStorage.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AA6BrD,MAAM,CAAC,MAAM,iBAAiB,GAAG,IAAI,iBAAiB,EAEnD,CAAC;AAEJ,MAAM,CAAC,MAAM,2BAA2B,GAAG,GAAG,EAAE;IAC9C,MAAM,KAAK,GAAG,iBAAiB,CAAC,QAAQ,EAAE,CAAC;IAC3C,IAAI,OAAO,KAAK,KAAK,WAAW,EAAE,CAAC;QACjC,MAAM,IAAI,KAAK,CACb,sEAAsE,CACvE,CAAC;IACJ,CAAC;IACD,OAAO,KAAK,aAAL,KAAK,uBAAL,KAAK,CAAE,WAAW,CAAC;AAC5B,CAAC,CAAC","sourcesContent":["import { AsyncLocalStorage } from \"node:async_hooks\";\n\nexport type AsyncStorageValue<TContext> = {\n /**\n * The Federated Connection access token.\n */\n accessToken?: string;\n\n /**\n * The tool execution context.\n */\n context: TContext;\n\n /**\n * The federated connection name.\n */\n connection: string;\n\n /**\n * The scopes required to access the federated connection.\n */\n scopes: string[];\n\n /**\n * The scopes that the current access token has.\n */\n currentScopes?: string[];\n};\n\nexport const asyncLocalStorage = new AsyncLocalStorage<\n AsyncStorageValue<any>\n>();\n\nexport const getAccessTokenForConnection = () => {\n const store = asyncLocalStorage.getStore();\n if (typeof store === \"undefined\") {\n throw new Error(\n \"The tool must be wrapped with the withFederatedConnections function.\"\n );\n }\n return store?.accessToken;\n};\n"]}
@@ -0,0 +1,38 @@
1
+ import { Auth0Interrupt } from "../../interrupts";
2
+ import { AuthorizerToolParameter } from "../../parameters";
3
+ import { TokenResponse } from "../../TokenResponse";
4
+ export type { AsyncStorageValue } from "./asyncLocalStorage";
5
+ export { asyncLocalStorage, getAccessTokenForConnection, } from "./asyncLocalStorage";
6
+ export type FederatedConnectionAuthorizerParams<ToolExecuteArgs extends any[]> = {
7
+ refreshToken?: AuthorizerToolParameter<ToolExecuteArgs, string | undefined>;
8
+ accessToken?: AuthorizerToolParameter<ToolExecuteArgs, TokenResponse | undefined>;
9
+ scopes: AuthorizerToolParameter<ToolExecuteArgs, string[]>;
10
+ connection: AuthorizerToolParameter<ToolExecuteArgs, string>;
11
+ };
12
+ /**
13
+ * Requests authorization to a third party service via Federated Connection.
14
+ */
15
+ export declare class FederatedConnectionAuthorizerBase<ToolExecuteArgs extends any[]> {
16
+ private readonly auth0;
17
+ private params;
18
+ constructor(auth0: {
19
+ domain: string;
20
+ clientId: string;
21
+ clientSecret: string | undefined;
22
+ }, params: FederatedConnectionAuthorizerParams<ToolExecuteArgs>);
23
+ protected handleAuthorizationInterrupts(err: Auth0Interrupt): void;
24
+ protected validateToken(tokenResponse?: TokenResponse): void;
25
+ private getAccessTokenImpl;
26
+ protected getAccessToken(...toolContext: ToolExecuteArgs): Promise<TokenResponse | undefined>;
27
+ protected getRefreshToken(...toolContext: ToolExecuteArgs): Promise<string | undefined>;
28
+ /**
29
+ *
30
+ * Wraps the execute method of an AI tool to handle Federated Connections authorization.
31
+ *
32
+ * @param getContext - A function that returns the context of the tool execution.
33
+ * @param execute - The tool execute method.
34
+ * @returns The wrapped execute method.
35
+ */
36
+ protect(getContext: (...args: ToolExecuteArgs) => any, execute: (...args: ToolExecuteArgs) => any): (...args: ToolExecuteArgs) => any;
37
+ }
38
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,123 @@
1
+ import { Auth0Interrupt, FederatedConnectionError, FederatedConnectionInterrupt, } from "../../interrupts";
2
+ import { resolveParameter } from "../../parameters";
3
+ import { asyncLocalStorage } from "./asyncLocalStorage";
4
+ export { asyncLocalStorage, getAccessTokenForConnection, } from "./asyncLocalStorage";
5
+ /**
6
+ * Requests authorization to a third party service via Federated Connection.
7
+ */
8
+ export class FederatedConnectionAuthorizerBase {
9
+ constructor(auth0, params) {
10
+ this.auth0 = auth0;
11
+ this.params = params;
12
+ if (typeof params.refreshToken === "undefined" &&
13
+ typeof params.accessToken === "undefined") {
14
+ throw new Error("Either refreshToken or accessToken must be provided to initialize the Authorizer.");
15
+ }
16
+ if (typeof params.accessToken !== "undefined" &&
17
+ typeof params.refreshToken !== "undefined") {
18
+ throw new Error("Only one of refreshToken or accessToken can be provided to initialize the Authorizer.");
19
+ }
20
+ }
21
+ handleAuthorizationInterrupts(err) {
22
+ throw err;
23
+ }
24
+ validateToken(tokenResponse) {
25
+ var _a;
26
+ const store = asyncLocalStorage.getStore();
27
+ if (!store) {
28
+ throw new Error("The tool must be wrapped with the FederationConnectionAuthorizer.");
29
+ }
30
+ const { scopes, connection } = store;
31
+ if (!tokenResponse) {
32
+ throw new FederatedConnectionInterrupt(`Authorization required to access the Federated Connection: ${this.params.connection}`, connection, scopes, scopes);
33
+ }
34
+ const currentScopes = ((_a = tokenResponse.scope) !== null && _a !== void 0 ? _a : "").split(" ");
35
+ const missingScopes = scopes.filter((s) => !currentScopes.includes(s));
36
+ store.currentScopes = currentScopes;
37
+ if (missingScopes.length > 0) {
38
+ throw new FederatedConnectionInterrupt(`Authorization required to access the Federated Connection: ${this.params.connection}. Missing scopes: ${missingScopes.join(", ")}`, connection, scopes, [...currentScopes, ...scopes]);
39
+ }
40
+ }
41
+ async getAccessTokenImpl(...toolContext) {
42
+ const store = asyncLocalStorage.getStore();
43
+ if (!store) {
44
+ throw new Error("The tool must be wrapped with the FederationConnectionAuthorizer.");
45
+ }
46
+ const { connection } = store;
47
+ const subjectToken = await this.getRefreshToken(...toolContext);
48
+ if (!subjectToken) {
49
+ return;
50
+ }
51
+ const exchangeParams = {
52
+ grant_type: "urn:auth0:params:oauth:grant-type:token-exchange:federated-connection-access-token",
53
+ client_id: this.auth0.clientId,
54
+ client_secret: this.auth0.clientSecret,
55
+ subject_token_type: "urn:ietf:params:oauth:token-type:refresh_token",
56
+ subject_token: subjectToken,
57
+ connection: connection,
58
+ requested_token_type: "http://auth0.com/oauth/token-type/federated-connection-access-token",
59
+ };
60
+ const res = await fetch(`https://${this.auth0.domain}/oauth/token`, {
61
+ method: "POST",
62
+ headers: { "Content-Type": "application/json" },
63
+ body: JSON.stringify(exchangeParams),
64
+ });
65
+ if (!res.ok) {
66
+ return;
67
+ }
68
+ const tokenResponse = await res.json();
69
+ return tokenResponse;
70
+ }
71
+ async getAccessToken(...toolContext) {
72
+ let tokenResponse;
73
+ if (typeof this.params.refreshToken === "function") {
74
+ tokenResponse = await this.getAccessTokenImpl(...toolContext);
75
+ }
76
+ else {
77
+ tokenResponse = await resolveParameter(this.params.accessToken, toolContext);
78
+ }
79
+ this.validateToken(tokenResponse);
80
+ return tokenResponse;
81
+ }
82
+ async getRefreshToken(...toolContext) {
83
+ return await resolveParameter(this.params.refreshToken, toolContext);
84
+ }
85
+ /**
86
+ *
87
+ * Wraps the execute method of an AI tool to handle Federated Connections authorization.
88
+ *
89
+ * @param getContext - A function that returns the context of the tool execution.
90
+ * @param execute - The tool execute method.
91
+ * @returns The wrapped execute method.
92
+ */
93
+ protect(getContext, execute) {
94
+ return async (...args) => {
95
+ const asyncStore = {
96
+ context: await getContext(...args),
97
+ scopes: await resolveParameter(this.params.scopes, args),
98
+ connection: await resolveParameter(this.params.connection, args),
99
+ };
100
+ if (asyncLocalStorage.getStore()) {
101
+ throw new Error("Cannot nest tool calls that require federated connection authorization.");
102
+ }
103
+ return asyncLocalStorage.run(asyncStore, async () => {
104
+ try {
105
+ const tokenResponse = await this.getAccessToken(...args);
106
+ asyncStore.accessToken = tokenResponse.access_token;
107
+ return await execute(...args);
108
+ }
109
+ catch (err) {
110
+ if (err instanceof FederatedConnectionError) {
111
+ const interrupt = new FederatedConnectionInterrupt(err.message, asyncStore.connection, asyncStore.scopes, asyncStore.scopes);
112
+ return this.handleAuthorizationInterrupts(interrupt);
113
+ }
114
+ if (err instanceof Auth0Interrupt) {
115
+ return this.handleAuthorizationInterrupts(err);
116
+ }
117
+ throw err;
118
+ }
119
+ });
120
+ };
121
+ }
122
+ }
123
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/authorizers/federated-connections/index.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n Auth0Interrupt,\n FederatedConnectionError,\n FederatedConnectionInterrupt,\n} from \"../../interrupts\";\nimport { AuthorizerToolParameter, resolveParameter } from \"../../parameters\";\nimport { TokenResponse } from \"../../TokenResponse\";\nimport { asyncLocalStorage, AsyncStorageValue } from \"./asyncLocalStorage\";\n\nexport type { AsyncStorageValue } from \"./asyncLocalStorage\";\n\nexport {\n asyncLocalStorage,\n getAccessTokenForConnection,\n} from \"./asyncLocalStorage\";\n\nexport type FederatedConnectionAuthorizerParams<ToolExecuteArgs extends any[]> =\n {\n refreshToken?: AuthorizerToolParameter<ToolExecuteArgs, string | undefined>;\n accessToken?: AuthorizerToolParameter<\n ToolExecuteArgs,\n TokenResponse | undefined\n >;\n scopes: AuthorizerToolParameter<ToolExecuteArgs, string[]>;\n connection: AuthorizerToolParameter<ToolExecuteArgs, string>;\n };\n\n/**\n * Requests authorization to a third party service via Federated Connection.\n */\nexport class FederatedConnectionAuthorizerBase<ToolExecuteArgs extends any[]> {\n constructor(\n private readonly auth0: {\n domain: string;\n clientId: string;\n clientSecret: string | undefined;\n },\n private params: FederatedConnectionAuthorizerParams<ToolExecuteArgs>\n ) {\n if (\n typeof params.refreshToken === \"undefined\" &&\n typeof params.accessToken === \"undefined\"\n ) {\n throw new Error(\n \"Either refreshToken or accessToken must be provided to initialize the Authorizer.\"\n );\n }\n\n if (\n typeof params.accessToken !== \"undefined\" &&\n typeof params.refreshToken !== \"undefined\"\n ) {\n throw new Error(\n \"Only one of refreshToken or accessToken can be provided to initialize the Authorizer.\"\n );\n }\n }\n\n protected handleAuthorizationInterrupts(err: Auth0Interrupt) {\n throw err;\n }\n\n protected validateToken(tokenResponse?: TokenResponse) {\n const store = asyncLocalStorage.getStore();\n if (!store) {\n throw new Error(\n \"The tool must be wrapped with the FederationConnectionAuthorizer.\"\n );\n }\n\n const { scopes, connection } = store;\n\n if (!tokenResponse) {\n throw new FederatedConnectionInterrupt(\n `Authorization required to access the Federated Connection: ${this.params.connection}`,\n connection,\n scopes,\n scopes\n );\n }\n\n const currentScopes = (tokenResponse.scope ?? \"\").split(\" \");\n const missingScopes = scopes.filter((s) => !currentScopes.includes(s));\n store.currentScopes = currentScopes;\n\n if (missingScopes.length > 0) {\n throw new FederatedConnectionInterrupt(\n `Authorization required to access the Federated Connection: ${\n this.params.connection\n }. Missing scopes: ${missingScopes.join(\", \")}`,\n connection,\n scopes,\n [...currentScopes, ...scopes]\n );\n }\n }\n\n private async getAccessTokenImpl(\n ...toolContext: ToolExecuteArgs\n ): Promise<TokenResponse | undefined> {\n const store = asyncLocalStorage.getStore();\n if (!store) {\n throw new Error(\n \"The tool must be wrapped with the FederationConnectionAuthorizer.\"\n );\n }\n\n const { connection } = store;\n\n const subjectToken = await this.getRefreshToken(...toolContext);\n\n if (!subjectToken) {\n return;\n }\n\n const exchangeParams = {\n grant_type:\n \"urn:auth0:params:oauth:grant-type:token-exchange:federated-connection-access-token\",\n client_id: this.auth0.clientId,\n client_secret: this.auth0.clientSecret,\n subject_token_type: \"urn:ietf:params:oauth:token-type:refresh_token\",\n subject_token: subjectToken,\n connection: connection,\n requested_token_type:\n \"http://auth0.com/oauth/token-type/federated-connection-access-token\",\n };\n\n const res = await fetch(`https://${this.auth0.domain}/oauth/token`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify(exchangeParams),\n });\n\n if (!res.ok) {\n return;\n }\n\n const tokenResponse: TokenResponse = await res.json();\n return tokenResponse;\n }\n\n protected async getAccessToken(\n ...toolContext: ToolExecuteArgs\n ): Promise<TokenResponse | undefined> {\n let tokenResponse: TokenResponse | undefined;\n if (typeof this.params.refreshToken === \"function\") {\n tokenResponse = await this.getAccessTokenImpl(...toolContext);\n } else {\n tokenResponse = await resolveParameter(\n this.params.accessToken!,\n toolContext\n );\n }\n this.validateToken(tokenResponse);\n return tokenResponse;\n }\n\n protected async getRefreshToken(...toolContext: ToolExecuteArgs) {\n return await resolveParameter(this.params.refreshToken, toolContext);\n }\n\n /**\n *\n * Wraps the execute method of an AI tool to handle Federated Connections authorization.\n *\n * @param getContext - A function that returns the context of the tool execution.\n * @param execute - The tool execute method.\n * @returns The wrapped execute method.\n */\n protect(\n getContext: (...args: ToolExecuteArgs) => any,\n execute: (...args: ToolExecuteArgs) => any\n ): (...args: ToolExecuteArgs) => any {\n return async (...args: ToolExecuteArgs) => {\n const asyncStore: AsyncStorageValue<any> = {\n context: await getContext(...args),\n scopes: await resolveParameter(this.params.scopes, args),\n connection: await resolveParameter(this.params.connection, args),\n };\n\n if (asyncLocalStorage.getStore()) {\n throw new Error(\n \"Cannot nest tool calls that require federated connection authorization.\"\n );\n }\n\n return asyncLocalStorage.run(asyncStore, async () => {\n try {\n const tokenResponse = await this.getAccessToken(...args);\n asyncStore.accessToken = tokenResponse!.access_token;\n return await execute(...args);\n } catch (err) {\n if (err instanceof FederatedConnectionError) {\n const interrupt = new FederatedConnectionInterrupt(\n err.message,\n asyncStore.connection,\n asyncStore.scopes,\n asyncStore.scopes\n );\n return this.handleAuthorizationInterrupts(interrupt);\n }\n if (err instanceof Auth0Interrupt) {\n return this.handleAuthorizationInterrupts(err);\n }\n throw err;\n }\n });\n };\n }\n}\n"]}
@@ -0,0 +1,16 @@
1
+ import { OpenFgaClient } from "@openfga/sdk";
2
+ export type FGAClientParams = {
3
+ apiUrl?: string;
4
+ storeId?: string;
5
+ credentials?: {
6
+ method: any;
7
+ config: {
8
+ apiTokenIssuer: string;
9
+ apiAudience: string;
10
+ clientId: string;
11
+ clientSecret: string;
12
+ };
13
+ };
14
+ };
15
+ export declare function buildOpenFgaClient(fgaClientParams?: FGAClientParams | null): OpenFgaClient;
16
+ //# sourceMappingURL=fga-client.d.ts.map
@@ -0,0 +1,27 @@
1
+ import { CredentialsMethod, OpenFgaClient } from "@openfga/sdk";
2
+ export function buildOpenFgaClient(fgaClientParams) {
3
+ var _a, _b, _c, _d, _e;
4
+ return new OpenFgaClient({
5
+ apiUrl: (fgaClientParams === null || fgaClientParams === void 0 ? void 0 : fgaClientParams.apiUrl) ||
6
+ process.env.FGA_API_URL ||
7
+ "https://api.us1.fga.dev",
8
+ storeId: (fgaClientParams === null || fgaClientParams === void 0 ? void 0 : fgaClientParams.storeId) || process.env.FGA_STORE_ID,
9
+ credentials: {
10
+ method: ((_a = fgaClientParams === null || fgaClientParams === void 0 ? void 0 : fgaClientParams.credentials) === null || _a === void 0 ? void 0 : _a.method) ||
11
+ CredentialsMethod.ClientCredentials,
12
+ config: {
13
+ apiTokenIssuer: ((_b = fgaClientParams === null || fgaClientParams === void 0 ? void 0 : fgaClientParams.credentials) === null || _b === void 0 ? void 0 : _b.config.apiTokenIssuer) ||
14
+ process.env.FGA_API_TOKEN_ISSUER ||
15
+ "auth.fga.dev",
16
+ apiAudience: ((_c = fgaClientParams === null || fgaClientParams === void 0 ? void 0 : fgaClientParams.credentials) === null || _c === void 0 ? void 0 : _c.config.apiAudience) ||
17
+ process.env.FGA_API_AUDIENCE ||
18
+ "https://api.us1.fga.dev/",
19
+ clientId: ((_d = fgaClientParams === null || fgaClientParams === void 0 ? void 0 : fgaClientParams.credentials) === null || _d === void 0 ? void 0 : _d.config.clientId) ||
20
+ process.env.FGA_CLIENT_ID,
21
+ clientSecret: ((_e = fgaClientParams === null || fgaClientParams === void 0 ? void 0 : fgaClientParams.credentials) === null || _e === void 0 ? void 0 : _e.config.clientSecret) ||
22
+ process.env.FGA_CLIENT_SECRET,
23
+ },
24
+ },
25
+ });
26
+ }
27
+ //# sourceMappingURL=fga-client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fga-client.js","sourceRoot":"","sources":["../../../../src/authorizers/fga/fga-client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAgBhE,MAAM,UAAU,kBAAkB,CAChC,eAAwC;;IAExC,OAAO,IAAI,aAAa,CAAC;QACvB,MAAM,EACJ,CAAA,eAAe,aAAf,eAAe,uBAAf,eAAe,CAAE,MAAM;YACvB,OAAO,CAAC,GAAG,CAAC,WAAW;YACvB,yBAAyB;QAC3B,OAAO,EAAE,CAAA,eAAe,aAAf,eAAe,uBAAf,eAAe,CAAE,OAAO,KAAI,OAAO,CAAC,GAAG,CAAC,YAAa;QAC9D,WAAW,EAAE;YACX,MAAM,EACJ,CAAA,MAAA,eAAe,aAAf,eAAe,uBAAf,eAAe,CAAE,WAAW,0CAAE,MAAM;gBACpC,iBAAiB,CAAC,iBAAiB;YACrC,MAAM,EAAE;gBACN,cAAc,EACZ,CAAA,MAAA,eAAe,aAAf,eAAe,uBAAf,eAAe,CAAE,WAAW,0CAAE,MAAM,CAAC,cAAc;oBACnD,OAAO,CAAC,GAAG,CAAC,oBAAoB;oBAChC,cAAc;gBAChB,WAAW,EACT,CAAA,MAAA,eAAe,aAAf,eAAe,uBAAf,eAAe,CAAE,WAAW,0CAAE,MAAM,CAAC,WAAW;oBAChD,OAAO,CAAC,GAAG,CAAC,gBAAgB;oBAC5B,0BAA0B;gBAC5B,QAAQ,EACN,CAAA,MAAA,eAAe,aAAf,eAAe,uBAAf,eAAe,CAAE,WAAW,0CAAE,MAAM,CAAC,QAAQ;oBAC7C,OAAO,CAAC,GAAG,CAAC,aAAc;gBAC5B,YAAY,EACV,CAAA,MAAA,eAAe,aAAf,eAAe,uBAAf,eAAe,CAAE,WAAW,0CAAE,MAAM,CAAC,YAAY;oBACjD,OAAO,CAAC,GAAG,CAAC,iBAAkB;aACjC;SACF;KACF,CAAC,CAAC;AACL,CAAC","sourcesContent":["import { CredentialsMethod, OpenFgaClient } from \"@openfga/sdk\";\n\nexport type FGAClientParams = {\n apiUrl?: string;\n storeId?: string;\n credentials?: {\n method: any;\n config: {\n apiTokenIssuer: string;\n apiAudience: string;\n clientId: string;\n clientSecret: string;\n };\n };\n};\n\nexport function buildOpenFgaClient(\n fgaClientParams?: FGAClientParams | null\n): OpenFgaClient {\n return new OpenFgaClient({\n apiUrl:\n fgaClientParams?.apiUrl ||\n process.env.FGA_API_URL ||\n \"https://api.us1.fga.dev\",\n storeId: fgaClientParams?.storeId || process.env.FGA_STORE_ID!,\n credentials: {\n method:\n fgaClientParams?.credentials?.method ||\n CredentialsMethod.ClientCredentials,\n config: {\n apiTokenIssuer:\n fgaClientParams?.credentials?.config.apiTokenIssuer ||\n process.env.FGA_API_TOKEN_ISSUER ||\n \"auth.fga.dev\",\n apiAudience:\n fgaClientParams?.credentials?.config.apiAudience ||\n process.env.FGA_API_AUDIENCE ||\n \"https://api.us1.fga.dev/\",\n clientId:\n fgaClientParams?.credentials?.config.clientId ||\n process.env.FGA_CLIENT_ID!,\n clientSecret:\n fgaClientParams?.credentials?.config.clientSecret ||\n process.env.FGA_CLIENT_SECRET!,\n },\n },\n });\n}\n"]}
@@ -0,0 +1,63 @@
1
+ import { ClientBatchCheckItem, ConsistencyPreference } from "@openfga/sdk";
2
+ import { FGAClientParams } from "./fga-client";
3
+ export type FGAFilterCheckerFn<T extends Record<string, any>> = (doc: T) => ClientBatchCheckItem;
4
+ export type FGAFilterArgs<T extends Record<string, any>> = {
5
+ buildQuery: FGAFilterCheckerFn<T>;
6
+ consistency?: ConsistencyPreference;
7
+ };
8
+ /**
9
+ * A filter that allows filtering documents based on access control checks
10
+ * using OpenFGA. This class performs batch checks on passed documents,
11
+ * returning only the ones that pass the specified access criteria.
12
+ *
13
+ * @remarks
14
+ * The FGAFilter requires a buildQuery function to specify how access checks
15
+ * are formed for each document, the checks are executed via an OpenFGA client
16
+ * or equivalent mechanism. The checks are then mapped back to their corresponding
17
+ * documents to filter out those for which access is denied.
18
+ *
19
+ * @example
20
+ * ```ts
21
+ * const filter = FGAFilter.create({
22
+ * buildQuery: (doc) => ({
23
+ * user: `user:${user}`,
24
+ * object: `doc:${doc.metadata.id}`,
25
+ * relation: "viewer",
26
+ * }),
27
+ * });
28
+ * ```
29
+ */
30
+ export declare class FGAFilter<T extends Record<string, any>> {
31
+ private buildQuery;
32
+ private consistency;
33
+ private fgaClient;
34
+ private constructor();
35
+ /**
36
+ * Creates a new FGAFilter instance using the given arguments and optional OpenFgaClient.
37
+ *
38
+ * @param args - @FGAFilterArgs
39
+ * @param args.buildQuery - A function to generate access check requests for each document.
40
+ * @param args.consistency - Optional - The consistency preference for the OpenFGA client.
41
+ * @param fgaClientParams - Optional - OpenFgaClient configuration to execute checks against.
42
+ * @returns A newly created FGAFilter instance configured with the provided arguments.
43
+ */
44
+ static create<T extends Record<string, any>>(args: FGAFilterArgs<T>, fgaClientParams?: FGAClientParams): FGAFilter<T>;
45
+ /**
46
+ * Filters documents based on the provided query parameters, processes
47
+ * them through a checker function,
48
+ * and filters the documents based on permissions.
49
+ *
50
+ * @param params - The query parameters used to retrieve nodes.
51
+ * @returns A promise that resolves to an array of documents that have passed the permission checks.
52
+ */
53
+ filter(documents: T[]): Promise<T[]>;
54
+ /**
55
+ * Checks permissions for a list of client requests.
56
+ *
57
+ * @param checks - An array of `ClientBatchCheckItem` objects representing the permissions to be checked.
58
+ * @returns A promise that resolves to a `Map` where the keys are object identifiers and the values are booleans indicating whether the permission is allowed.
59
+ */
60
+ private checkPermissions;
61
+ private getCheckKey;
62
+ }
63
+ //# sourceMappingURL=fga-filter.d.ts.map
@@ -0,0 +1,90 @@
1
+ import { ConsistencyPreference, } from "@openfga/sdk";
2
+ import { buildOpenFgaClient } from "./fga-client";
3
+ /**
4
+ * A filter that allows filtering documents based on access control checks
5
+ * using OpenFGA. This class performs batch checks on passed documents,
6
+ * returning only the ones that pass the specified access criteria.
7
+ *
8
+ * @remarks
9
+ * The FGAFilter requires a buildQuery function to specify how access checks
10
+ * are formed for each document, the checks are executed via an OpenFGA client
11
+ * or equivalent mechanism. The checks are then mapped back to their corresponding
12
+ * documents to filter out those for which access is denied.
13
+ *
14
+ * @example
15
+ * ```ts
16
+ * const filter = FGAFilter.create({
17
+ * buildQuery: (doc) => ({
18
+ * user: `user:${user}`,
19
+ * object: `doc:${doc.metadata.id}`,
20
+ * relation: "viewer",
21
+ * }),
22
+ * });
23
+ * ```
24
+ */
25
+ export class FGAFilter {
26
+ constructor({ buildQuery, consistency }, fgaClientParams) {
27
+ this.buildQuery = buildQuery;
28
+ this.consistency = consistency || ConsistencyPreference.HigherConsistency;
29
+ this.fgaClient = buildOpenFgaClient(fgaClientParams);
30
+ }
31
+ /**
32
+ * Creates a new FGAFilter instance using the given arguments and optional OpenFgaClient.
33
+ *
34
+ * @param args - @FGAFilterArgs
35
+ * @param args.buildQuery - A function to generate access check requests for each document.
36
+ * @param args.consistency - Optional - The consistency preference for the OpenFGA client.
37
+ * @param fgaClientParams - Optional - OpenFgaClient configuration to execute checks against.
38
+ * @returns A newly created FGAFilter instance configured with the provided arguments.
39
+ */
40
+ static create(args, fgaClientParams) {
41
+ return new FGAFilter(args, fgaClientParams);
42
+ }
43
+ /**
44
+ * Filters documents based on the provided query parameters, processes
45
+ * them through a checker function,
46
+ * and filters the documents based on permissions.
47
+ *
48
+ * @param params - The query parameters used to retrieve nodes.
49
+ * @returns A promise that resolves to an array of documents that have passed the permission checks.
50
+ */
51
+ async filter(documents) {
52
+ const { checks, documentToObject } = documents.reduce((acc, doc) => {
53
+ const check = this.buildQuery(doc);
54
+ const checkKey = this.getCheckKey(check);
55
+ acc.documentToObject.set(doc, checkKey);
56
+ // Skip duplicate checks for same user, object, and relation
57
+ if (!acc.seenChecks.has(checkKey)) {
58
+ acc.seenChecks.add(checkKey);
59
+ acc.checks.push(check);
60
+ }
61
+ return acc;
62
+ }, {
63
+ checks: [],
64
+ documentToObject: new Map(),
65
+ seenChecks: new Set(),
66
+ });
67
+ const permissionsMap = await this.checkPermissions(checks);
68
+ return documents.filter((d) => permissionsMap.get(documentToObject.get(d) || "") === true);
69
+ }
70
+ /**
71
+ * Checks permissions for a list of client requests.
72
+ *
73
+ * @param checks - An array of `ClientBatchCheckItem` objects representing the permissions to be checked.
74
+ * @returns A promise that resolves to a `Map` where the keys are object identifiers and the values are booleans indicating whether the permission is allowed.
75
+ */
76
+ async checkPermissions(checks) {
77
+ const response = await this.fgaClient.batchCheck({ checks }, {
78
+ consistency: this.consistency,
79
+ });
80
+ return response.result.reduce((permissionMap, result) => {
81
+ const checkKey = this.getCheckKey(result.request);
82
+ permissionMap.set(checkKey, result.allowed || false);
83
+ return permissionMap;
84
+ }, new Map());
85
+ }
86
+ getCheckKey(check) {
87
+ return `${check.user}|${check.object}|${check.relation}`;
88
+ }
89
+ }
90
+ //# sourceMappingURL=fga-filter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fga-filter.js","sourceRoot":"","sources":["../../../../src/authorizers/fga/fga-filter.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,qBAAqB,GAEtB,MAAM,cAAc,CAAC;AAEtB,OAAO,EAAE,kBAAkB,EAAmB,MAAM,cAAc,CAAC;AAWnE;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,MAAM,OAAO,SAAS;IAKpB,YACE,EAAE,UAAU,EAAE,WAAW,EAAoB,EAC7C,eAAiC;QAEjC,IAAI,CAAC,UAAU,GAAG,UAAU,CAAC;QAC7B,IAAI,CAAC,WAAW,GAAG,WAAW,IAAI,qBAAqB,CAAC,iBAAiB,CAAC;QAC1E,IAAI,CAAC,SAAS,GAAG,kBAAkB,CAAC,eAAe,CAAC,CAAC;IACvD,CAAC;IAED;;;;;;;;OAQG;IACH,MAAM,CAAC,MAAM,CACX,IAAsB,EACtB,eAAiC;QAEjC,OAAO,IAAI,SAAS,CAAC,IAAI,EAAE,eAAe,CAAC,CAAC;IAC9C,CAAC;IAED;;;;;;;OAOG;IACH,KAAK,CAAC,MAAM,CAAC,SAAc;QACzB,MAAM,EAAE,MAAM,EAAE,gBAAgB,EAAE,GAAG,SAAS,CAAC,MAAM,CACnD,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE;YACX,MAAM,KAAK,GAAG,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC;YACnC,MAAM,QAAQ,GAAG,IAAI,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC;YACzC,GAAG,CAAC,gBAAgB,CAAC,GAAG,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC;YACxC,4DAA4D;YAC5D,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAE,CAAC;gBAClC,GAAG,CAAC,UAAU,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;gBAC7B,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YACzB,CAAC;YACD,OAAO,GAAG,CAAC;QACb,CAAC,EACD;YACE,MAAM,EAAE,EAA4B;YACpC,gBAAgB,EAAE,IAAI,GAAG,EAAa;YACtC,UAAU,EAAE,IAAI,GAAG,EAAU;SAC9B,CACF,CAAC;QAEF,MAAM,cAAc,GAAG,MAAM,IAAI,CAAC,gBAAgB,CAAC,MAAM,CAAC,CAAC;QAE3D,OAAO,SAAS,CAAC,MAAM,CACrB,CAAC,CAAC,EAAE,EAAE,CAAC,cAAc,CAAC,GAAG,CAAC,gBAAgB,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,KAAK,IAAI,CAClE,CAAC;IACJ,CAAC;IAED;;;;;OAKG;IACK,KAAK,CAAC,gBAAgB,CAC5B,MAA8B;QAE9B,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,SAAS,CAAC,UAAU,CAC9C,EAAE,MAAM,EAAE,EACV;YACE,WAAW,EAAE,IAAI,CAAC,WAAW;SAC9B,CACF,CAAC;QAEF,OAAO,QAAQ,CAAC,MAAM,CAAC,MAAM,CAC3B,CAAC,aAAmC,EAAE,MAAM,EAAE,EAAE;YAC9C,MAAM,QAAQ,GAAG,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;YAClD,aAAa,CAAC,GAAG,CAAC,QAAQ,EAAE,MAAM,CAAC,OAAO,IAAI,KAAK,CAAC,CAAC;YACrD,OAAO,aAAa,CAAC;QACvB,CAAC,EACD,IAAI,GAAG,EAAmB,CAC3B,CAAC;IACJ,CAAC;IAEO,WAAW,CAAC,KAA2B;QAC7C,OAAO,GAAG,KAAK,CAAC,IAAI,IAAI,KAAK,CAAC,MAAM,IAAI,KAAK,CAAC,QAAQ,EAAE,CAAC;IAC3D,CAAC;CACF","sourcesContent":["import {\n ClientBatchCheckItem,\n ConsistencyPreference,\n OpenFgaClient,\n} from \"@openfga/sdk\";\n\nimport { buildOpenFgaClient, FGAClientParams } from \"./fga-client\";\n\nexport type FGAFilterCheckerFn<T extends Record<string, any>> = (\n doc: T\n) => ClientBatchCheckItem;\n\nexport type FGAFilterArgs<T extends Record<string, any>> = {\n buildQuery: FGAFilterCheckerFn<T>;\n consistency?: ConsistencyPreference;\n};\n\n/**\n * A filter that allows filtering documents based on access control checks\n * using OpenFGA. This class performs batch checks on passed documents,\n * returning only the ones that pass the specified access criteria.\n *\n * @remarks\n * The FGAFilter requires a buildQuery function to specify how access checks\n * are formed for each document, the checks are executed via an OpenFGA client\n * or equivalent mechanism. The checks are then mapped back to their corresponding\n * documents to filter out those for which access is denied.\n *\n * @example\n * ```ts\n * const filter = FGAFilter.create({\n * buildQuery: (doc) => ({\n * user: `user:${user}`,\n * object: `doc:${doc.metadata.id}`,\n * relation: \"viewer\",\n * }),\n * });\n * ```\n */\nexport class FGAFilter<T extends Record<string, any>> {\n private buildQuery: FGAFilterCheckerFn<T>;\n private consistency: ConsistencyPreference;\n private fgaClient: OpenFgaClient;\n\n private constructor(\n { buildQuery, consistency }: FGAFilterArgs<T>,\n fgaClientParams?: FGAClientParams\n ) {\n this.buildQuery = buildQuery;\n this.consistency = consistency || ConsistencyPreference.HigherConsistency;\n this.fgaClient = buildOpenFgaClient(fgaClientParams);\n }\n\n /**\n * Creates a new FGAFilter instance using the given arguments and optional OpenFgaClient.\n *\n * @param args - @FGAFilterArgs\n * @param args.buildQuery - A function to generate access check requests for each document.\n * @param args.consistency - Optional - The consistency preference for the OpenFGA client.\n * @param fgaClientParams - Optional - OpenFgaClient configuration to execute checks against.\n * @returns A newly created FGAFilter instance configured with the provided arguments.\n */\n static create<T extends Record<string, any>>(\n args: FGAFilterArgs<T>,\n fgaClientParams?: FGAClientParams\n ): FGAFilter<T> {\n return new FGAFilter(args, fgaClientParams);\n }\n\n /**\n * Filters documents based on the provided query parameters, processes\n * them through a checker function,\n * and filters the documents based on permissions.\n *\n * @param params - The query parameters used to retrieve nodes.\n * @returns A promise that resolves to an array of documents that have passed the permission checks.\n */\n async filter(documents: T[]): Promise<T[]> {\n const { checks, documentToObject } = documents.reduce(\n (acc, doc) => {\n const check = this.buildQuery(doc);\n const checkKey = this.getCheckKey(check);\n acc.documentToObject.set(doc, checkKey);\n // Skip duplicate checks for same user, object, and relation\n if (!acc.seenChecks.has(checkKey)) {\n acc.seenChecks.add(checkKey);\n acc.checks.push(check);\n }\n return acc;\n },\n {\n checks: [] as ClientBatchCheckItem[],\n documentToObject: new Map<T, string>(),\n seenChecks: new Set<string>(),\n }\n );\n\n const permissionsMap = await this.checkPermissions(checks);\n\n return documents.filter(\n (d) => permissionsMap.get(documentToObject.get(d) || \"\") === true\n );\n }\n\n /**\n * Checks permissions for a list of client requests.\n *\n * @param checks - An array of `ClientBatchCheckItem` objects representing the permissions to be checked.\n * @returns A promise that resolves to a `Map` where the keys are object identifiers and the values are booleans indicating whether the permission is allowed.\n */\n private async checkPermissions(\n checks: ClientBatchCheckItem[]\n ): Promise<Map<string, boolean>> {\n const response = await this.fgaClient.batchCheck(\n { checks },\n {\n consistency: this.consistency,\n }\n );\n\n return response.result.reduce(\n (permissionMap: Map<string, boolean>, result) => {\n const checkKey = this.getCheckKey(result.request);\n permissionMap.set(checkKey, result.allowed || false);\n return permissionMap;\n },\n new Map<string, boolean>()\n );\n }\n\n private getCheckKey(check: ClientBatchCheckItem): string {\n return `${check.user}|${check.object}|${check.relation}`;\n }\n}\n"]}