@thirdweb-dev/service-utils 0.1.1-nightly-4915ac50-20230714041125 → 0.2.0-nightly-5eb6fc1b-20230714082745
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/cf-worker/dist/thirdweb-dev-service-utils-cf-worker.cjs.d.ts +2 -0
- package/cf-worker/dist/thirdweb-dev-service-utils-cf-worker.cjs.d.ts.map +1 -0
- package/cf-worker/dist/thirdweb-dev-service-utils-cf-worker.cjs.dev.js +115 -0
- package/cf-worker/dist/thirdweb-dev-service-utils-cf-worker.cjs.js +7 -0
- package/cf-worker/dist/thirdweb-dev-service-utils-cf-worker.cjs.prod.js +115 -0
- package/cf-worker/dist/thirdweb-dev-service-utils-cf-worker.esm.js +105 -0
- package/cf-worker/package.json +4 -0
- package/dist/declarations/src/cf-worker/index.d.ts +18 -0
- package/dist/declarations/src/cf-worker/index.d.ts.map +1 -0
- package/dist/declarations/src/core/api.d.ts +31 -0
- package/dist/declarations/src/core/api.d.ts.map +1 -0
- package/dist/declarations/src/core/authorize/index.d.ts +18 -0
- package/dist/declarations/src/core/authorize/index.d.ts.map +1 -0
- package/dist/declarations/src/core/authorize/types.d.ts +11 -0
- package/dist/declarations/src/core/authorize/types.d.ts.map +1 -0
- package/dist/declarations/src/core/services.d.ts.map +1 -0
- package/dist/declarations/src/core/types.d.ts +5 -0
- package/dist/declarations/src/core/types.d.ts.map +1 -0
- package/dist/declarations/src/index.d.ts +1 -2
- package/dist/declarations/src/index.d.ts.map +1 -1
- package/dist/declarations/src/node/index.d.ts +16 -0
- package/dist/declarations/src/node/index.d.ts.map +1 -0
- package/dist/index-294e111f.esm.js +252 -0
- package/dist/index-3060948e.cjs.prod.js +254 -0
- package/dist/index-fcf69a55.cjs.dev.js +254 -0
- package/dist/services-86283509.esm.js +44 -0
- package/dist/services-9e185105.cjs.prod.js +49 -0
- package/dist/services-a3f36057.cjs.dev.js +49 -0
- package/dist/thirdweb-dev-service-utils.cjs.dev.js +5 -284
- package/dist/thirdweb-dev-service-utils.cjs.prod.js +5 -284
- package/dist/thirdweb-dev-service-utils.esm.js +1 -282
- package/node/dist/thirdweb-dev-service-utils-node.cjs.d.ts +2 -0
- package/node/dist/thirdweb-dev-service-utils-node.cjs.d.ts.map +1 -0
- package/node/dist/thirdweb-dev-service-utils-node.cjs.dev.js +113 -0
- package/node/dist/thirdweb-dev-service-utils-node.cjs.js +7 -0
- package/node/dist/thirdweb-dev-service-utils-node.cjs.prod.js +113 -0
- package/node/dist/thirdweb-dev-service-utils-node.esm.js +102 -0
- package/node/package.json +4 -0
- 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 -61
- package/dist/declarations/src/auth/types.d.ts.map +0 -1
- package/dist/declarations/src/services.d.ts.map +0 -1
- /package/dist/declarations/src/{services.d.ts → core/services.d.ts} +0 -0
@@ -0,0 +1,254 @@
|
|
1
|
+
'use strict';
|
2
|
+
|
3
|
+
async function fetchKeyMetadataFromApi(clientId, config) {
|
4
|
+
const {
|
5
|
+
apiUrl,
|
6
|
+
serviceScope,
|
7
|
+
serviceApiKey
|
8
|
+
} = config;
|
9
|
+
const url = new URL(`${apiUrl}/v1/keys/use`);
|
10
|
+
url.searchParams.set("clientId", clientId);
|
11
|
+
url.searchParams.set("scope", serviceScope);
|
12
|
+
const response = await fetch(url, {
|
13
|
+
method: "GET",
|
14
|
+
headers: {
|
15
|
+
"x-service-api-key": serviceApiKey,
|
16
|
+
"content-type": "application/json"
|
17
|
+
}
|
18
|
+
});
|
19
|
+
if (!response.ok) {
|
20
|
+
throw new Error(`Error fetching key metadata from API: ${response.statusText}`);
|
21
|
+
}
|
22
|
+
return await response.json();
|
23
|
+
}
|
24
|
+
|
25
|
+
function authorizeClient(authOptions, apiKeyMeta) {
|
26
|
+
const {
|
27
|
+
origin,
|
28
|
+
secretKeyHash: providedSecretHash
|
29
|
+
} = authOptions;
|
30
|
+
const {
|
31
|
+
domains,
|
32
|
+
secretHash
|
33
|
+
} = apiKeyMeta;
|
34
|
+
if (providedSecretHash) {
|
35
|
+
if (secretHash !== providedSecretHash) {
|
36
|
+
return {
|
37
|
+
authorized: false,
|
38
|
+
errorMessage: "The secret is invalid.",
|
39
|
+
errorCode: "SECRET_INVALID",
|
40
|
+
status: 401
|
41
|
+
};
|
42
|
+
}
|
43
|
+
return {
|
44
|
+
authorized: true,
|
45
|
+
apiKeyMeta
|
46
|
+
};
|
47
|
+
}
|
48
|
+
|
49
|
+
// validate domains
|
50
|
+
if (origin) {
|
51
|
+
if (
|
52
|
+
// find matching domain, or if all domains allowed
|
53
|
+
domains.find(d => {
|
54
|
+
if (d === "*") {
|
55
|
+
return true;
|
56
|
+
}
|
57
|
+
|
58
|
+
// If the allowedDomain has a wildcard,
|
59
|
+
// we'll check that the ending of our domain matches the wildcard
|
60
|
+
if (d.startsWith("*.")) {
|
61
|
+
const domainRoot = d.slice(2);
|
62
|
+
return origin.endsWith(domainRoot);
|
63
|
+
}
|
64
|
+
|
65
|
+
// If there's no wildcard, we'll check for an exact match
|
66
|
+
return d === origin;
|
67
|
+
})) {
|
68
|
+
return {
|
69
|
+
authorized: true,
|
70
|
+
apiKeyMeta
|
71
|
+
};
|
72
|
+
}
|
73
|
+
return {
|
74
|
+
authorized: false,
|
75
|
+
errorMessage: "The origin is not authorized for this key.",
|
76
|
+
errorCode: "ORIGIN_UNAUTHORIZED",
|
77
|
+
status: 401
|
78
|
+
};
|
79
|
+
}
|
80
|
+
|
81
|
+
// FIXME: validate bundle id
|
82
|
+
return {
|
83
|
+
authorized: false,
|
84
|
+
errorMessage: "The keys are invalid.",
|
85
|
+
errorCode: "UNAUTHORIZED",
|
86
|
+
status: 401
|
87
|
+
};
|
88
|
+
}
|
89
|
+
|
90
|
+
function authorizeService(apikeyMetadata, serviceConfig, authorizationPayload) {
|
91
|
+
const {
|
92
|
+
services
|
93
|
+
} = apikeyMetadata;
|
94
|
+
// const { serviceTargetAddresses, serviceAction } = validations;
|
95
|
+
|
96
|
+
// validate services
|
97
|
+
const service = services.find(srv => srv.name === serviceConfig.serviceScope);
|
98
|
+
if (!service) {
|
99
|
+
return {
|
100
|
+
authorized: false,
|
101
|
+
errorMessage: `The service "${serviceConfig.serviceScope}" is not authorized for this key.`,
|
102
|
+
errorCode: "SERVICE_UNAUTHORIZED",
|
103
|
+
status: 403
|
104
|
+
};
|
105
|
+
}
|
106
|
+
|
107
|
+
// validate service actions
|
108
|
+
if (serviceConfig.serviceAction) {
|
109
|
+
const isActionAllowed = service.actions.includes(serviceConfig.serviceAction);
|
110
|
+
if (!isActionAllowed) {
|
111
|
+
return {
|
112
|
+
authorized: false,
|
113
|
+
errorMessage: `The service "${serviceConfig.serviceScope}" action "${serviceConfig.serviceAction}" is not authorized for this key.`,
|
114
|
+
errorCode: "SERVICE_ACTION_UNAUTHORIZED",
|
115
|
+
status: 403
|
116
|
+
};
|
117
|
+
}
|
118
|
+
}
|
119
|
+
|
120
|
+
// validate service target addresses
|
121
|
+
// the service has to pass in the target address for this to be validated
|
122
|
+
if (authorizationPayload?.targetAddress) {
|
123
|
+
const isTargetAddressAllowed = service.targetAddresses.includes(authorizationPayload.targetAddress);
|
124
|
+
if (!isTargetAddressAllowed) {
|
125
|
+
return {
|
126
|
+
authorized: false,
|
127
|
+
errorMessage: `The service "${serviceConfig.serviceScope}" target address "${authorizationPayload.targetAddress}" is not authorized for this key.`,
|
128
|
+
errorCode: "SERVICE_TARGET_ADDRESS_UNAUTHORIZED",
|
129
|
+
status: 403
|
130
|
+
};
|
131
|
+
}
|
132
|
+
}
|
133
|
+
return {
|
134
|
+
authorized: true,
|
135
|
+
apiKeyMeta: apikeyMetadata
|
136
|
+
};
|
137
|
+
}
|
138
|
+
|
139
|
+
async function authorize(authData, serviceConfig, cacheOptions) {
|
140
|
+
// if we don't have a client id at this point we can't authorize
|
141
|
+
if (!authData.clientId) {
|
142
|
+
return {
|
143
|
+
authorized: false,
|
144
|
+
status: 401,
|
145
|
+
errorMessage: "Missing clientId or secretKey.",
|
146
|
+
errorCode: "MISSING_KEY"
|
147
|
+
};
|
148
|
+
}
|
149
|
+
let apiKeyMeta = null;
|
150
|
+
// if we have cache options we want to check the cache first
|
151
|
+
if (cacheOptions) {
|
152
|
+
try {
|
153
|
+
const cachedKey = await cacheOptions.get(authData.clientId);
|
154
|
+
if (cachedKey) {
|
155
|
+
const parsed = JSON.parse(cachedKey);
|
156
|
+
if ("updatedAt" in parsed) {
|
157
|
+
// we want to compare the updatedAt time to the current time
|
158
|
+
// if the difference is greater than the cacheTtl we want to ignore the cached data
|
159
|
+
const now = Date.now();
|
160
|
+
const diff = now - parsed.updatedAt;
|
161
|
+
const cacheTtl = cacheOptions.cacheTtlSeconds * 1000;
|
162
|
+
// only if the diff is less than the cacheTtl do we want to use the cached key
|
163
|
+
if (diff < cacheTtl * 1000) {
|
164
|
+
apiKeyMeta = parsed.apiKeyMeta;
|
165
|
+
}
|
166
|
+
} else {
|
167
|
+
apiKeyMeta = parsed;
|
168
|
+
}
|
169
|
+
}
|
170
|
+
} catch (err) {
|
171
|
+
// ignore errors, proceed as if not in cache
|
172
|
+
}
|
173
|
+
}
|
174
|
+
|
175
|
+
// if we don't have a cached key, fetch from the API
|
176
|
+
if (!apiKeyMeta) {
|
177
|
+
try {
|
178
|
+
const {
|
179
|
+
data,
|
180
|
+
error
|
181
|
+
} = await fetchKeyMetadataFromApi(authData.clientId, serviceConfig);
|
182
|
+
if (error) {
|
183
|
+
return {
|
184
|
+
authorized: false,
|
185
|
+
errorCode: error.code,
|
186
|
+
errorMessage: error.message,
|
187
|
+
status: error.statusCode
|
188
|
+
};
|
189
|
+
} else if (!data) {
|
190
|
+
return {
|
191
|
+
authorized: false,
|
192
|
+
errorCode: "NO_KEY",
|
193
|
+
errorMessage: "No error but also no key returned.",
|
194
|
+
status: 500
|
195
|
+
};
|
196
|
+
}
|
197
|
+
// if we have a key for sure then assign it
|
198
|
+
apiKeyMeta = data;
|
199
|
+
|
200
|
+
// cache the retrieved key if we have cache options
|
201
|
+
if (cacheOptions) {
|
202
|
+
// we await this always because it can be a promise or not
|
203
|
+
await cacheOptions.put(authData.clientId, data);
|
204
|
+
}
|
205
|
+
} catch (err) {
|
206
|
+
console.warn("failed to fetch key metadata from api", err);
|
207
|
+
return {
|
208
|
+
authorized: false,
|
209
|
+
status: 500,
|
210
|
+
errorMessage: "Failed to fetch key metadata.",
|
211
|
+
errorCode: "FAILED_TO_FETCH_KEY"
|
212
|
+
};
|
213
|
+
}
|
214
|
+
}
|
215
|
+
if (!apiKeyMeta) {
|
216
|
+
return {
|
217
|
+
authorized: false,
|
218
|
+
status: 401,
|
219
|
+
errorMessage: "Key is invalid.",
|
220
|
+
errorCode: "INVALID_KEY"
|
221
|
+
};
|
222
|
+
}
|
223
|
+
// now we can validate the key itself
|
224
|
+
const clientAuth = authorizeClient(authData, apiKeyMeta);
|
225
|
+
if (!clientAuth.authorized) {
|
226
|
+
return {
|
227
|
+
errorCode: clientAuth.errorCode,
|
228
|
+
authorized: false,
|
229
|
+
status: 401,
|
230
|
+
errorMessage: clientAuth.errorMessage
|
231
|
+
};
|
232
|
+
}
|
233
|
+
|
234
|
+
// if we've made it this far we need to check service specific authorization
|
235
|
+
const serviceAuth = authorizeService(apiKeyMeta, serviceConfig, {
|
236
|
+
targetAddress: authData.targetAddress
|
237
|
+
});
|
238
|
+
if (!serviceAuth.authorized) {
|
239
|
+
return {
|
240
|
+
errorCode: serviceAuth.errorCode,
|
241
|
+
authorized: false,
|
242
|
+
status: 403,
|
243
|
+
errorMessage: serviceAuth.errorMessage
|
244
|
+
};
|
245
|
+
}
|
246
|
+
|
247
|
+
// if we reach this point we are authorized!
|
248
|
+
return {
|
249
|
+
authorized: true,
|
250
|
+
apiKeyMeta
|
251
|
+
};
|
252
|
+
}
|
253
|
+
|
254
|
+
exports.authorize = authorize;
|
@@ -0,0 +1,44 @@
|
|
1
|
+
const SERVICE_DEFINITIONS = {
|
2
|
+
storage: {
|
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
|
+
rpc: {
|
17
|
+
name: "rpc",
|
18
|
+
title: "RPC",
|
19
|
+
description: "Accelerated RPC Edge",
|
20
|
+
// all actions allowed
|
21
|
+
actions: []
|
22
|
+
},
|
23
|
+
bundler: {
|
24
|
+
name: "bundler",
|
25
|
+
title: "Smart Wallets",
|
26
|
+
description: "Bundler & Paymaster services",
|
27
|
+
// all actions allowed
|
28
|
+
actions: []
|
29
|
+
},
|
30
|
+
relayer: {
|
31
|
+
name: "relayer",
|
32
|
+
title: "Gasless Relayer",
|
33
|
+
description: "Enable gasless transactions",
|
34
|
+
// all actions allowed
|
35
|
+
actions: []
|
36
|
+
}
|
37
|
+
};
|
38
|
+
const SERVICE_NAMES = Object.keys(SERVICE_DEFINITIONS);
|
39
|
+
const SERVICES = Object.values(SERVICE_DEFINITIONS);
|
40
|
+
function getServiceByName(name) {
|
41
|
+
return SERVICE_DEFINITIONS[name];
|
42
|
+
}
|
43
|
+
|
44
|
+
export { SERVICE_DEFINITIONS as S, SERVICE_NAMES as a, SERVICES as b, getServiceByName as g };
|
@@ -0,0 +1,49 @@
|
|
1
|
+
'use strict';
|
2
|
+
|
3
|
+
const SERVICE_DEFINITIONS = {
|
4
|
+
storage: {
|
5
|
+
name: "storage",
|
6
|
+
title: "Storage",
|
7
|
+
description: "IPFS Upload and Download",
|
8
|
+
actions: [{
|
9
|
+
name: "read",
|
10
|
+
title: "Download",
|
11
|
+
description: "Download a file from Storage"
|
12
|
+
}, {
|
13
|
+
name: "write",
|
14
|
+
title: "Upload",
|
15
|
+
description: "Upload a file to Storage"
|
16
|
+
}]
|
17
|
+
},
|
18
|
+
rpc: {
|
19
|
+
name: "rpc",
|
20
|
+
title: "RPC",
|
21
|
+
description: "Accelerated RPC Edge",
|
22
|
+
// all actions allowed
|
23
|
+
actions: []
|
24
|
+
},
|
25
|
+
bundler: {
|
26
|
+
name: "bundler",
|
27
|
+
title: "Smart Wallets",
|
28
|
+
description: "Bundler & Paymaster services",
|
29
|
+
// all actions allowed
|
30
|
+
actions: []
|
31
|
+
},
|
32
|
+
relayer: {
|
33
|
+
name: "relayer",
|
34
|
+
title: "Gasless Relayer",
|
35
|
+
description: "Enable gasless transactions",
|
36
|
+
// all actions allowed
|
37
|
+
actions: []
|
38
|
+
}
|
39
|
+
};
|
40
|
+
const SERVICE_NAMES = Object.keys(SERVICE_DEFINITIONS);
|
41
|
+
const SERVICES = Object.values(SERVICE_DEFINITIONS);
|
42
|
+
function getServiceByName(name) {
|
43
|
+
return SERVICE_DEFINITIONS[name];
|
44
|
+
}
|
45
|
+
|
46
|
+
exports.SERVICES = SERVICES;
|
47
|
+
exports.SERVICE_DEFINITIONS = SERVICE_DEFINITIONS;
|
48
|
+
exports.SERVICE_NAMES = SERVICE_NAMES;
|
49
|
+
exports.getServiceByName = getServiceByName;
|
@@ -0,0 +1,49 @@
|
|
1
|
+
'use strict';
|
2
|
+
|
3
|
+
const SERVICE_DEFINITIONS = {
|
4
|
+
storage: {
|
5
|
+
name: "storage",
|
6
|
+
title: "Storage",
|
7
|
+
description: "IPFS Upload and Download",
|
8
|
+
actions: [{
|
9
|
+
name: "read",
|
10
|
+
title: "Download",
|
11
|
+
description: "Download a file from Storage"
|
12
|
+
}, {
|
13
|
+
name: "write",
|
14
|
+
title: "Upload",
|
15
|
+
description: "Upload a file to Storage"
|
16
|
+
}]
|
17
|
+
},
|
18
|
+
rpc: {
|
19
|
+
name: "rpc",
|
20
|
+
title: "RPC",
|
21
|
+
description: "Accelerated RPC Edge",
|
22
|
+
// all actions allowed
|
23
|
+
actions: []
|
24
|
+
},
|
25
|
+
bundler: {
|
26
|
+
name: "bundler",
|
27
|
+
title: "Smart Wallets",
|
28
|
+
description: "Bundler & Paymaster services",
|
29
|
+
// all actions allowed
|
30
|
+
actions: []
|
31
|
+
},
|
32
|
+
relayer: {
|
33
|
+
name: "relayer",
|
34
|
+
title: "Gasless Relayer",
|
35
|
+
description: "Enable gasless transactions",
|
36
|
+
// all actions allowed
|
37
|
+
actions: []
|
38
|
+
}
|
39
|
+
};
|
40
|
+
const SERVICE_NAMES = Object.keys(SERVICE_DEFINITIONS);
|
41
|
+
const SERVICES = Object.values(SERVICE_DEFINITIONS);
|
42
|
+
function getServiceByName(name) {
|
43
|
+
return SERVICE_DEFINITIONS[name];
|
44
|
+
}
|
45
|
+
|
46
|
+
exports.SERVICES = SERVICES;
|
47
|
+
exports.SERVICE_DEFINITIONS = SERVICE_DEFINITIONS;
|
48
|
+
exports.SERVICE_NAMES = SERVICE_NAMES;
|
49
|
+
exports.getServiceByName = getServiceByName;
|
@@ -2,290 +2,11 @@
|
|
2
2
|
|
3
3
|
Object.defineProperty(exports, '__esModule', { value: true });
|
4
4
|
|
5
|
-
|
6
|
-
storage: {
|
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
|
-
rpc: {
|
21
|
-
name: "rpc",
|
22
|
-
title: "RPC",
|
23
|
-
description: "Accelerated RPC Edge",
|
24
|
-
// all actions allowed
|
25
|
-
actions: []
|
26
|
-
},
|
27
|
-
bundler: {
|
28
|
-
name: "bundler",
|
29
|
-
title: "Smart Wallets",
|
30
|
-
description: "Bundler & Paymaster services",
|
31
|
-
// all actions allowed
|
32
|
-
actions: []
|
33
|
-
},
|
34
|
-
relayer: {
|
35
|
-
name: "relayer",
|
36
|
-
title: "Gasless Relayer",
|
37
|
-
description: "Enable gasless transactions",
|
38
|
-
// all actions allowed
|
39
|
-
actions: []
|
40
|
-
}
|
41
|
-
};
|
42
|
-
const SERVICE_NAMES = Object.keys(SERVICE_DEFINITIONS);
|
43
|
-
const SERVICES = Object.values(SERVICE_DEFINITIONS);
|
44
|
-
function getServiceByName(name) {
|
45
|
-
return SERVICE_DEFINITIONS[name];
|
46
|
-
}
|
5
|
+
var services = require('./services-a3f36057.cjs.dev.js');
|
47
6
|
|
48
|
-
async function authorizeCFWorkerService(options) {
|
49
|
-
const {
|
50
|
-
kvStore,
|
51
|
-
ctx,
|
52
|
-
authOptions,
|
53
|
-
serviceConfig,
|
54
|
-
validations
|
55
|
-
} = options;
|
56
|
-
const {
|
57
|
-
clientId
|
58
|
-
} = authOptions;
|
59
|
-
let cachedKey;
|
60
7
|
|
61
|
-
// first, check if the key is in KV
|
62
|
-
try {
|
63
|
-
const kvKey = await kvStore.get(clientId);
|
64
|
-
if (kvKey) {
|
65
|
-
cachedKey = JSON.parse(kvKey);
|
66
|
-
}
|
67
|
-
} catch (err) {
|
68
|
-
// ignore JSON parse, assuming not valid
|
69
|
-
}
|
70
|
-
const updateKv = async keyData => {
|
71
|
-
kvStore.put(clientId, JSON.stringify(keyData), {
|
72
|
-
expirationTtl: serviceConfig.cacheTtl || 60
|
73
|
-
});
|
74
|
-
};
|
75
|
-
return authorize({
|
76
|
-
authOptions,
|
77
|
-
serviceConfig: {
|
78
|
-
...serviceConfig,
|
79
|
-
cachedKey,
|
80
|
-
onRefetchComplete: keyData => {
|
81
|
-
ctx.waitUntil(updateKv(keyData));
|
82
|
-
}
|
83
|
-
},
|
84
|
-
validations
|
85
|
-
});
|
86
|
-
}
|
87
|
-
async function authorizeNodeService(options) {
|
88
|
-
const {
|
89
|
-
authOptions,
|
90
|
-
serviceConfig,
|
91
|
-
validations
|
92
|
-
} = options;
|
93
|
-
return authorize({
|
94
|
-
authOptions,
|
95
|
-
serviceConfig,
|
96
|
-
validations
|
97
|
-
});
|
98
|
-
}
|
99
8
|
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
*/
|
105
|
-
async function authorize(options) {
|
106
|
-
try {
|
107
|
-
const {
|
108
|
-
authOptions,
|
109
|
-
serviceConfig,
|
110
|
-
validations
|
111
|
-
} = options;
|
112
|
-
const {
|
113
|
-
clientId
|
114
|
-
} = authOptions;
|
115
|
-
const {
|
116
|
-
apiUrl,
|
117
|
-
scope,
|
118
|
-
serviceKey,
|
119
|
-
cachedKey,
|
120
|
-
onRefetchComplete
|
121
|
-
} = serviceConfig;
|
122
|
-
let keyData = cachedKey;
|
123
|
-
|
124
|
-
// no cached key, re-fetch from API
|
125
|
-
if (!keyData) {
|
126
|
-
const response = await fetch(`${apiUrl}/v1/keys/use?scope=${scope}&clientId=${clientId}`, {
|
127
|
-
method: "GET",
|
128
|
-
headers: {
|
129
|
-
"x-service-api-key": serviceKey,
|
130
|
-
"content-type": "application/json"
|
131
|
-
}
|
132
|
-
});
|
133
|
-
const apiResponse = await response.json();
|
134
|
-
if (!response.ok) {
|
135
|
-
const {
|
136
|
-
error
|
137
|
-
} = apiResponse;
|
138
|
-
return {
|
139
|
-
authorized: false,
|
140
|
-
errorMessage: error.message,
|
141
|
-
errorCode: error.code || "",
|
142
|
-
statusCode: error.statusCode || 401
|
143
|
-
};
|
144
|
-
}
|
145
|
-
keyData = apiResponse.data;
|
146
|
-
if (onRefetchComplete) {
|
147
|
-
onRefetchComplete(keyData);
|
148
|
-
}
|
149
|
-
}
|
150
|
-
|
151
|
-
//
|
152
|
-
// Run validations
|
153
|
-
//
|
154
|
-
const authResponse = authAccess(authOptions, keyData);
|
155
|
-
if (!authResponse?.authorized) {
|
156
|
-
return authResponse;
|
157
|
-
}
|
158
|
-
const authzResponse = authzServices(validations, keyData, scope);
|
159
|
-
if (!authzResponse?.authorized) {
|
160
|
-
return authzResponse;
|
161
|
-
}
|
162
|
-
// FIXME: validate bundleId
|
163
|
-
|
164
|
-
return {
|
165
|
-
authorized: true,
|
166
|
-
data: keyData
|
167
|
-
};
|
168
|
-
} catch (err) {
|
169
|
-
console.error("Failed to authorize this key.", err);
|
170
|
-
return {
|
171
|
-
authorized: false,
|
172
|
-
errorMessage: "Internal error",
|
173
|
-
errorCode: "INTERNAL_ERROR",
|
174
|
-
statusCode: 500
|
175
|
-
};
|
176
|
-
}
|
177
|
-
}
|
178
|
-
function authAccess(authOptions, apiKey) {
|
179
|
-
const {
|
180
|
-
origin,
|
181
|
-
secretHash: providedSecretHash
|
182
|
-
} = authOptions;
|
183
|
-
const {
|
184
|
-
domains,
|
185
|
-
secretHash
|
186
|
-
} = apiKey;
|
187
|
-
if (providedSecretHash) {
|
188
|
-
if (secretHash !== providedSecretHash) {
|
189
|
-
return {
|
190
|
-
authorized: false,
|
191
|
-
errorMessage: "The secret is invalid.",
|
192
|
-
errorCode: "SECRET_INVALID",
|
193
|
-
statusCode: 401
|
194
|
-
};
|
195
|
-
}
|
196
|
-
return {
|
197
|
-
authorized: true
|
198
|
-
};
|
199
|
-
}
|
200
|
-
|
201
|
-
// validate domains
|
202
|
-
if (origin) {
|
203
|
-
if (
|
204
|
-
// find matching domain, or if all domains allowed
|
205
|
-
domains.find(d => {
|
206
|
-
if (d === "*") {
|
207
|
-
return true;
|
208
|
-
}
|
209
|
-
|
210
|
-
// If the allowedDomain has a wildcard,
|
211
|
-
// we'll check that the ending of our domain matches the wildcard
|
212
|
-
if (d.startsWith("*.")) {
|
213
|
-
const domainRoot = d.slice(2);
|
214
|
-
return origin.endsWith(domainRoot);
|
215
|
-
}
|
216
|
-
|
217
|
-
// If there's no wildcard, we'll check for an exact match
|
218
|
-
return d === origin;
|
219
|
-
})) {
|
220
|
-
return {
|
221
|
-
authorized: true
|
222
|
-
};
|
223
|
-
}
|
224
|
-
return {
|
225
|
-
authorized: false,
|
226
|
-
errorMessage: "The origin is not authorized for this key.",
|
227
|
-
errorCode: "ORIGIN_UNAUTHORIZED",
|
228
|
-
statusCode: 401
|
229
|
-
};
|
230
|
-
}
|
231
|
-
|
232
|
-
// FIXME: validate bundle id
|
233
|
-
return {
|
234
|
-
authorized: false,
|
235
|
-
errorMessage: "The keys are invalid.",
|
236
|
-
errorCode: "UNAUTHORIZED",
|
237
|
-
statusCode: 401
|
238
|
-
};
|
239
|
-
}
|
240
|
-
function authzServices(validations, apiKey, scope) {
|
241
|
-
const {
|
242
|
-
services
|
243
|
-
} = apiKey;
|
244
|
-
const {
|
245
|
-
serviceTargetAddresses,
|
246
|
-
serviceAction
|
247
|
-
} = validations;
|
248
|
-
|
249
|
-
// validate services
|
250
|
-
const service = services.find(srv => srv.name === scope);
|
251
|
-
if (!service) {
|
252
|
-
return {
|
253
|
-
authorized: false,
|
254
|
-
errorMessage: `The service "${scope}" is not authorized for this key.`,
|
255
|
-
errorCode: "SERVICE_UNAUTHORIZED",
|
256
|
-
statusCode: 403
|
257
|
-
};
|
258
|
-
}
|
259
|
-
|
260
|
-
// validate service actions
|
261
|
-
if (serviceAction) {
|
262
|
-
if (!service.actions.includes(serviceAction)) {
|
263
|
-
return {
|
264
|
-
authorized: false,
|
265
|
-
errorMessage: `The service "${scope}" action "${serviceAction}" is not authorized for this key.`,
|
266
|
-
errorCode: "SERVICE_ACTION_UNAUTHORIZED",
|
267
|
-
statusCode: 403
|
268
|
-
};
|
269
|
-
}
|
270
|
-
}
|
271
|
-
|
272
|
-
// validate service target addresses
|
273
|
-
if (serviceTargetAddresses && !service.targetAddresses.find(addr => addr === "*" || serviceTargetAddresses.includes(addr))) {
|
274
|
-
return {
|
275
|
-
authorized: false,
|
276
|
-
errorMessage: `The service "${scope}" target address is not authorized for this key.`,
|
277
|
-
errorCode: "SERVICE_TARGET_ADDRESS_UNAUTHORIZED",
|
278
|
-
statusCode: 403
|
279
|
-
};
|
280
|
-
}
|
281
|
-
return {
|
282
|
-
authorized: true
|
283
|
-
};
|
284
|
-
}
|
285
|
-
|
286
|
-
exports.SERVICES = SERVICES;
|
287
|
-
exports.SERVICE_DEFINITIONS = SERVICE_DEFINITIONS;
|
288
|
-
exports.SERVICE_NAMES = SERVICE_NAMES;
|
289
|
-
exports.authorizeCFWorkerService = authorizeCFWorkerService;
|
290
|
-
exports.authorizeNodeService = authorizeNodeService;
|
291
|
-
exports.getServiceByName = getServiceByName;
|
9
|
+
exports.SERVICES = services.SERVICES;
|
10
|
+
exports.SERVICE_DEFINITIONS = services.SERVICE_DEFINITIONS;
|
11
|
+
exports.SERVICE_NAMES = services.SERVICE_NAMES;
|
12
|
+
exports.getServiceByName = services.getServiceByName;
|