@fragno-dev/backoffice-api 0.0.0-stage → 0.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.
- package/LICENSE.md +16 -0
- package/README.md +90 -2
- package/dist/api.d.ts +35 -0
- package/dist/api.d.ts.map +1 -0
- package/dist/api.js +10 -0
- package/dist/api.js.map +1 -0
- package/dist/errors.d.ts +45 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +35 -0
- package/dist/errors.js.map +1 -0
- package/dist/openapi.d.ts +46 -0
- package/dist/openapi.d.ts.map +1 -0
- package/dist/openapi.js +205 -0
- package/dist/openapi.js.map +1 -0
- package/dist/v0/account.d.ts +171 -0
- package/dist/v0/account.d.ts.map +1 -0
- package/dist/v0/account.js +78 -0
- package/dist/v0/account.js.map +1 -0
- package/dist/v0/api.d.ts +3437 -0
- package/dist/v0/api.d.ts.map +1 -0
- package/dist/v0/api.js +52 -0
- package/dist/v0/api.js.map +1 -0
- package/dist/v0/app-actions.d.ts +105 -0
- package/dist/v0/app-actions.d.ts.map +1 -0
- package/dist/v0/app-actions.js +100 -0
- package/dist/v0/app-actions.js.map +1 -0
- package/dist/v0/apps.d.ts +417 -0
- package/dist/v0/apps.d.ts.map +1 -0
- package/dist/v0/apps.js +163 -0
- package/dist/v0/apps.js.map +1 -0
- package/dist/v0/automation.d.ts +730 -0
- package/dist/v0/automation.d.ts.map +1 -0
- package/dist/v0/automation.js +355 -0
- package/dist/v0/automation.js.map +1 -0
- package/dist/v0/capabilities.d.ts +97 -0
- package/dist/v0/capabilities.d.ts.map +1 -0
- package/dist/v0/capabilities.js +41 -0
- package/dist/v0/capabilities.js.map +1 -0
- package/dist/v0/client.d.ts +39 -0
- package/dist/v0/client.d.ts.map +1 -0
- package/dist/v0/client.js +61 -0
- package/dist/v0/client.js.map +1 -0
- package/dist/v0/events.d.ts +789 -0
- package/dist/v0/events.d.ts.map +1 -0
- package/dist/v0/events.js +153 -0
- package/dist/v0/events.js.map +1 -0
- package/dist/v0/forms.d.ts +172 -0
- package/dist/v0/forms.d.ts.map +1 -0
- package/dist/v0/forms.js +88 -0
- package/dist/v0/forms.js.map +1 -0
- package/dist/v0/hooks.d.ts +61 -0
- package/dist/v0/hooks.d.ts.map +1 -0
- package/dist/v0/hooks.js +49 -0
- package/dist/v0/hooks.js.map +1 -0
- package/dist/v0/identity.d.ts +73 -0
- package/dist/v0/identity.d.ts.map +1 -0
- package/dist/v0/identity.js +46 -0
- package/dist/v0/identity.js.map +1 -0
- package/dist/v0/integrations.d.ts +847 -0
- package/dist/v0/integrations.d.ts.map +1 -0
- package/dist/v0/integrations.js +255 -0
- package/dist/v0/integrations.js.map +1 -0
- package/dist/v0/javascript.d.ts +160 -0
- package/dist/v0/javascript.d.ts.map +1 -0
- package/dist/v0/javascript.js +123 -0
- package/dist/v0/javascript.js.map +1 -0
- package/dist/v0/marketplace.d.ts +825 -0
- package/dist/v0/marketplace.d.ts.map +1 -0
- package/dist/v0/marketplace.js +284 -0
- package/dist/v0/marketplace.js.map +1 -0
- package/dist/v0/organization.d.ts +182 -0
- package/dist/v0/organization.d.ts.map +1 -0
- package/dist/v0/organization.js +88 -0
- package/dist/v0/organization.js.map +1 -0
- package/dist/v0/otp.d.ts +37 -0
- package/dist/v0/otp.d.ts.map +1 -0
- package/dist/v0/otp.js +29 -0
- package/dist/v0/otp.js.map +1 -0
- package/dist/v0/pi.d.ts +322 -0
- package/dist/v0/pi.d.ts.map +1 -0
- package/dist/v0/pi.js +146 -0
- package/dist/v0/pi.js.map +1 -0
- package/dist/v0/sandbox.d.ts +173 -0
- package/dist/v0/sandbox.d.ts.map +1 -0
- package/dist/v0/sandbox.js +97 -0
- package/dist/v0/sandbox.js.map +1 -0
- package/dist/v0/shared/bytes.d.ts +12 -0
- package/dist/v0/shared/bytes.d.ts.map +1 -0
- package/dist/v0/shared/bytes.js +16 -0
- package/dist/v0/shared/bytes.js.map +1 -0
- package/dist/v0/shared/datetime.d.ts +10 -0
- package/dist/v0/shared/datetime.d.ts.map +1 -0
- package/dist/v0/shared/datetime.js +14 -0
- package/dist/v0/shared/datetime.js.map +1 -0
- package/dist/v0/shared/json.d.ts +9 -0
- package/dist/v0/shared/json.d.ts.map +1 -0
- package/dist/v0/shared/json.js +14 -0
- package/dist/v0/shared/json.js.map +1 -0
- package/dist/v0/shared/pagination.d.ts +11 -0
- package/dist/v0/shared/pagination.d.ts.map +1 -0
- package/dist/v0/shared/pagination.js +11 -0
- package/dist/v0/shared/pagination.js.map +1 -0
- package/dist/v0/shared/permissions.d.ts +279 -0
- package/dist/v0/shared/permissions.d.ts.map +1 -0
- package/dist/v0/shared/permissions.js +285 -0
- package/dist/v0/shared/permissions.js.map +1 -0
- package/dist/v0/shared/pi-agent-name.d.ts +10 -0
- package/dist/v0/shared/pi-agent-name.d.ts.map +1 -0
- package/dist/v0/shared/pi-agent-name.js +11 -0
- package/dist/v0/shared/pi-agent-name.js.map +1 -0
- package/dist/v0/shared/scope.d.ts +305 -0
- package/dist/v0/shared/scope.d.ts.map +1 -0
- package/dist/v0/shared/scope.js +279 -0
- package/dist/v0/shared/scope.js.map +1 -0
- package/dist/v0/state.d.ts +512 -0
- package/dist/v0/state.d.ts.map +1 -0
- package/dist/v0/state.js +267 -0
- package/dist/v0/state.js.map +1 -0
- package/dist/v0/store.d.ts +138 -0
- package/dist/v0/store.d.ts.map +1 -0
- package/dist/v0/store.js +83 -0
- package/dist/v0/store.js.map +1 -0
- package/dist/v0/upload.d.ts +568 -0
- package/dist/v0/upload.d.ts.map +1 -0
- package/dist/v0/upload.js +109 -0
- package/dist/v0/upload.js.map +1 -0
- package/dist/v0/web.d.ts +47 -0
- package/dist/v0/web.d.ts.map +1 -0
- package/dist/v0/web.js +31 -0
- package/dist/v0/web.js.map +1 -0
- package/dist/v0/workflow.d.ts +267 -0
- package/dist/v0/workflow.d.ts.map +1 -0
- package/dist/v0/workflow.js +116 -0
- package/dist/v0/workflow.js.map +1 -0
- package/package.json +64 -3
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"api.d.ts","names":[],"sources":["../../src/v0/api.ts"],"mappings":";;;;;;cAwBa;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;oCAAA"}
|
package/dist/v0/api.js
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { organizationOperations } from "./organization.js";
|
|
2
|
+
import { accountOperations } from "./account.js";
|
|
3
|
+
import { appsOperations } from "./apps.js";
|
|
4
|
+
import { automationRouterOperations } from "./automation.js";
|
|
5
|
+
import { capabilitiesOperations } from "./capabilities.js";
|
|
6
|
+
import { eventsOperations } from "./events.js";
|
|
7
|
+
import { formsOperations } from "./forms.js";
|
|
8
|
+
import { hooksOperations } from "./hooks.js";
|
|
9
|
+
import { identityOperations } from "./identity.js";
|
|
10
|
+
import { integrationsOperations } from "./integrations.js";
|
|
11
|
+
import { javascriptOperations } from "./javascript.js";
|
|
12
|
+
import { marketplaceOperations, packagesOperations } from "./marketplace.js";
|
|
13
|
+
import { otpOperations } from "./otp.js";
|
|
14
|
+
import { piOperations } from "./pi.js";
|
|
15
|
+
import { sandboxOperations } from "./sandbox.js";
|
|
16
|
+
import { stateOperations } from "./state.js";
|
|
17
|
+
import { storeOperations } from "./store.js";
|
|
18
|
+
import { uploadOperations } from "./upload.js";
|
|
19
|
+
import { webOperations } from "./web.js";
|
|
20
|
+
import { workflowOperations } from "./workflow.js";
|
|
21
|
+
//#region src/v0/api.ts
|
|
22
|
+
/** v0 is unstable: operations and schemas may change without a new version. */
|
|
23
|
+
const backofficeApiV0 = {
|
|
24
|
+
version: "v0",
|
|
25
|
+
operations: {
|
|
26
|
+
...accountOperations,
|
|
27
|
+
...appsOperations,
|
|
28
|
+
...automationRouterOperations,
|
|
29
|
+
...capabilitiesOperations,
|
|
30
|
+
...eventsOperations,
|
|
31
|
+
...formsOperations,
|
|
32
|
+
...hooksOperations,
|
|
33
|
+
...identityOperations,
|
|
34
|
+
...integrationsOperations,
|
|
35
|
+
...javascriptOperations,
|
|
36
|
+
...marketplaceOperations,
|
|
37
|
+
...organizationOperations,
|
|
38
|
+
...otpOperations,
|
|
39
|
+
...packagesOperations,
|
|
40
|
+
...piOperations,
|
|
41
|
+
...sandboxOperations,
|
|
42
|
+
...stateOperations,
|
|
43
|
+
...storeOperations,
|
|
44
|
+
...uploadOperations,
|
|
45
|
+
...webOperations,
|
|
46
|
+
...workflowOperations
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
//#endregion
|
|
50
|
+
export { backofficeApiV0 };
|
|
51
|
+
|
|
52
|
+
//# sourceMappingURL=api.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"api.js","names":[],"sources":["../../src/v0/api.ts"],"sourcesContent":["import type { BackofficeApi } from \"../api\";\nimport { accountOperations } from \"./account\";\nimport { appsOperations } from \"./apps\";\nimport { automationRouterOperations } from \"./automation\";\nimport { capabilitiesOperations } from \"./capabilities\";\nimport { eventsOperations } from \"./events\";\nimport { formsOperations } from \"./forms\";\nimport { hooksOperations } from \"./hooks\";\nimport { identityOperations } from \"./identity\";\nimport { integrationsOperations } from \"./integrations\";\nimport { javascriptOperations } from \"./javascript\";\nimport { marketplaceOperations } from \"./marketplace\";\nimport { packagesOperations } from \"./marketplace\";\nimport { organizationOperations } from \"./organization\";\nimport { otpOperations } from \"./otp\";\nimport { piOperations } from \"./pi\";\nimport { sandboxOperations } from \"./sandbox\";\nimport { stateOperations } from \"./state\";\nimport { storeOperations } from \"./store\";\nimport { uploadOperations } from \"./upload\";\nimport { webOperations } from \"./web\";\nimport { workflowOperations } from \"./workflow\";\n\n/** v0 is unstable: operations and schemas may change without a new version. */\nexport const backofficeApiV0 = {\n version: \"v0\",\n operations: {\n ...accountOperations,\n ...appsOperations,\n ...automationRouterOperations,\n ...capabilitiesOperations,\n ...eventsOperations,\n ...formsOperations,\n ...hooksOperations,\n ...identityOperations,\n ...integrationsOperations,\n ...javascriptOperations,\n ...marketplaceOperations,\n ...organizationOperations,\n ...otpOperations,\n ...packagesOperations,\n ...piOperations,\n ...sandboxOperations,\n ...stateOperations,\n ...storeOperations,\n ...uploadOperations,\n ...webOperations,\n ...workflowOperations,\n },\n} satisfies BackofficeApi;\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;AAwBA,MAAa,kBAAkB;CAC7B,SAAS;CACT,YAAY;EACV,GAAG;EACH,GAAG;EACH,GAAG;EACH,GAAG;EACH,GAAG;EACH,GAAG;EACH,GAAG;EACH,GAAG;EACH,GAAG;EACH,GAAG;EACH,GAAG;EACH,GAAG;EACH,GAAG;EACH,GAAG;EACH,GAAG;EACH,GAAG;EACH,GAAG;EACH,GAAG;EACH,GAAG;EACH,GAAG;EACH,GAAG;CACL;AACF"}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { JsonValue } from "./shared/json.js";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
//#region src/v0/app-actions.d.ts
|
|
4
|
+
/** Issuer of every token Backoffice's auth server signs. */
|
|
5
|
+
declare const BACKOFFICE_TOKEN_ISSUER = "fragno-backoffice-auth";
|
|
6
|
+
/** Short enough that a captured request cannot be replayed later. */
|
|
7
|
+
declare const APP_ACTION_TOKEN_LIFETIME_SECONDS = 60;
|
|
8
|
+
/** The JSON body Backoffice POSTs to an app's action endpoint. */
|
|
9
|
+
declare const appActionRequestSchema: z.ZodLazy<z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
10
|
+
type: z.ZodLiteral<"ping">;
|
|
11
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
12
|
+
type: z.ZodLiteral<"action">;
|
|
13
|
+
actionId: z.ZodString;
|
|
14
|
+
input: z.ZodType<JsonValue, unknown, z.core.$ZodTypeInternals<JsonValue, unknown>>;
|
|
15
|
+
}, z.core.$strict>], "type">>;
|
|
16
|
+
type AppActionRequest = z.infer<typeof appActionRequestSchema>;
|
|
17
|
+
/**
|
|
18
|
+
* The bearer token on every action request. Its audience is the app's endpoint URL, so a request
|
|
19
|
+
* sent to one app cannot be replayed against another.
|
|
20
|
+
*/
|
|
21
|
+
declare const appActionTokenClaimsSchema: z.ZodLazy<z.ZodObject<{
|
|
22
|
+
iss: z.ZodLiteral<"fragno-backoffice-auth">;
|
|
23
|
+
aud: z.ZodString;
|
|
24
|
+
iat: z.ZodNumber;
|
|
25
|
+
exp: z.ZodNumber;
|
|
26
|
+
jti: z.ZodString;
|
|
27
|
+
sub: z.ZodString;
|
|
28
|
+
actor: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
29
|
+
kind: z.ZodLiteral<"user">;
|
|
30
|
+
userId: z.ZodString;
|
|
31
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
32
|
+
kind: z.ZodLiteral<"installation">;
|
|
33
|
+
}, z.core.$strict>], "kind">;
|
|
34
|
+
installation: z.ZodObject<{
|
|
35
|
+
appId: z.ZodString;
|
|
36
|
+
activation: z.ZodNumber;
|
|
37
|
+
externalAccount: z.ZodLazy<z.ZodObject<{
|
|
38
|
+
id: z.ZodString;
|
|
39
|
+
label: z.ZodString;
|
|
40
|
+
}, z.core.$strict>>;
|
|
41
|
+
}, z.core.$strict>;
|
|
42
|
+
scopeRestriction: z.ZodLazy<z.ZodDiscriminatedUnion<[z.ZodLazy<z.ZodObject<{
|
|
43
|
+
kind: z.ZodLiteral<"org">;
|
|
44
|
+
orgId: z.ZodString;
|
|
45
|
+
}, z.core.$strict>>, z.ZodLazy<z.ZodObject<{
|
|
46
|
+
kind: z.ZodLiteral<"user">;
|
|
47
|
+
userId: z.ZodString;
|
|
48
|
+
}, z.core.$strict>>, z.ZodLazy<z.ZodObject<{
|
|
49
|
+
kind: z.ZodLiteral<"app">;
|
|
50
|
+
owner: z.ZodLazy<z.ZodDiscriminatedUnion<[z.ZodLazy<z.ZodObject<{
|
|
51
|
+
kind: z.ZodLiteral<"org">;
|
|
52
|
+
orgId: z.ZodString;
|
|
53
|
+
}, z.core.$strict>>, z.ZodLazy<z.ZodObject<{
|
|
54
|
+
kind: z.ZodLiteral<"user">;
|
|
55
|
+
userId: z.ZodString;
|
|
56
|
+
}, z.core.$strict>>], "kind">>;
|
|
57
|
+
appId: z.ZodString;
|
|
58
|
+
}, z.core.$strict>>, z.ZodLazy<z.ZodObject<{
|
|
59
|
+
kind: z.ZodLiteral<"project">;
|
|
60
|
+
owner: z.ZodDiscriminatedUnion<[z.ZodLazy<z.ZodObject<{
|
|
61
|
+
kind: z.ZodLiteral<"org">;
|
|
62
|
+
orgId: z.ZodString;
|
|
63
|
+
}, z.core.$strict>>, z.ZodLazy<z.ZodObject<{
|
|
64
|
+
kind: z.ZodLiteral<"user">;
|
|
65
|
+
userId: z.ZodString;
|
|
66
|
+
}, z.core.$strict>>, z.ZodLazy<z.ZodObject<{
|
|
67
|
+
kind: z.ZodLiteral<"app">;
|
|
68
|
+
owner: z.ZodLazy<z.ZodDiscriminatedUnion<[z.ZodLazy<z.ZodObject<{
|
|
69
|
+
kind: z.ZodLiteral<"org">;
|
|
70
|
+
orgId: z.ZodString;
|
|
71
|
+
}, z.core.$strict>>, z.ZodLazy<z.ZodObject<{
|
|
72
|
+
kind: z.ZodLiteral<"user">;
|
|
73
|
+
userId: z.ZodString;
|
|
74
|
+
}, z.core.$strict>>], "kind">>;
|
|
75
|
+
appId: z.ZodString;
|
|
76
|
+
}, z.core.$strict>>], "kind">;
|
|
77
|
+
projectId: z.ZodString;
|
|
78
|
+
}, z.core.$strict>>], "kind">>;
|
|
79
|
+
bodySha256: z.ZodString;
|
|
80
|
+
}, z.core.$strip>>;
|
|
81
|
+
type AppActionTokenClaims = z.infer<typeof appActionTokenClaimsSchema>;
|
|
82
|
+
/** Binds a token to one body; Backoffice signs it and the app's verifier recomputes it. */
|
|
83
|
+
declare function appActionBodySha256(body: string): Promise<string>;
|
|
84
|
+
/** A request the endpoint must refuse; `status` is the HTTP status to answer with. */
|
|
85
|
+
declare class AppActionRequestError extends Error {
|
|
86
|
+
readonly status: 400 | 401 | 405;
|
|
87
|
+
constructor(status: 400 | 401 | 405, message: string);
|
|
88
|
+
}
|
|
89
|
+
type VerifiedAppActionRequest = {
|
|
90
|
+
claims: AppActionTokenClaims;
|
|
91
|
+
request: AppActionRequest;
|
|
92
|
+
};
|
|
93
|
+
/**
|
|
94
|
+
* Creates the verifier for an app's action endpoint. It checks Backoffice's signature, that the
|
|
95
|
+
* token is addressed to `endpointUrl` and unexpired, and that it signs this exact body. Signing keys
|
|
96
|
+
* come from `<backofficeOrigin>/api/auth/jwks` through `fetch` and are cached between requests.
|
|
97
|
+
*/
|
|
98
|
+
declare function createAppActionVerifier({ backofficeOrigin, endpointUrl, fetch }: {
|
|
99
|
+
backofficeOrigin: string;
|
|
100
|
+
endpointUrl: string;
|
|
101
|
+
fetch: typeof globalThis.fetch;
|
|
102
|
+
}): (request: Request) => Promise<VerifiedAppActionRequest>;
|
|
103
|
+
//#endregion
|
|
104
|
+
export { APP_ACTION_TOKEN_LIFETIME_SECONDS, AppActionRequest, AppActionRequestError, AppActionTokenClaims, BACKOFFICE_TOKEN_ISSUER, VerifiedAppActionRequest, appActionBodySha256, appActionRequestSchema, appActionTokenClaimsSchema, createAppActionVerifier };
|
|
105
|
+
//# sourceMappingURL=app-actions.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"app-actions.d.ts","names":[],"sources":["../../src/v0/app-actions.ts"],"mappings":";;;;cAQa;;cAGA;;cAGA,wBAAsB,EAAA,QAAA,EAAA,uBAAA,EAAA;;;;;;;KAWvB,mBAAmB,EAAE,aAAa;;;;;cAMjC,4BAA0B,EAAA,QAAA,EAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;KAwB3B,uBAAuB,EAAE,aAAa;;iBAG5B,oBAAoB,eAAe;;cAY5C,8BAA8B;WAChC;cAEG,yBAAyB;;KAO3B;EACV,QAAQ;EACR,SAAS;;;;;;;iBAQK,0BACd,kBACA,aACA;EAEA;EACA;EACA,cAAc,WAAW;KACtB,SAAS,YAAY,QAAQ"}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { backofficeRoutableScopeSchema } from "./shared/scope.js";
|
|
2
|
+
import { appInstallationExternalAccountSchema } from "./apps.js";
|
|
3
|
+
import { jsonValueSchema } from "./shared/json.js";
|
|
4
|
+
import { z } from "zod";
|
|
5
|
+
import { createRemoteJWKSet, customFetch, errors, jwtVerify } from "jose";
|
|
6
|
+
//#region src/v0/app-actions.ts
|
|
7
|
+
/** Issuer of every token Backoffice's auth server signs. */
|
|
8
|
+
const BACKOFFICE_TOKEN_ISSUER = "fragno-backoffice-auth";
|
|
9
|
+
/** Short enough that a captured request cannot be replayed later. */
|
|
10
|
+
const APP_ACTION_TOKEN_LIFETIME_SECONDS = 60;
|
|
11
|
+
/** The JSON body Backoffice POSTs to an app's action endpoint. */
|
|
12
|
+
const appActionRequestSchema = z.lazy(() => z.discriminatedUnion("type", [z.strictObject({ type: z.literal("ping") }), z.strictObject({
|
|
13
|
+
type: z.literal("action"),
|
|
14
|
+
actionId: z.string().min(1),
|
|
15
|
+
input: jsonValueSchema.describe("Validated against the action's declared input schema.")
|
|
16
|
+
})]));
|
|
17
|
+
/**
|
|
18
|
+
* The bearer token on every action request. Its audience is the app's endpoint URL, so a request
|
|
19
|
+
* sent to one app cannot be replayed against another.
|
|
20
|
+
*/
|
|
21
|
+
const appActionTokenClaimsSchema = z.lazy(() => z.object({
|
|
22
|
+
iss: z.literal(BACKOFFICE_TOKEN_ISSUER),
|
|
23
|
+
aud: z.string().min(1),
|
|
24
|
+
iat: z.number().int().nonnegative(),
|
|
25
|
+
exp: z.number().int().positive(),
|
|
26
|
+
/** Unique per request; apps may use it as an idempotency key. */
|
|
27
|
+
jti: z.string().min(1),
|
|
28
|
+
sub: z.string().min(1),
|
|
29
|
+
/** The Backoffice user behind the call, or the installation itself for automated calls. */
|
|
30
|
+
actor: z.discriminatedUnion("kind", [z.strictObject({
|
|
31
|
+
kind: z.literal("user"),
|
|
32
|
+
userId: z.string().min(1)
|
|
33
|
+
}), z.strictObject({ kind: z.literal("installation") })]),
|
|
34
|
+
installation: z.strictObject({
|
|
35
|
+
appId: z.string().min(1),
|
|
36
|
+
activation: z.number().int().positive(),
|
|
37
|
+
externalAccount: appInstallationExternalAccountSchema
|
|
38
|
+
}),
|
|
39
|
+
scopeRestriction: backofficeRoutableScopeSchema,
|
|
40
|
+
/** Base64url SHA-256 of the exact request body bytes. */
|
|
41
|
+
bodySha256: z.string().min(1)
|
|
42
|
+
}));
|
|
43
|
+
/** Binds a token to one body; Backoffice signs it and the app's verifier recomputes it. */
|
|
44
|
+
async function appActionBodySha256(body) {
|
|
45
|
+
const digest = new Uint8Array(await crypto.subtle.digest("SHA-256", new TextEncoder().encode(body)));
|
|
46
|
+
let binary = "";
|
|
47
|
+
for (const byte of digest) binary += String.fromCharCode(byte);
|
|
48
|
+
return btoa(binary).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
|
|
49
|
+
}
|
|
50
|
+
/** A request the endpoint must refuse; `status` is the HTTP status to answer with. */
|
|
51
|
+
var AppActionRequestError = class extends Error {
|
|
52
|
+
status;
|
|
53
|
+
constructor(status, message) {
|
|
54
|
+
super(message);
|
|
55
|
+
this.name = "AppActionRequestError";
|
|
56
|
+
this.status = status;
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
/**
|
|
60
|
+
* Creates the verifier for an app's action endpoint. It checks Backoffice's signature, that the
|
|
61
|
+
* token is addressed to `endpointUrl` and unexpired, and that it signs this exact body. Signing keys
|
|
62
|
+
* come from `<backofficeOrigin>/api/auth/jwks` through `fetch` and are cached between requests.
|
|
63
|
+
*/
|
|
64
|
+
function createAppActionVerifier({ backofficeOrigin, endpointUrl, fetch }) {
|
|
65
|
+
const jwks = createRemoteJWKSet(new URL("/api/auth/jwks", backofficeOrigin), { [customFetch]: fetch });
|
|
66
|
+
return async function verifyAppActionRequest(request) {
|
|
67
|
+
if (request.method !== "POST") throw new AppActionRequestError(405, "App action requests use POST.");
|
|
68
|
+
const token = /^Bearer (\S+)$/.exec(request.headers.get("authorization") ?? "")?.[1];
|
|
69
|
+
if (!token) throw new AppActionRequestError(401, "App action request is missing its bearer token.");
|
|
70
|
+
const body = await request.text();
|
|
71
|
+
let claims;
|
|
72
|
+
try {
|
|
73
|
+
const verified = await jwtVerify(token, jwks, {
|
|
74
|
+
issuer: BACKOFFICE_TOKEN_ISSUER,
|
|
75
|
+
audience: endpointUrl
|
|
76
|
+
});
|
|
77
|
+
claims = appActionTokenClaimsSchema.parse(verified.payload);
|
|
78
|
+
} catch (error) {
|
|
79
|
+
throw new AppActionRequestError(401, error instanceof errors.JWTExpired ? "App action token expired." : "App action token is invalid.");
|
|
80
|
+
}
|
|
81
|
+
if (claims.exp - claims.iat > 60) throw new AppActionRequestError(401, "App action token lives longer than allowed.");
|
|
82
|
+
if (await appActionBodySha256(body) !== claims.bodySha256) throw new AppActionRequestError(401, "App action token does not sign this request body.");
|
|
83
|
+
let json;
|
|
84
|
+
try {
|
|
85
|
+
json = JSON.parse(body);
|
|
86
|
+
} catch {
|
|
87
|
+
throw new AppActionRequestError(400, "App action request body is not JSON.");
|
|
88
|
+
}
|
|
89
|
+
const parsed = appActionRequestSchema.safeParse(json);
|
|
90
|
+
if (!parsed.success) throw new AppActionRequestError(400, "App action request body is malformed.");
|
|
91
|
+
return {
|
|
92
|
+
claims,
|
|
93
|
+
request: parsed.data
|
|
94
|
+
};
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
//#endregion
|
|
98
|
+
export { APP_ACTION_TOKEN_LIFETIME_SECONDS, AppActionRequestError, BACKOFFICE_TOKEN_ISSUER, appActionBodySha256, appActionRequestSchema, appActionTokenClaimsSchema, createAppActionVerifier };
|
|
99
|
+
|
|
100
|
+
//# sourceMappingURL=app-actions.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"app-actions.js","names":[],"sources":["../../src/v0/app-actions.ts"],"sourcesContent":["import { createRemoteJWKSet, customFetch, errors, jwtVerify } from \"jose\";\nimport { z } from \"zod\";\n\nimport { appInstallationExternalAccountSchema } from \"./apps\";\nimport { jsonValueSchema } from \"./shared/json\";\nimport { backofficeRoutableScopeSchema } from \"./shared/scope\";\n\n/** Issuer of every token Backoffice's auth server signs. */\nexport const BACKOFFICE_TOKEN_ISSUER = \"fragno-backoffice-auth\";\n\n/** Short enough that a captured request cannot be replayed later. */\nexport const APP_ACTION_TOKEN_LIFETIME_SECONDS = 60;\n\n/** The JSON body Backoffice POSTs to an app's action endpoint. */\nexport const appActionRequestSchema = z.lazy(() =>\n z.discriminatedUnion(\"type\", [\n // integrations.verify proves the endpoint accepts Backoffice's signature without running an action.\n z.strictObject({ type: z.literal(\"ping\") }),\n z.strictObject({\n type: z.literal(\"action\"),\n actionId: z.string().min(1),\n input: jsonValueSchema.describe(\"Validated against the action's declared input schema.\"),\n }),\n ]),\n);\nexport type AppActionRequest = z.infer<typeof appActionRequestSchema>;\n\n/**\n * The bearer token on every action request. Its audience is the app's endpoint URL, so a request\n * sent to one app cannot be replayed against another.\n */\nexport const appActionTokenClaimsSchema = z.lazy(() =>\n z.object({\n iss: z.literal(BACKOFFICE_TOKEN_ISSUER),\n aud: z.string().min(1),\n iat: z.number().int().nonnegative(),\n exp: z.number().int().positive(),\n /** Unique per request; apps may use it as an idempotency key. */\n jti: z.string().min(1),\n sub: z.string().min(1),\n /** The Backoffice user behind the call, or the installation itself for automated calls. */\n actor: z.discriminatedUnion(\"kind\", [\n z.strictObject({ kind: z.literal(\"user\"), userId: z.string().min(1) }),\n z.strictObject({ kind: z.literal(\"installation\") }),\n ]),\n installation: z.strictObject({\n appId: z.string().min(1),\n activation: z.number().int().positive(),\n externalAccount: appInstallationExternalAccountSchema,\n }),\n scopeRestriction: backofficeRoutableScopeSchema,\n /** Base64url SHA-256 of the exact request body bytes. */\n bodySha256: z.string().min(1),\n }),\n);\nexport type AppActionTokenClaims = z.infer<typeof appActionTokenClaimsSchema>;\n\n/** Binds a token to one body; Backoffice signs it and the app's verifier recomputes it. */\nexport async function appActionBodySha256(body: string): Promise<string> {\n const digest = new Uint8Array(\n await crypto.subtle.digest(\"SHA-256\", new TextEncoder().encode(body)),\n );\n let binary = \"\";\n for (const byte of digest) {\n binary += String.fromCharCode(byte);\n }\n return btoa(binary).replace(/\\+/g, \"-\").replace(/\\//g, \"_\").replace(/=+$/, \"\");\n}\n\n/** A request the endpoint must refuse; `status` is the HTTP status to answer with. */\nexport class AppActionRequestError extends Error {\n readonly status: 400 | 401 | 405;\n\n constructor(status: 400 | 401 | 405, message: string) {\n super(message);\n this.name = \"AppActionRequestError\";\n this.status = status;\n }\n}\n\nexport type VerifiedAppActionRequest = {\n claims: AppActionTokenClaims;\n request: AppActionRequest;\n};\n\n/**\n * Creates the verifier for an app's action endpoint. It checks Backoffice's signature, that the\n * token is addressed to `endpointUrl` and unexpired, and that it signs this exact body. Signing keys\n * come from `<backofficeOrigin>/api/auth/jwks` through `fetch` and are cached between requests.\n */\nexport function createAppActionVerifier({\n backofficeOrigin,\n endpointUrl,\n fetch,\n}: {\n backofficeOrigin: string;\n endpointUrl: string;\n fetch: typeof globalThis.fetch;\n}): (request: Request) => Promise<VerifiedAppActionRequest> {\n const jwks = createRemoteJWKSet(new URL(\"/api/auth/jwks\", backofficeOrigin), {\n [customFetch]: fetch,\n });\n return async function verifyAppActionRequest(request) {\n if (request.method !== \"POST\") {\n throw new AppActionRequestError(405, \"App action requests use POST.\");\n }\n const token = /^Bearer (\\S+)$/.exec(request.headers.get(\"authorization\") ?? \"\")?.[1];\n if (!token) {\n throw new AppActionRequestError(401, \"App action request is missing its bearer token.\");\n }\n const body = await request.text();\n let claims: AppActionTokenClaims;\n try {\n const verified = await jwtVerify(token, jwks, {\n issuer: BACKOFFICE_TOKEN_ISSUER,\n audience: endpointUrl,\n });\n claims = appActionTokenClaimsSchema.parse(verified.payload);\n } catch (error) {\n throw new AppActionRequestError(\n 401,\n error instanceof errors.JWTExpired\n ? \"App action token expired.\"\n : \"App action token is invalid.\",\n );\n }\n if (claims.exp - claims.iat > APP_ACTION_TOKEN_LIFETIME_SECONDS) {\n throw new AppActionRequestError(401, \"App action token lives longer than allowed.\");\n }\n if ((await appActionBodySha256(body)) !== claims.bodySha256) {\n throw new AppActionRequestError(401, \"App action token does not sign this request body.\");\n }\n let json: unknown;\n try {\n json = JSON.parse(body);\n } catch {\n throw new AppActionRequestError(400, \"App action request body is not JSON.\");\n }\n const parsed = appActionRequestSchema.safeParse(json);\n if (!parsed.success) {\n throw new AppActionRequestError(400, \"App action request body is malformed.\");\n }\n return { claims, request: parsed.data };\n };\n}\n"],"mappings":"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"}
|