@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 +22 -10
- package/dist/declarations/src/auth/index.d.ts +0 -4
- package/dist/declarations/src/auth/index.d.ts.map +0 -1
- package/dist/declarations/src/auth/types.d.ts +0 -62
- package/dist/declarations/src/auth/types.d.ts.map +0 -1
- package/dist/declarations/src/index.d.ts +0 -4
- package/dist/declarations/src/index.d.ts.map +0 -1
- package/dist/declarations/src/types.d.ts +0 -15
- package/dist/declarations/src/types.d.ts.map +0 -1
- package/dist/thirdweb-dev-service-utils.cjs.d.ts +0 -2
- package/dist/thirdweb-dev-service-utils.cjs.d.ts.map +0 -1
- package/dist/thirdweb-dev-service-utils.cjs.dev.js +0 -279
- package/dist/thirdweb-dev-service-utils.cjs.js +0 -7
- package/dist/thirdweb-dev-service-utils.cjs.prod.js +0 -279
- package/dist/thirdweb-dev-service-utils.esm.js +0 -271
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@thirdweb-dev/service-utils",
|
3
|
-
"version": "0.0.0-dev-
|
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 +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 +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,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 };
|