@thirdweb-dev/service-utils 0.0.0-dev-f2d9bcd-20230713055621 → 0.0.0-dev-2666fdc-20230713214856
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/dist/declarations/src/auth/index.d.ts +5 -20
- package/dist/declarations/src/auth/index.d.ts.map +1 -1
- package/dist/declarations/src/auth/types.d.ts +29 -13
- package/dist/declarations/src/auth/types.d.ts.map +1 -1
- package/dist/declarations/src/index.d.ts +1 -3
- package/dist/declarations/src/index.d.ts.map +1 -1
- package/dist/declarations/src/types.d.ts +3 -1
- package/dist/declarations/src/types.d.ts.map +1 -1
- package/dist/thirdweb-dev-service-utils.cjs.dev.js +199 -185
- package/dist/thirdweb-dev-service-utils.cjs.prod.js +199 -185
- package/dist/thirdweb-dev-service-utils.esm.js +196 -185
- package/package.json +1 -1
@@ -1,21 +1,6 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
ctx?: ExecutionContext;
|
7
|
-
kvStore: KVNamespace<string>;
|
8
|
-
headers: Headers;
|
9
|
-
clientId: string;
|
10
|
-
authOpts: AuthorizationOptions;
|
11
|
-
validations?: AuthorizationValidations;
|
12
|
-
}
|
13
|
-
export declare function authorizeWorkerService(options: AuthorizeWorkerOptions): Promise<AuthorizationResponse>;
|
14
|
-
export declare function authorizeNodeService(options: {
|
15
|
-
clientId: string;
|
16
|
-
headers: IncomingHttpHeaders;
|
17
|
-
authOpts: AuthorizationOptions;
|
18
|
-
validations?: AuthorizationValidations;
|
19
|
-
}): Promise<AuthorizationResponse>;
|
20
|
-
export {};
|
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
|
+
export declare function hashSecret(secret: string): string;
|
5
|
+
export declare function hashClientId(secret: string): string;
|
21
6
|
//# sourceMappingURL=index.d.ts.map
|
@@ -1 +1 @@
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"../../../../src/auth","sources":["index.ts"],"names":[],"mappings":"
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"../../../../src/auth","sources":["index.ts"],"names":[],"mappings":"AACA,OAAO,EAIL,qBAAqB,EAErB,wBAAwB,EACxB,2BAA2B,EAE5B,MAAM,SAAS,CAAC;AAGjB,wBAAsB,wBAAwB,CAC5C,OAAO,EAAE,wBAAwB,kCAkClC;AAED,wBAAsB,oBAAoB,CACxC,OAAO,EAAE,2BAA2B,kCASrC;AAED,wBAAgB,UAAU,CAAC,MAAM,EAAE,MAAM,UAExC;AAED,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,UAG1C"}
|
@@ -1,18 +1,34 @@
|
|
1
|
+
import { KVNamespace, ExecutionContext } from "@cloudflare/workers-types";
|
1
2
|
import { ServiceName } from "../types";
|
2
|
-
export interface
|
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 {
|
3
22
|
apiUrl: string;
|
4
|
-
serviceAPIKey: string;
|
5
23
|
scope: ServiceName;
|
6
|
-
|
24
|
+
serviceKey: string;
|
7
25
|
cachedKey?: ApiKey;
|
8
26
|
cacheTtl?: number;
|
9
27
|
onRefetchComplete?: (key: ApiKey) => void;
|
10
28
|
}
|
11
29
|
export interface AuthorizationValidations {
|
12
30
|
serviceTargetAddresses?: string[];
|
13
|
-
|
14
|
-
domains: string[];
|
15
|
-
bundleIds: string[];
|
31
|
+
serviceAction?: string;
|
16
32
|
}
|
17
33
|
export interface AuthorizationResponse {
|
18
34
|
authorized: boolean;
|
@@ -32,14 +48,14 @@ export interface ApiResponse {
|
|
32
48
|
export interface ApiKey {
|
33
49
|
id: string;
|
34
50
|
key: string;
|
51
|
+
secretHash: string;
|
35
52
|
walletAddresses: string[];
|
36
53
|
domains: string[];
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
];
|
54
|
+
bundleIds: string[];
|
55
|
+
services: {
|
56
|
+
name: string;
|
57
|
+
targetAddresses: string[];
|
58
|
+
actions: string[];
|
59
|
+
}[];
|
44
60
|
}
|
45
61
|
//# sourceMappingURL=types.d.ts.map
|
@@ -1 +1 @@
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"../../../../src/auth","sources":["types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAEvC,MAAM,WAAW,
|
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,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,6 +1,4 @@
|
|
1
|
-
|
2
|
-
export declare const SERVICES: Service[];
|
3
|
-
export declare function getServiceByName(name: string): Service | undefined;
|
1
|
+
export declare function getServiceByName(name: string): import("./types").Service | undefined;
|
4
2
|
export * from "./types";
|
5
3
|
export * from "./auth";
|
6
4
|
//# sourceMappingURL=index.d.ts.map
|
@@ -1 +1 @@
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"../../../src","sources":["index.ts"],"names":[],"mappings":"
|
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,4 +1,6 @@
|
|
1
|
-
export
|
1
|
+
export declare const SERVICE_NAMES: readonly ["bundler", "rpc", "storage"];
|
2
|
+
export declare const SERVICES: Service[];
|
3
|
+
export type ServiceName = (typeof SERVICE_NAMES)[number];
|
2
4
|
export interface ServiceAction {
|
3
5
|
name: string;
|
4
6
|
title: string;
|
@@ -1 +1 @@
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"../../../src","sources":["types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,WAAW,GAAG,
|
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"}
|
@@ -3,81 +3,121 @@
|
|
3
3
|
Object.defineProperty(exports, '__esModule', { value: true });
|
4
4
|
|
5
5
|
var fetch = require('isomorphic-unfetch');
|
6
|
+
var crypto = require('crypto');
|
6
7
|
|
7
8
|
function _interopDefault (e) { return e && e.__esModule ? e : { 'default': e }; }
|
8
9
|
|
9
10
|
var fetch__default = /*#__PURE__*/_interopDefault(fetch);
|
10
11
|
|
11
|
-
|
12
|
+
const SERVICE_NAMES = ["bundler", "rpc", "storage"];
|
13
|
+
const SERVICES = [{
|
14
|
+
name: "storage",
|
15
|
+
title: "Storage",
|
16
|
+
description: "IPFS Upload and Download",
|
17
|
+
actions: [{
|
18
|
+
name: "read",
|
19
|
+
title: "Download",
|
20
|
+
description: "Download a file from Storage"
|
21
|
+
}, {
|
22
|
+
name: "write",
|
23
|
+
title: "Upload",
|
24
|
+
description: "Upload a file to Storage"
|
25
|
+
}]
|
26
|
+
}, {
|
27
|
+
name: "rpc",
|
28
|
+
title: "RPC",
|
29
|
+
description: "Accelerated RPC Edge",
|
30
|
+
// all actions allowed
|
31
|
+
actions: []
|
32
|
+
}, {
|
33
|
+
name: "bundler",
|
34
|
+
title: "Smart Wallets",
|
35
|
+
description: "Bundler & Paymaster services",
|
36
|
+
// all actions allowed
|
37
|
+
actions: []
|
38
|
+
}];
|
39
|
+
|
40
|
+
async function authorizeCFWorkerService(options) {
|
41
|
+
const {
|
42
|
+
kvStore,
|
43
|
+
ctx,
|
44
|
+
authOptions,
|
45
|
+
serviceConfig,
|
46
|
+
validations
|
47
|
+
} = options;
|
48
|
+
const {
|
49
|
+
clientId
|
50
|
+
} = authOptions;
|
12
51
|
let cachedKey;
|
13
|
-
if (!options.clientId) {
|
14
|
-
return {
|
15
|
-
authorized: false,
|
16
|
-
errorMessage: "The ClientId is missing. Make sure it is included with your Authorization Bearer request header.",
|
17
|
-
errorCode: "MISSING_CLIENT_ID",
|
18
|
-
statusCode: 422
|
19
|
-
};
|
20
|
-
}
|
21
52
|
|
22
53
|
// first, check if the key is in KV
|
23
54
|
try {
|
24
|
-
const kvKey = await
|
55
|
+
const kvKey = await kvStore.get(clientId);
|
25
56
|
if (kvKey) {
|
26
57
|
cachedKey = JSON.parse(kvKey);
|
27
58
|
}
|
28
59
|
} catch (err) {
|
29
60
|
// ignore JSON parse, assuming not valid
|
30
61
|
}
|
31
|
-
const origin = options.headers.get("Origin") || "";
|
32
|
-
let originHost;
|
33
|
-
if (origin) {
|
34
|
-
try {
|
35
|
-
const originUrl = new URL(origin);
|
36
|
-
originHost = originUrl.host;
|
37
|
-
} catch (error) {
|
38
|
-
// ignore, will be verified by domains
|
39
|
-
}
|
40
|
-
}
|
41
62
|
const updateKv = async keyData => {
|
42
|
-
|
43
|
-
expirationTtl:
|
63
|
+
kvStore.put(clientId, JSON.stringify(keyData), {
|
64
|
+
expirationTtl: serviceConfig.cacheTtl || 60
|
44
65
|
});
|
45
66
|
};
|
46
|
-
return authorize(
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
67
|
+
return authorize({
|
68
|
+
authOptions,
|
69
|
+
serviceConfig: {
|
70
|
+
...serviceConfig,
|
71
|
+
cachedKey,
|
72
|
+
onRefetchComplete: keyData => {
|
73
|
+
ctx.waitUntil(updateKv(keyData));
|
74
|
+
}
|
75
|
+
},
|
76
|
+
validations
|
77
|
+
});
|
78
|
+
}
|
79
|
+
async function authorizeNodeService(options) {
|
80
|
+
const {
|
81
|
+
authOptions,
|
82
|
+
serviceConfig,
|
83
|
+
validations
|
84
|
+
} = options;
|
85
|
+
return authorize({
|
86
|
+
authOptions,
|
87
|
+
serviceConfig,
|
88
|
+
validations
|
89
|
+
});
|
90
|
+
}
|
91
|
+
function hashSecret(secret) {
|
92
|
+
return crypto.createHash("sha256").update(secret).digest("hex");
|
93
|
+
}
|
94
|
+
function hashClientId(secret) {
|
95
|
+
const hashed = crypto.createHash("sha256").update(secret).digest("hex");
|
96
|
+
return hashed.slice(0, 32);
|
54
97
|
}
|
55
98
|
|
56
99
|
/**
|
57
|
-
* Authorizes a request for a given
|
58
|
-
*
|
59
|
-
* @param clientId The String client id
|
60
|
-
* @params authOpts The Object auth options
|
61
|
-
* origin - The String origin
|
62
|
-
* apiUrl - The String API URL
|
63
|
-
* scope - The ServiceName scope identifier
|
64
|
-
* cachedKey - The ApiKey (optional) cached key
|
65
|
-
* onRefetchComplete - The Func to trigger after key refetch from API
|
66
|
-
* @params validations The Object of validations to run on a key
|
67
|
-
* serviceTargetAddresses - The Array (optional) of service target addresses to validate
|
68
|
-
* serviceActions - The Array (optional) of service actions to validate
|
100
|
+
* Authorizes a request for a given client ID
|
69
101
|
*
|
70
102
|
* @returns The Promise AuthorizationResponse
|
71
103
|
*/
|
72
|
-
async function authorize(
|
104
|
+
async function authorize(options) {
|
73
105
|
try {
|
106
|
+
const {
|
107
|
+
authOptions,
|
108
|
+
serviceConfig,
|
109
|
+
validations
|
110
|
+
} = options;
|
111
|
+
const {
|
112
|
+
clientId
|
113
|
+
} = authOptions;
|
74
114
|
const {
|
75
115
|
apiUrl,
|
76
|
-
origin,
|
77
116
|
scope,
|
117
|
+
serviceKey,
|
78
118
|
cachedKey,
|
79
119
|
onRefetchComplete
|
80
|
-
} =
|
120
|
+
} = serviceConfig;
|
81
121
|
let keyData = cachedKey;
|
82
122
|
|
83
123
|
// no cached key, re-fetch from API
|
@@ -85,8 +125,8 @@ async function authorize(clientId, authOpts, validations) {
|
|
85
125
|
const response = await fetch__default["default"](`${apiUrl}/v1/keys/use/?scope=${scope}&clientId=${clientId}`, {
|
86
126
|
method: "GET",
|
87
127
|
headers: {
|
88
|
-
"
|
89
|
-
"
|
128
|
+
"x-service-api-key": serviceKey,
|
129
|
+
"content-type": "application/json"
|
90
130
|
}
|
91
131
|
});
|
92
132
|
const apiResponse = await response.json();
|
@@ -110,107 +150,22 @@ async function authorize(clientId, authOpts, validations) {
|
|
110
150
|
//
|
111
151
|
// Run validations
|
112
152
|
//
|
113
|
-
const
|
114
|
-
|
115
|
-
|
116
|
-
} = validations || {};
|
117
|
-
|
118
|
-
// validate domains
|
119
|
-
if (keyData.domains && keyData.domains?.length > 0) {
|
120
|
-
let originHost = "";
|
121
|
-
if (origin) {
|
122
|
-
try {
|
123
|
-
const originUrl = new URL(origin);
|
124
|
-
originHost = originUrl.host;
|
125
|
-
} catch (error) {
|
126
|
-
// ignore, will be verified by domains
|
127
|
-
}
|
128
|
-
}
|
129
|
-
if (
|
130
|
-
// find matching domain, or if all domains allowed
|
131
|
-
!keyData.domains.find(d => {
|
132
|
-
if (d === "*") {
|
133
|
-
return true;
|
134
|
-
}
|
135
|
-
|
136
|
-
// If the allowedDomain has a wildcard,
|
137
|
-
// we'll check that the ending of our domain matches the wildcard
|
138
|
-
if (d.startsWith("*.")) {
|
139
|
-
const wildcard = d.slice(2);
|
140
|
-
return originHost.endsWith(wildcard);
|
141
|
-
}
|
142
|
-
|
143
|
-
// If there's no wildcard, we'll check for an exact match
|
144
|
-
return d === originHost;
|
145
|
-
})) {
|
146
|
-
return {
|
147
|
-
authorized: false,
|
148
|
-
errorMessage: "The domain is not authorized for this key.",
|
149
|
-
errorCode: "DOMAIN_UNAUTHORIZED",
|
150
|
-
statusCode: 403
|
151
|
-
};
|
152
|
-
}
|
153
|
+
const authResponse = authAccess(authOptions, keyData);
|
154
|
+
if (!authResponse?.authorized) {
|
155
|
+
return authResponse;
|
153
156
|
}
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
const service = (keyData.services || []).find(srv => srv.name === scope);
|
158
|
-
if (!service) {
|
159
|
-
return {
|
160
|
-
authorized: false,
|
161
|
-
errorMessage: `The service "${scope}" is not authorized for this key.`,
|
162
|
-
errorCode: "SERVICE_UNAUTHORIZED",
|
163
|
-
statusCode: 403
|
164
|
-
};
|
165
|
-
}
|
166
|
-
|
167
|
-
// validate service actions
|
168
|
-
if (serviceActions) {
|
169
|
-
let unknownAction;
|
170
|
-
serviceActions.forEach(action => {
|
171
|
-
if (!service.actions.includes(action)) {
|
172
|
-
unknownAction = action;
|
173
|
-
}
|
174
|
-
});
|
175
|
-
if (unknownAction) {
|
176
|
-
return {
|
177
|
-
authorized: false,
|
178
|
-
errorMessage: `The service "${scope}" action "${unknownAction}" is not authorized for this key.`,
|
179
|
-
errorCode: "SERVICE_ACTION_UNAUTHORIZED",
|
180
|
-
statusCode: 403
|
181
|
-
};
|
182
|
-
}
|
183
|
-
}
|
184
|
-
|
185
|
-
// validate service target addresses
|
186
|
-
if (serviceTargetAddresses && !service.targetAddresses.find(addr => addr === "*" || serviceTargetAddresses.includes(addr))) {
|
187
|
-
return {
|
188
|
-
authorized: false,
|
189
|
-
errorMessage: `The service "${scope}" target address is not authorized for this key.`,
|
190
|
-
errorCode: "SERVICE_TARGET_ADDRESS_UNAUTHORIZED",
|
191
|
-
statusCode: 403
|
192
|
-
};
|
193
|
-
}
|
157
|
+
const authzResponse = authzServices(validations, keyData, scope);
|
158
|
+
if (!authzResponse?.authorized) {
|
159
|
+
return authzResponse;
|
194
160
|
}
|
161
|
+
// FIXME: validate bundleId
|
195
162
|
|
196
|
-
// validate bundleIds
|
197
|
-
// if (
|
198
|
-
// bundleIds &&
|
199
|
-
// !keyData.bundleIds.find((addr) => addr === "*" || bundleIds.includes(addr))
|
200
|
-
// ) {
|
201
|
-
// return {
|
202
|
-
// authorized: false,
|
203
|
-
// errorMessage: `The service "${scope}" for BundlerIds ${bundleIds} is not authorized for this key.`,
|
204
|
-
// errorCode: "SERVICE_TARGET_ADDRESS_UNAUTHORIZED",
|
205
|
-
// statusCode: 403,
|
206
|
-
// };
|
207
|
-
// }
|
208
163
|
return {
|
209
164
|
authorized: true,
|
210
165
|
data: keyData
|
211
166
|
};
|
212
167
|
} catch (err) {
|
213
|
-
console.error("Failed to authorize this key", err);
|
168
|
+
console.error("Failed to authorize this key.", err);
|
214
169
|
return {
|
215
170
|
authorized: false,
|
216
171
|
errorMessage: "Internal error",
|
@@ -219,63 +174,122 @@ async function authorize(clientId, authOpts, validations) {
|
|
219
174
|
};
|
220
175
|
}
|
221
176
|
}
|
222
|
-
|
223
|
-
|
177
|
+
function authAccess(authOptions, apiKey) {
|
178
|
+
const {
|
179
|
+
origin,
|
180
|
+
secretHash: providedSecretHash
|
181
|
+
} = authOptions;
|
182
|
+
const {
|
183
|
+
domains,
|
184
|
+
secretHash
|
185
|
+
} = apiKey;
|
186
|
+
if (providedSecretHash) {
|
187
|
+
if (secretHash !== providedSecretHash) {
|
188
|
+
return {
|
189
|
+
authorized: false,
|
190
|
+
errorMessage: "The secret is invalid.",
|
191
|
+
errorCode: "SECRET_INVALID",
|
192
|
+
statusCode: 401
|
193
|
+
};
|
194
|
+
}
|
224
195
|
return {
|
225
|
-
authorized:
|
226
|
-
errorMessage: "The API key is missing. Make sure it is included with your Authorization Bearer request header.",
|
227
|
-
errorCode: "MISSING_API_KEY",
|
228
|
-
statusCode: 422
|
196
|
+
authorized: true
|
229
197
|
};
|
230
198
|
}
|
231
|
-
|
232
|
-
|
199
|
+
|
200
|
+
// validate domains
|
233
201
|
if (origin) {
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
202
|
+
if (
|
203
|
+
// find matching domain, or if all domains allowed
|
204
|
+
domains.find(d => {
|
205
|
+
if (d === "*") {
|
206
|
+
return true;
|
207
|
+
}
|
208
|
+
|
209
|
+
// If the allowedDomain has a wildcard,
|
210
|
+
// we'll check that the ending of our domain matches the wildcard
|
211
|
+
if (d.startsWith("*.")) {
|
212
|
+
const domainRoot = d.slice(2);
|
213
|
+
return origin.endsWith(domainRoot);
|
214
|
+
}
|
215
|
+
|
216
|
+
// If there's no wildcard, we'll check for an exact match
|
217
|
+
return d === origin;
|
218
|
+
})) {
|
219
|
+
return {
|
220
|
+
authorized: true
|
221
|
+
};
|
239
222
|
}
|
223
|
+
return {
|
224
|
+
authorized: false,
|
225
|
+
errorMessage: "The origin is not authorized for this key.",
|
226
|
+
errorCode: "ORIGIN_UNAUTHORIZED",
|
227
|
+
statusCode: 401
|
228
|
+
};
|
240
229
|
}
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
230
|
+
|
231
|
+
// FIXME: validate bundle id
|
232
|
+
return {
|
233
|
+
authorized: false,
|
234
|
+
errorMessage: "The keys are invalid.",
|
235
|
+
errorCode: "UNAUTHORIZED",
|
236
|
+
statusCode: 401
|
237
|
+
};
|
238
|
+
}
|
239
|
+
function authzServices(validations, apiKey, scope) {
|
240
|
+
const {
|
241
|
+
services
|
242
|
+
} = apiKey;
|
243
|
+
const {
|
244
|
+
serviceTargetAddresses,
|
245
|
+
serviceAction
|
246
|
+
} = validations;
|
247
|
+
|
248
|
+
// validate services
|
249
|
+
const service = services.find(srv => srv.name === scope);
|
250
|
+
if (!service) {
|
251
|
+
return {
|
252
|
+
authorized: false,
|
253
|
+
errorMessage: `The service "${scope}" is not authorized for this key.`,
|
254
|
+
errorCode: "SERVICE_UNAUTHORIZED",
|
255
|
+
statusCode: 403
|
256
|
+
};
|
257
|
+
}
|
258
|
+
|
259
|
+
// validate service actions
|
260
|
+
if (serviceAction) {
|
261
|
+
if (!service.actions.includes(serviceAction)) {
|
262
|
+
return {
|
263
|
+
authorized: false,
|
264
|
+
errorMessage: `The service "${scope}" action "${serviceAction}" is not authorized for this key.`,
|
265
|
+
errorCode: "SERVICE_ACTION_UNAUTHORIZED",
|
266
|
+
statusCode: 403
|
267
|
+
};
|
268
|
+
}
|
269
|
+
}
|
270
|
+
|
271
|
+
// validate service target addresses
|
272
|
+
if (serviceTargetAddresses && !service.targetAddresses.find(addr => addr === "*" || serviceTargetAddresses.includes(addr))) {
|
273
|
+
return {
|
274
|
+
authorized: false,
|
275
|
+
errorMessage: `The service "${scope}" target address is not authorized for this key.`,
|
276
|
+
errorCode: "SERVICE_TARGET_ADDRESS_UNAUTHORIZED",
|
277
|
+
statusCode: 403
|
278
|
+
};
|
279
|
+
}
|
280
|
+
return {
|
281
|
+
authorized: true
|
282
|
+
};
|
246
283
|
}
|
247
284
|
|
248
|
-
const SERVICES = [{
|
249
|
-
name: "storage",
|
250
|
-
title: "Storage",
|
251
|
-
description: "IPFS Upload and Download",
|
252
|
-
actions: [{
|
253
|
-
name: "read",
|
254
|
-
title: "Download",
|
255
|
-
description: "Download a file from Storage"
|
256
|
-
}, {
|
257
|
-
name: "write",
|
258
|
-
title: "Upload",
|
259
|
-
description: "Upload a file to Storage"
|
260
|
-
}]
|
261
|
-
}, {
|
262
|
-
name: "rpc",
|
263
|
-
title: "RPC",
|
264
|
-
description: "Accelerated RPC Edge",
|
265
|
-
// all actions allowed
|
266
|
-
actions: []
|
267
|
-
}, {
|
268
|
-
name: "bundler",
|
269
|
-
title: "Smart Wallets",
|
270
|
-
description: "Bundler & Paymaster services",
|
271
|
-
// all actions allowed
|
272
|
-
actions: []
|
273
|
-
}];
|
274
285
|
function getServiceByName(name) {
|
275
286
|
return SERVICES.find(srv => srv.name === name);
|
276
287
|
}
|
277
288
|
|
278
289
|
exports.SERVICES = SERVICES;
|
290
|
+
exports.SERVICE_NAMES = SERVICE_NAMES;
|
291
|
+
exports.authorizeCFWorkerService = authorizeCFWorkerService;
|
279
292
|
exports.authorizeNodeService = authorizeNodeService;
|
280
|
-
exports.authorizeWorkerService = authorizeWorkerService;
|
281
293
|
exports.getServiceByName = getServiceByName;
|
294
|
+
exports.hashClientId = hashClientId;
|
295
|
+
exports.hashSecret = hashSecret;
|