@thirdweb-dev/service-utils 0.0.0-dev-d4941e0-20230714040644 → 0.0.0-dev-f8121eb-20230714072905

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@thirdweb-dev/service-utils",
3
- "version": "0.0.0-dev-d4941e0-20230714040644",
3
+ "version": "0.0.0-dev-f8121eb-20230714072905",
4
4
  "main": "dist/thirdweb-dev-service-utils.cjs.js",
5
5
  "module": "dist/thirdweb-dev-service-utils.esm.js",
6
6
  "exports": {
@@ -8,6 +8,14 @@
8
8
  "module": "./dist/thirdweb-dev-service-utils.esm.js",
9
9
  "default": "./dist/thirdweb-dev-service-utils.cjs.js"
10
10
  },
11
+ "./node": {
12
+ "module": "./node/dist/thirdweb-dev-service-utils-node.esm.js",
13
+ "default": "./node/dist/thirdweb-dev-service-utils-node.cjs.js"
14
+ },
15
+ "./cf-worker": {
16
+ "module": "./cf-worker/dist/thirdweb-dev-service-utils-cf-worker.esm.js",
17
+ "default": "./cf-worker/dist/thirdweb-dev-service-utils-cf-worker.cjs.js"
18
+ },
11
19
  "./package.json": "./package.json"
12
20
  },
13
21
  "repository": "https://github.com/thirdweb-dev/js/tree/main/packages/pay",
@@ -17,29 +25,32 @@
17
25
  },
18
26
  "author": "thirdweb eng <eng@thirdweb.com>",
19
27
  "files": [
20
- "dist/"
28
+ "dist/",
29
+ "node/",
30
+ "cf-worker/"
21
31
  ],
22
32
  "preconstruct": {
23
33
  "entrypoints": [
24
- "index.ts"
34
+ "index.ts",
35
+ "cf-worker/index.ts",
36
+ "node/index.ts"
25
37
  ],
26
38
  "exports": true
27
39
  },
28
40
  "sideEffects": false,
29
41
  "devDependencies": {
30
42
  "@cloudflare/workers-types": "^4.20230710.0",
43
+ "@preconstruct/cli": "^2.8.1",
44
+ "@thirdweb-dev/tsconfig": "^0.1.7",
45
+ "@types/jest": "^29.5.2",
31
46
  "@types/node": "^20.4.1",
32
47
  "@typescript-eslint/eslint-plugin": "^6.0.0",
33
48
  "@typescript-eslint/parser": "^6.0.0",
34
49
  "eslint": "^8.44.0",
35
50
  "eslint-config-prettier": "^8.8.0",
36
- "lint-staged": "^13.2.3",
37
- "prettier": "^3.0.0",
38
- "sort-package-json": "^2.5.1",
39
- "ts-node": "^10.9.1",
40
- "@preconstruct/cli": "^2.7.0",
41
- "@thirdweb-dev/tsconfig": "^0.1.7",
42
51
  "eslint-config-thirdweb": "^0.1.5",
52
+ "jest": "^29.4.3",
53
+ "prettier": "^3.0.0",
43
54
  "typescript": "^5.1.6"
44
55
  },
45
56
  "scripts": {
@@ -48,6 +59,7 @@
48
59
  "fix": "eslint src/ --fix",
49
60
  "clean": "rm -rf dist/",
50
61
  "build": "tsc && preconstruct build",
51
- "push": "yalc push"
62
+ "push": "yalc push",
63
+ "test": "jest"
52
64
  }
53
65
  }
@@ -1,4 +0,0 @@
1
- import { AuthorizationResponse, AuthorizeCFWorkerOptions, AuthorizeNodeServiceOptions } from "./types";
2
- export declare function authorizeCFWorkerService(options: AuthorizeCFWorkerOptions): Promise<AuthorizationResponse>;
3
- export declare function authorizeNodeService(options: AuthorizeNodeServiceOptions): Promise<AuthorizationResponse>;
4
- //# sourceMappingURL=index.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"../../../../src/auth","sources":["index.ts"],"names":[],"mappings":"AAAA,OAAO,EAIL,qBAAqB,EAErB,wBAAwB,EACxB,2BAA2B,EAE5B,MAAM,SAAS,CAAC;AAEjB,wBAAsB,wBAAwB,CAC5C,OAAO,EAAE,wBAAwB,kCAkClC;AAED,wBAAsB,oBAAoB,CACxC,OAAO,EAAE,2BAA2B,kCASrC"}
@@ -1,62 +0,0 @@
1
- import { KVNamespace, ExecutionContext } from "@cloudflare/workers-types";
2
- import { ServiceName } from "../types";
3
- export interface AuthOptions {
4
- clientId: string;
5
- secretHash?: string;
6
- bundleId?: string;
7
- origin?: string;
8
- }
9
- export interface AuthorizeCFWorkerOptions {
10
- ctx: ExecutionContext;
11
- kvStore: KVNamespace<string>;
12
- authOptions: AuthOptions;
13
- serviceConfig: ServiceConfiguration;
14
- validations: AuthorizationValidations;
15
- }
16
- export interface AuthorizeNodeServiceOptions {
17
- authOptions: AuthOptions;
18
- serviceConfig: ServiceConfiguration;
19
- validations: AuthorizationValidations;
20
- }
21
- export interface ServiceConfiguration {
22
- apiUrl: string;
23
- scope: ServiceName;
24
- serviceKey: string;
25
- cachedKey?: ApiKey;
26
- cacheTtl?: number;
27
- onRefetchComplete?: (key: ApiKey) => void;
28
- }
29
- export interface AuthorizationValidations {
30
- serviceTargetAddresses?: string[];
31
- serviceAction?: string;
32
- }
33
- export interface AuthorizationResponse {
34
- authorized: boolean;
35
- errorMessage?: string;
36
- errorCode?: string;
37
- statusCode?: number;
38
- data?: ApiKey;
39
- }
40
- export interface ApiResponse {
41
- data: ApiKey | null;
42
- error: {
43
- code: string;
44
- statusCode: number;
45
- message: string;
46
- };
47
- }
48
- export interface ApiKey {
49
- id: string;
50
- key: string;
51
- creatorWalletAddress: string;
52
- secretHash: string;
53
- walletAddresses: string[];
54
- domains: string[];
55
- bundleIds: string[];
56
- services: {
57
- name: string;
58
- targetAddresses: string[];
59
- actions: string[];
60
- }[];
61
- }
62
- //# sourceMappingURL=types.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"../../../../src/auth","sources":["types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AAC1E,OAAO,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAEvC,MAAM,WAAW,WAAW;IAC1B,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,wBAAwB;IACvC,GAAG,EAAE,gBAAgB,CAAC;IACtB,OAAO,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IAC7B,WAAW,EAAE,WAAW,CAAC;IACzB,aAAa,EAAE,oBAAoB,CAAC;IACpC,WAAW,EAAE,wBAAwB,CAAC;CACvC;AAED,MAAM,WAAW,2BAA2B;IAC1C,WAAW,EAAE,WAAW,CAAC;IACzB,aAAa,EAAE,oBAAoB,CAAC;IACpC,WAAW,EAAE,wBAAwB,CAAC;CACvC;AAED,MAAM,WAAW,oBAAoB;IACnC,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,WAAW,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,iBAAiB,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;CAC3C;AAED,MAAM,WAAW,wBAAwB;IACvC,sBAAsB,CAAC,EAAE,MAAM,EAAE,CAAC;IAClC,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,qBAAqB;IACpC,UAAU,EAAE,OAAO,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,KAAK,EAAE;QACL,IAAI,EAAE,MAAM,CAAC;QACb,UAAU,EAAE,MAAM,CAAC;QACnB,OAAO,EAAE,MAAM,CAAC;KACjB,CAAC;CACH;AAED,MAAM,WAAW,MAAM;IACrB,EAAE,EAAE,MAAM,CAAC;IACX,GAAG,EAAE,MAAM,CAAC;IACZ,oBAAoB,EAAE,MAAM,CAAC;IAC7B,UAAU,EAAE,MAAM,CAAC;IACnB,eAAe,EAAE,MAAM,EAAE,CAAC;IAC1B,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,QAAQ,EAAE;QACR,IAAI,EAAE,MAAM,CAAC;QACb,eAAe,EAAE,MAAM,EAAE,CAAC;QAC1B,OAAO,EAAE,MAAM,EAAE,CAAC;KACnB,EAAE,CAAC;CACL"}
@@ -1,4 +0,0 @@
1
- export declare function getServiceByName(name: string): import("./types").Service | undefined;
2
- export * from "./types";
3
- export * from "./auth";
4
- //# sourceMappingURL=index.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"../../../src","sources":["index.ts"],"names":[],"mappings":"AAEA,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,yCAE5C;AAED,cAAc,SAAS,CAAC;AACxB,cAAc,QAAQ,CAAC"}
@@ -1,15 +0,0 @@
1
- export declare const SERVICE_NAMES: readonly ["bundler", "rpc", "storage"];
2
- export declare const SERVICES: Service[];
3
- export type ServiceName = (typeof SERVICE_NAMES)[number];
4
- export interface ServiceAction {
5
- name: string;
6
- title: string;
7
- description?: string;
8
- }
9
- export interface Service {
10
- name: ServiceName;
11
- title: string;
12
- description?: string;
13
- actions: ServiceAction[];
14
- }
15
- //# sourceMappingURL=types.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"../../../src","sources":["types.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,aAAa,wCAAyC,CAAC;AAEpE,eAAO,MAAM,QAAQ,EAAE,OAAO,EAgC7B,CAAC;AAEF,MAAM,MAAM,WAAW,GAAG,CAAC,OAAO,aAAa,CAAC,CAAC,MAAM,CAAC,CAAC;AAEzD,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,OAAO;IACtB,IAAI,EAAE,WAAW,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,aAAa,EAAE,CAAC;CAC1B"}
@@ -1,2 +0,0 @@
1
- export * from "./declarations/src/index";
2
- //# sourceMappingURL=thirdweb-dev-service-utils.cjs.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"thirdweb-dev-service-utils.cjs.d.ts","sourceRoot":"","sources":["./declarations/src/index.d.ts"],"names":[],"mappings":"AAAA"}
@@ -1,279 +0,0 @@
1
- 'use strict';
2
-
3
- Object.defineProperty(exports, '__esModule', { value: true });
4
-
5
- const SERVICE_NAMES = ["bundler", "rpc", "storage"];
6
- const SERVICES = [{
7
- name: "storage",
8
- title: "Storage",
9
- description: "IPFS Upload and Download",
10
- actions: [{
11
- name: "read",
12
- title: "Download",
13
- description: "Download a file from Storage"
14
- }, {
15
- name: "write",
16
- title: "Upload",
17
- description: "Upload a file to Storage"
18
- }]
19
- }, {
20
- name: "rpc",
21
- title: "RPC",
22
- description: "Accelerated RPC Edge",
23
- // all actions allowed
24
- actions: []
25
- }, {
26
- name: "bundler",
27
- title: "Smart Wallets",
28
- description: "Bundler & Paymaster services",
29
- // all actions allowed
30
- actions: []
31
- }];
32
-
33
- async function authorizeCFWorkerService(options) {
34
- const {
35
- kvStore,
36
- ctx,
37
- authOptions,
38
- serviceConfig,
39
- validations
40
- } = options;
41
- const {
42
- clientId
43
- } = authOptions;
44
- let cachedKey;
45
-
46
- // first, check if the key is in KV
47
- try {
48
- const kvKey = await kvStore.get(clientId);
49
- if (kvKey) {
50
- cachedKey = JSON.parse(kvKey);
51
- }
52
- } catch (err) {
53
- // ignore JSON parse, assuming not valid
54
- }
55
- const updateKv = async keyData => {
56
- kvStore.put(clientId, JSON.stringify(keyData), {
57
- expirationTtl: serviceConfig.cacheTtl || 60
58
- });
59
- };
60
- return authorize({
61
- authOptions,
62
- serviceConfig: {
63
- ...serviceConfig,
64
- cachedKey,
65
- onRefetchComplete: keyData => {
66
- ctx.waitUntil(updateKv(keyData));
67
- }
68
- },
69
- validations
70
- });
71
- }
72
- async function authorizeNodeService(options) {
73
- const {
74
- authOptions,
75
- serviceConfig,
76
- validations
77
- } = options;
78
- return authorize({
79
- authOptions,
80
- serviceConfig,
81
- validations
82
- });
83
- }
84
-
85
- /**
86
- * Authorizes a request for a given client ID
87
- *
88
- * @returns The Promise AuthorizationResponse
89
- */
90
- async function authorize(options) {
91
- try {
92
- const {
93
- authOptions,
94
- serviceConfig,
95
- validations
96
- } = options;
97
- const {
98
- clientId
99
- } = authOptions;
100
- const {
101
- apiUrl,
102
- scope,
103
- serviceKey,
104
- cachedKey,
105
- onRefetchComplete
106
- } = serviceConfig;
107
- let keyData = cachedKey;
108
-
109
- // no cached key, re-fetch from API
110
- if (!keyData) {
111
- const response = await fetch(`${apiUrl}/v1/keys/use?scope=${scope}&clientId=${clientId}`, {
112
- method: "GET",
113
- headers: {
114
- "x-service-api-key": serviceKey,
115
- "content-type": "application/json"
116
- }
117
- });
118
- const apiResponse = await response.json();
119
- if (!response.ok) {
120
- const {
121
- error
122
- } = apiResponse;
123
- return {
124
- authorized: false,
125
- errorMessage: error.message,
126
- errorCode: error.code || "",
127
- statusCode: error.statusCode || 401
128
- };
129
- }
130
- keyData = apiResponse.data;
131
- if (onRefetchComplete) {
132
- onRefetchComplete(keyData);
133
- }
134
- }
135
-
136
- //
137
- // Run validations
138
- //
139
- const authResponse = authAccess(authOptions, keyData);
140
- if (!authResponse?.authorized) {
141
- return authResponse;
142
- }
143
- const authzResponse = authzServices(validations, keyData, scope);
144
- if (!authzResponse?.authorized) {
145
- return authzResponse;
146
- }
147
- // FIXME: validate bundleId
148
-
149
- return {
150
- authorized: true,
151
- data: keyData
152
- };
153
- } catch (err) {
154
- console.error("Failed to authorize this key.", err);
155
- return {
156
- authorized: false,
157
- errorMessage: "Internal error",
158
- errorCode: "INTERNAL_ERROR",
159
- statusCode: 500
160
- };
161
- }
162
- }
163
- function authAccess(authOptions, apiKey) {
164
- const {
165
- origin,
166
- secretHash: providedSecretHash
167
- } = authOptions;
168
- const {
169
- domains,
170
- secretHash
171
- } = apiKey;
172
- if (providedSecretHash) {
173
- if (secretHash !== providedSecretHash) {
174
- return {
175
- authorized: false,
176
- errorMessage: "The secret is invalid.",
177
- errorCode: "SECRET_INVALID",
178
- statusCode: 401
179
- };
180
- }
181
- return {
182
- authorized: true
183
- };
184
- }
185
-
186
- // validate domains
187
- if (origin) {
188
- if (
189
- // find matching domain, or if all domains allowed
190
- domains.find(d => {
191
- if (d === "*") {
192
- return true;
193
- }
194
-
195
- // If the allowedDomain has a wildcard,
196
- // we'll check that the ending of our domain matches the wildcard
197
- if (d.startsWith("*.")) {
198
- const domainRoot = d.slice(2);
199
- return origin.endsWith(domainRoot);
200
- }
201
-
202
- // If there's no wildcard, we'll check for an exact match
203
- return d === origin;
204
- })) {
205
- return {
206
- authorized: true
207
- };
208
- }
209
- return {
210
- authorized: false,
211
- errorMessage: "The origin is not authorized for this key.",
212
- errorCode: "ORIGIN_UNAUTHORIZED",
213
- statusCode: 401
214
- };
215
- }
216
-
217
- // FIXME: validate bundle id
218
- return {
219
- authorized: false,
220
- errorMessage: "The keys are invalid.",
221
- errorCode: "UNAUTHORIZED",
222
- statusCode: 401
223
- };
224
- }
225
- function authzServices(validations, apiKey, scope) {
226
- const {
227
- services
228
- } = apiKey;
229
- const {
230
- serviceTargetAddresses,
231
- serviceAction
232
- } = validations;
233
-
234
- // validate services
235
- const service = services.find(srv => srv.name === scope);
236
- if (!service) {
237
- return {
238
- authorized: false,
239
- errorMessage: `The service "${scope}" is not authorized for this key.`,
240
- errorCode: "SERVICE_UNAUTHORIZED",
241
- statusCode: 403
242
- };
243
- }
244
-
245
- // validate service actions
246
- if (serviceAction) {
247
- if (!service.actions.includes(serviceAction)) {
248
- return {
249
- authorized: false,
250
- errorMessage: `The service "${scope}" action "${serviceAction}" is not authorized for this key.`,
251
- errorCode: "SERVICE_ACTION_UNAUTHORIZED",
252
- statusCode: 403
253
- };
254
- }
255
- }
256
-
257
- // validate service target addresses
258
- if (serviceTargetAddresses && !service.targetAddresses.find(addr => addr === "*" || serviceTargetAddresses.includes(addr))) {
259
- return {
260
- authorized: false,
261
- errorMessage: `The service "${scope}" target address is not authorized for this key.`,
262
- errorCode: "SERVICE_TARGET_ADDRESS_UNAUTHORIZED",
263
- statusCode: 403
264
- };
265
- }
266
- return {
267
- authorized: true
268
- };
269
- }
270
-
271
- function getServiceByName(name) {
272
- return SERVICES.find(srv => srv.name === name);
273
- }
274
-
275
- exports.SERVICES = SERVICES;
276
- exports.SERVICE_NAMES = SERVICE_NAMES;
277
- exports.authorizeCFWorkerService = authorizeCFWorkerService;
278
- exports.authorizeNodeService = authorizeNodeService;
279
- exports.getServiceByName = getServiceByName;
@@ -1,7 +0,0 @@
1
- 'use strict';
2
-
3
- if (process.env.NODE_ENV === "production") {
4
- module.exports = require("./thirdweb-dev-service-utils.cjs.prod.js");
5
- } else {
6
- module.exports = require("./thirdweb-dev-service-utils.cjs.dev.js");
7
- }
@@ -1,279 +0,0 @@
1
- 'use strict';
2
-
3
- Object.defineProperty(exports, '__esModule', { value: true });
4
-
5
- const SERVICE_NAMES = ["bundler", "rpc", "storage"];
6
- const SERVICES = [{
7
- name: "storage",
8
- title: "Storage",
9
- description: "IPFS Upload and Download",
10
- actions: [{
11
- name: "read",
12
- title: "Download",
13
- description: "Download a file from Storage"
14
- }, {
15
- name: "write",
16
- title: "Upload",
17
- description: "Upload a file to Storage"
18
- }]
19
- }, {
20
- name: "rpc",
21
- title: "RPC",
22
- description: "Accelerated RPC Edge",
23
- // all actions allowed
24
- actions: []
25
- }, {
26
- name: "bundler",
27
- title: "Smart Wallets",
28
- description: "Bundler & Paymaster services",
29
- // all actions allowed
30
- actions: []
31
- }];
32
-
33
- async function authorizeCFWorkerService(options) {
34
- const {
35
- kvStore,
36
- ctx,
37
- authOptions,
38
- serviceConfig,
39
- validations
40
- } = options;
41
- const {
42
- clientId
43
- } = authOptions;
44
- let cachedKey;
45
-
46
- // first, check if the key is in KV
47
- try {
48
- const kvKey = await kvStore.get(clientId);
49
- if (kvKey) {
50
- cachedKey = JSON.parse(kvKey);
51
- }
52
- } catch (err) {
53
- // ignore JSON parse, assuming not valid
54
- }
55
- const updateKv = async keyData => {
56
- kvStore.put(clientId, JSON.stringify(keyData), {
57
- expirationTtl: serviceConfig.cacheTtl || 60
58
- });
59
- };
60
- return authorize({
61
- authOptions,
62
- serviceConfig: {
63
- ...serviceConfig,
64
- cachedKey,
65
- onRefetchComplete: keyData => {
66
- ctx.waitUntil(updateKv(keyData));
67
- }
68
- },
69
- validations
70
- });
71
- }
72
- async function authorizeNodeService(options) {
73
- const {
74
- authOptions,
75
- serviceConfig,
76
- validations
77
- } = options;
78
- return authorize({
79
- authOptions,
80
- serviceConfig,
81
- validations
82
- });
83
- }
84
-
85
- /**
86
- * Authorizes a request for a given client ID
87
- *
88
- * @returns The Promise AuthorizationResponse
89
- */
90
- async function authorize(options) {
91
- try {
92
- const {
93
- authOptions,
94
- serviceConfig,
95
- validations
96
- } = options;
97
- const {
98
- clientId
99
- } = authOptions;
100
- const {
101
- apiUrl,
102
- scope,
103
- serviceKey,
104
- cachedKey,
105
- onRefetchComplete
106
- } = serviceConfig;
107
- let keyData = cachedKey;
108
-
109
- // no cached key, re-fetch from API
110
- if (!keyData) {
111
- const response = await fetch(`${apiUrl}/v1/keys/use?scope=${scope}&clientId=${clientId}`, {
112
- method: "GET",
113
- headers: {
114
- "x-service-api-key": serviceKey,
115
- "content-type": "application/json"
116
- }
117
- });
118
- const apiResponse = await response.json();
119
- if (!response.ok) {
120
- const {
121
- error
122
- } = apiResponse;
123
- return {
124
- authorized: false,
125
- errorMessage: error.message,
126
- errorCode: error.code || "",
127
- statusCode: error.statusCode || 401
128
- };
129
- }
130
- keyData = apiResponse.data;
131
- if (onRefetchComplete) {
132
- onRefetchComplete(keyData);
133
- }
134
- }
135
-
136
- //
137
- // Run validations
138
- //
139
- const authResponse = authAccess(authOptions, keyData);
140
- if (!authResponse?.authorized) {
141
- return authResponse;
142
- }
143
- const authzResponse = authzServices(validations, keyData, scope);
144
- if (!authzResponse?.authorized) {
145
- return authzResponse;
146
- }
147
- // FIXME: validate bundleId
148
-
149
- return {
150
- authorized: true,
151
- data: keyData
152
- };
153
- } catch (err) {
154
- console.error("Failed to authorize this key.", err);
155
- return {
156
- authorized: false,
157
- errorMessage: "Internal error",
158
- errorCode: "INTERNAL_ERROR",
159
- statusCode: 500
160
- };
161
- }
162
- }
163
- function authAccess(authOptions, apiKey) {
164
- const {
165
- origin,
166
- secretHash: providedSecretHash
167
- } = authOptions;
168
- const {
169
- domains,
170
- secretHash
171
- } = apiKey;
172
- if (providedSecretHash) {
173
- if (secretHash !== providedSecretHash) {
174
- return {
175
- authorized: false,
176
- errorMessage: "The secret is invalid.",
177
- errorCode: "SECRET_INVALID",
178
- statusCode: 401
179
- };
180
- }
181
- return {
182
- authorized: true
183
- };
184
- }
185
-
186
- // validate domains
187
- if (origin) {
188
- if (
189
- // find matching domain, or if all domains allowed
190
- domains.find(d => {
191
- if (d === "*") {
192
- return true;
193
- }
194
-
195
- // If the allowedDomain has a wildcard,
196
- // we'll check that the ending of our domain matches the wildcard
197
- if (d.startsWith("*.")) {
198
- const domainRoot = d.slice(2);
199
- return origin.endsWith(domainRoot);
200
- }
201
-
202
- // If there's no wildcard, we'll check for an exact match
203
- return d === origin;
204
- })) {
205
- return {
206
- authorized: true
207
- };
208
- }
209
- return {
210
- authorized: false,
211
- errorMessage: "The origin is not authorized for this key.",
212
- errorCode: "ORIGIN_UNAUTHORIZED",
213
- statusCode: 401
214
- };
215
- }
216
-
217
- // FIXME: validate bundle id
218
- return {
219
- authorized: false,
220
- errorMessage: "The keys are invalid.",
221
- errorCode: "UNAUTHORIZED",
222
- statusCode: 401
223
- };
224
- }
225
- function authzServices(validations, apiKey, scope) {
226
- const {
227
- services
228
- } = apiKey;
229
- const {
230
- serviceTargetAddresses,
231
- serviceAction
232
- } = validations;
233
-
234
- // validate services
235
- const service = services.find(srv => srv.name === scope);
236
- if (!service) {
237
- return {
238
- authorized: false,
239
- errorMessage: `The service "${scope}" is not authorized for this key.`,
240
- errorCode: "SERVICE_UNAUTHORIZED",
241
- statusCode: 403
242
- };
243
- }
244
-
245
- // validate service actions
246
- if (serviceAction) {
247
- if (!service.actions.includes(serviceAction)) {
248
- return {
249
- authorized: false,
250
- errorMessage: `The service "${scope}" action "${serviceAction}" is not authorized for this key.`,
251
- errorCode: "SERVICE_ACTION_UNAUTHORIZED",
252
- statusCode: 403
253
- };
254
- }
255
- }
256
-
257
- // validate service target addresses
258
- if (serviceTargetAddresses && !service.targetAddresses.find(addr => addr === "*" || serviceTargetAddresses.includes(addr))) {
259
- return {
260
- authorized: false,
261
- errorMessage: `The service "${scope}" target address is not authorized for this key.`,
262
- errorCode: "SERVICE_TARGET_ADDRESS_UNAUTHORIZED",
263
- statusCode: 403
264
- };
265
- }
266
- return {
267
- authorized: true
268
- };
269
- }
270
-
271
- function getServiceByName(name) {
272
- return SERVICES.find(srv => srv.name === name);
273
- }
274
-
275
- exports.SERVICES = SERVICES;
276
- exports.SERVICE_NAMES = SERVICE_NAMES;
277
- exports.authorizeCFWorkerService = authorizeCFWorkerService;
278
- exports.authorizeNodeService = authorizeNodeService;
279
- exports.getServiceByName = getServiceByName;
@@ -1,271 +0,0 @@
1
- const SERVICE_NAMES = ["bundler", "rpc", "storage"];
2
- const SERVICES = [{
3
- name: "storage",
4
- title: "Storage",
5
- description: "IPFS Upload and Download",
6
- actions: [{
7
- name: "read",
8
- title: "Download",
9
- description: "Download a file from Storage"
10
- }, {
11
- name: "write",
12
- title: "Upload",
13
- description: "Upload a file to Storage"
14
- }]
15
- }, {
16
- name: "rpc",
17
- title: "RPC",
18
- description: "Accelerated RPC Edge",
19
- // all actions allowed
20
- actions: []
21
- }, {
22
- name: "bundler",
23
- title: "Smart Wallets",
24
- description: "Bundler & Paymaster services",
25
- // all actions allowed
26
- actions: []
27
- }];
28
-
29
- async function authorizeCFWorkerService(options) {
30
- const {
31
- kvStore,
32
- ctx,
33
- authOptions,
34
- serviceConfig,
35
- validations
36
- } = options;
37
- const {
38
- clientId
39
- } = authOptions;
40
- let cachedKey;
41
-
42
- // first, check if the key is in KV
43
- try {
44
- const kvKey = await kvStore.get(clientId);
45
- if (kvKey) {
46
- cachedKey = JSON.parse(kvKey);
47
- }
48
- } catch (err) {
49
- // ignore JSON parse, assuming not valid
50
- }
51
- const updateKv = async keyData => {
52
- kvStore.put(clientId, JSON.stringify(keyData), {
53
- expirationTtl: serviceConfig.cacheTtl || 60
54
- });
55
- };
56
- return authorize({
57
- authOptions,
58
- serviceConfig: {
59
- ...serviceConfig,
60
- cachedKey,
61
- onRefetchComplete: keyData => {
62
- ctx.waitUntil(updateKv(keyData));
63
- }
64
- },
65
- validations
66
- });
67
- }
68
- async function authorizeNodeService(options) {
69
- const {
70
- authOptions,
71
- serviceConfig,
72
- validations
73
- } = options;
74
- return authorize({
75
- authOptions,
76
- serviceConfig,
77
- validations
78
- });
79
- }
80
-
81
- /**
82
- * Authorizes a request for a given client ID
83
- *
84
- * @returns The Promise AuthorizationResponse
85
- */
86
- async function authorize(options) {
87
- try {
88
- const {
89
- authOptions,
90
- serviceConfig,
91
- validations
92
- } = options;
93
- const {
94
- clientId
95
- } = authOptions;
96
- const {
97
- apiUrl,
98
- scope,
99
- serviceKey,
100
- cachedKey,
101
- onRefetchComplete
102
- } = serviceConfig;
103
- let keyData = cachedKey;
104
-
105
- // no cached key, re-fetch from API
106
- if (!keyData) {
107
- const response = await fetch(`${apiUrl}/v1/keys/use?scope=${scope}&clientId=${clientId}`, {
108
- method: "GET",
109
- headers: {
110
- "x-service-api-key": serviceKey,
111
- "content-type": "application/json"
112
- }
113
- });
114
- const apiResponse = await response.json();
115
- if (!response.ok) {
116
- const {
117
- error
118
- } = apiResponse;
119
- return {
120
- authorized: false,
121
- errorMessage: error.message,
122
- errorCode: error.code || "",
123
- statusCode: error.statusCode || 401
124
- };
125
- }
126
- keyData = apiResponse.data;
127
- if (onRefetchComplete) {
128
- onRefetchComplete(keyData);
129
- }
130
- }
131
-
132
- //
133
- // Run validations
134
- //
135
- const authResponse = authAccess(authOptions, keyData);
136
- if (!authResponse?.authorized) {
137
- return authResponse;
138
- }
139
- const authzResponse = authzServices(validations, keyData, scope);
140
- if (!authzResponse?.authorized) {
141
- return authzResponse;
142
- }
143
- // FIXME: validate bundleId
144
-
145
- return {
146
- authorized: true,
147
- data: keyData
148
- };
149
- } catch (err) {
150
- console.error("Failed to authorize this key.", err);
151
- return {
152
- authorized: false,
153
- errorMessage: "Internal error",
154
- errorCode: "INTERNAL_ERROR",
155
- statusCode: 500
156
- };
157
- }
158
- }
159
- function authAccess(authOptions, apiKey) {
160
- const {
161
- origin,
162
- secretHash: providedSecretHash
163
- } = authOptions;
164
- const {
165
- domains,
166
- secretHash
167
- } = apiKey;
168
- if (providedSecretHash) {
169
- if (secretHash !== providedSecretHash) {
170
- return {
171
- authorized: false,
172
- errorMessage: "The secret is invalid.",
173
- errorCode: "SECRET_INVALID",
174
- statusCode: 401
175
- };
176
- }
177
- return {
178
- authorized: true
179
- };
180
- }
181
-
182
- // validate domains
183
- if (origin) {
184
- if (
185
- // find matching domain, or if all domains allowed
186
- domains.find(d => {
187
- if (d === "*") {
188
- return true;
189
- }
190
-
191
- // If the allowedDomain has a wildcard,
192
- // we'll check that the ending of our domain matches the wildcard
193
- if (d.startsWith("*.")) {
194
- const domainRoot = d.slice(2);
195
- return origin.endsWith(domainRoot);
196
- }
197
-
198
- // If there's no wildcard, we'll check for an exact match
199
- return d === origin;
200
- })) {
201
- return {
202
- authorized: true
203
- };
204
- }
205
- return {
206
- authorized: false,
207
- errorMessage: "The origin is not authorized for this key.",
208
- errorCode: "ORIGIN_UNAUTHORIZED",
209
- statusCode: 401
210
- };
211
- }
212
-
213
- // FIXME: validate bundle id
214
- return {
215
- authorized: false,
216
- errorMessage: "The keys are invalid.",
217
- errorCode: "UNAUTHORIZED",
218
- statusCode: 401
219
- };
220
- }
221
- function authzServices(validations, apiKey, scope) {
222
- const {
223
- services
224
- } = apiKey;
225
- const {
226
- serviceTargetAddresses,
227
- serviceAction
228
- } = validations;
229
-
230
- // validate services
231
- const service = services.find(srv => srv.name === scope);
232
- if (!service) {
233
- return {
234
- authorized: false,
235
- errorMessage: `The service "${scope}" is not authorized for this key.`,
236
- errorCode: "SERVICE_UNAUTHORIZED",
237
- statusCode: 403
238
- };
239
- }
240
-
241
- // validate service actions
242
- if (serviceAction) {
243
- if (!service.actions.includes(serviceAction)) {
244
- return {
245
- authorized: false,
246
- errorMessage: `The service "${scope}" action "${serviceAction}" is not authorized for this key.`,
247
- errorCode: "SERVICE_ACTION_UNAUTHORIZED",
248
- statusCode: 403
249
- };
250
- }
251
- }
252
-
253
- // validate service target addresses
254
- if (serviceTargetAddresses && !service.targetAddresses.find(addr => addr === "*" || serviceTargetAddresses.includes(addr))) {
255
- return {
256
- authorized: false,
257
- errorMessage: `The service "${scope}" target address is not authorized for this key.`,
258
- errorCode: "SERVICE_TARGET_ADDRESS_UNAUTHORIZED",
259
- statusCode: 403
260
- };
261
- }
262
- return {
263
- authorized: true
264
- };
265
- }
266
-
267
- function getServiceByName(name) {
268
- return SERVICES.find(srv => srv.name === name);
269
- }
270
-
271
- export { SERVICES, SERVICE_NAMES, authorizeCFWorkerService, authorizeNodeService, getServiceByName };