@thirdweb-dev/service-utils 0.0.0-dev-f2d9bcd-20230713055621 → 0.0.0-dev-7953a1c-20230713221245

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.
@@ -1,21 +1,4 @@
1
- /// <reference types="node" />
2
- import { AuthorizationOptions, AuthorizationResponse, AuthorizationValidations } from "./types";
3
- import { KVNamespace, ExecutionContext } from "@cloudflare/workers-types";
4
- import { IncomingHttpHeaders } from "http";
5
- interface AuthorizeWorkerOptions {
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>;
21
4
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"../../../../src/auth","sources":["index.ts"],"names":[],"mappings":";AACA,OAAO,EAGL,oBAAoB,EACpB,qBAAqB,EACrB,wBAAwB,EACzB,MAAM,SAAS,CAAC;AACjB,OAAO,EAAE,WAAW,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AAC1E,OAAO,EAAE,mBAAmB,EAAE,MAAM,MAAM,CAAC;AAE3C,UAAU,sBAAsB;IAC9B,GAAG,CAAC,EAAE,gBAAgB,CAAC;IACvB,OAAO,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IAC7B,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,oBAAoB,CAAC;IAC/B,WAAW,CAAC,EAAE,wBAAwB,CAAC;CACxC;AAED,wBAAsB,sBAAsB,CAAC,OAAO,EAAE,sBAAsB,kCAqD3E;AAwLD,wBAAsB,oBAAoB,CAAC,OAAO,EAAE;IAClD,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,mBAAmB,CAAC;IAC7B,QAAQ,EAAE,oBAAoB,CAAC;IAC/B,WAAW,CAAC,EAAE,wBAAwB,CAAC;CACxC,kCAmCA"}
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,18 +1,34 @@
1
+ import { KVNamespace, ExecutionContext } from "@cloudflare/workers-types";
1
2
  import { ServiceName } from "../types";
2
- export interface AuthorizationOptions {
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
- origin?: string;
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
- serviceActions?: string[];
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
- services?: [
38
- {
39
- name: string;
40
- targetAddresses: string[];
41
- actions: string[];
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,oBAAoB;IACnC,MAAM,EAAE,MAAM,CAAC;IACf,aAAa,EAAE,MAAM,CAAC;IACtB,KAAK,EAAE,WAAW,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,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,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;IAC1B,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,SAAS,EAAE,MAAM,EAAE,CAAC;CACrB;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,eAAe,EAAE,MAAM,EAAE,CAAC;IAC1B,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,QAAQ,CAAC,EAAE;QACT;YACE,IAAI,EAAE,MAAM,CAAC;YACb,eAAe,EAAE,MAAM,EAAE,CAAC;YAC1B,OAAO,EAAE,MAAM,EAAE,CAAC;SACnB;KACF,CAAC;CACH"}
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
- import { Service } from "./types";
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":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAElC,eAAO,MAAM,QAAQ,EAAE,OAAO,EAgC7B,CAAC;AAEF,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,GAAG,SAAS,CAElE;AAED,cAAc,SAAS,CAAC;AACxB,cAAc,QAAQ,CAAC"}
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 type ServiceName = "bundler" | "rpc" | "storage";
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,SAAS,GAAG,KAAK,GAAG,SAAS,CAAC;AAExD,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
+ {"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"}
@@ -2,91 +2,117 @@
2
2
 
3
3
  Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
- var fetch = require('isomorphic-unfetch');
6
-
7
- function _interopDefault (e) { return e && e.__esModule ? e : { 'default': e }; }
8
-
9
- var fetch__default = /*#__PURE__*/_interopDefault(fetch);
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
+ }];
10
32
 
11
- async function authorizeWorkerService(options) {
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;
12
44
  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
45
 
22
46
  // first, check if the key is in KV
23
47
  try {
24
- const kvKey = await options.kvStore.get(options.clientId);
48
+ const kvKey = await kvStore.get(clientId);
25
49
  if (kvKey) {
26
50
  cachedKey = JSON.parse(kvKey);
27
51
  }
28
52
  } catch (err) {
29
53
  // ignore JSON parse, assuming not valid
30
54
  }
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
55
  const updateKv = async keyData => {
42
- options.kvStore.put(options.clientId, JSON.stringify(keyData), {
43
- expirationTtl: options.authOpts.cacheTtl || 60
56
+ kvStore.put(clientId, JSON.stringify(keyData), {
57
+ expirationTtl: serviceConfig.cacheTtl || 60
44
58
  });
45
59
  };
46
- return authorize(options.clientId, {
47
- ...options.authOpts,
48
- origin: originHost,
49
- cachedKey,
50
- onRefetchComplete: keyData => {
51
- options?.ctx?.waitUntil(updateKv(keyData));
52
- }
53
- }, options.validations);
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
+ });
54
83
  }
55
84
 
56
85
  /**
57
- * Authorizes a request for a given clientId
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
86
+ * Authorizes a request for a given client ID
69
87
  *
70
88
  * @returns The Promise AuthorizationResponse
71
89
  */
72
- async function authorize(clientId, authOpts, validations) {
90
+ async function authorize(options) {
73
91
  try {
92
+ const {
93
+ authOptions,
94
+ serviceConfig,
95
+ validations
96
+ } = options;
97
+ const {
98
+ clientId
99
+ } = authOptions;
74
100
  const {
75
101
  apiUrl,
76
- origin,
77
102
  scope,
103
+ serviceKey,
78
104
  cachedKey,
79
105
  onRefetchComplete
80
- } = authOpts;
106
+ } = serviceConfig;
81
107
  let keyData = cachedKey;
82
108
 
83
109
  // no cached key, re-fetch from API
84
110
  if (!keyData) {
85
- const response = await fetch__default["default"](`${apiUrl}/v1/keys/use/?scope=${scope}&clientId=${clientId}`, {
111
+ const response = await fetch(`${apiUrl}/v1/keys/use/?scope=${scope}&clientId=${clientId}`, {
86
112
  method: "GET",
87
113
  headers: {
88
- "content-type": "application/json",
89
- "x-service-api-key": authOpts.serviceAPIKey
114
+ "x-service-api-key": serviceKey,
115
+ "content-type": "application/json"
90
116
  }
91
117
  });
92
118
  const apiResponse = await response.json();
@@ -110,107 +136,22 @@ async function authorize(clientId, authOpts, validations) {
110
136
  //
111
137
  // Run validations
112
138
  //
113
- const {
114
- serviceActions,
115
- serviceTargetAddresses
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
- }
139
+ const authResponse = authAccess(authOptions, keyData);
140
+ if (!authResponse?.authorized) {
141
+ return authResponse;
153
142
  }
154
-
155
- // validate services
156
- if (keyData.services && keyData.services?.length > 0) {
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
- }
143
+ const authzResponse = authzServices(validations, keyData, scope);
144
+ if (!authzResponse?.authorized) {
145
+ return authzResponse;
194
146
  }
147
+ // FIXME: validate bundleId
195
148
 
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
149
  return {
209
150
  authorized: true,
210
151
  data: keyData
211
152
  };
212
153
  } catch (err) {
213
- console.error("Failed to authorize this key", err);
154
+ console.error("Failed to authorize this key.", err);
214
155
  return {
215
156
  authorized: false,
216
157
  errorMessage: "Internal error",
@@ -219,63 +160,120 @@ async function authorize(clientId, authOpts, validations) {
219
160
  };
220
161
  }
221
162
  }
222
- async function authorizeNodeService(options) {
223
- if (!options.clientId) {
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
+ }
224
181
  return {
225
- authorized: false,
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
182
+ authorized: true
229
183
  };
230
184
  }
231
- const origin = typeof options.headers["Origin"] === "string" ? options.headers["Origin"] : options.headers["Origin"]?.join("");
232
- let originHost;
185
+
186
+ // validate domains
233
187
  if (origin) {
234
- try {
235
- const originUrl = new URL(origin);
236
- originHost = originUrl.hostname;
237
- } catch (error) {
238
- // ignore, will be verified by domains
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
+ };
239
208
  }
209
+ return {
210
+ authorized: false,
211
+ errorMessage: "The origin is not authorized for this key.",
212
+ errorCode: "ORIGIN_UNAUTHORIZED",
213
+ statusCode: 401
214
+ };
240
215
  }
241
- return authorize(options.clientId, {
242
- ...options.authOpts,
243
- origin: originHost,
244
- cachedKey: undefined
245
- }, options.validations);
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
+ };
246
269
  }
247
270
 
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
271
  function getServiceByName(name) {
275
272
  return SERVICES.find(srv => srv.name === name);
276
273
  }
277
274
 
278
275
  exports.SERVICES = SERVICES;
276
+ exports.SERVICE_NAMES = SERVICE_NAMES;
277
+ exports.authorizeCFWorkerService = authorizeCFWorkerService;
279
278
  exports.authorizeNodeService = authorizeNodeService;
280
- exports.authorizeWorkerService = authorizeWorkerService;
281
279
  exports.getServiceByName = getServiceByName;