diff --git a/.changeset/twelve-scissors-share.md b/.changeset/twelve-scissors-share.md new file mode 100644 index 00000000000..dae7e75c54d --- /dev/null +++ b/.changeset/twelve-scissors-share.md @@ -0,0 +1,11 @@ +--- +"@thirdweb-dev/service-utils": patch +--- + +expose domain and bundle Id validation logic + +```typescript +import { authorizeDomain, authorizeBundleId } from "@thirdweb-dev/service-utils +const isValidDomain = authorizeDomain({ domains, origin }); +const isValidBundleId = authorizeBundleId({ bundleId, bundleIds }); +``` diff --git a/packages/service-utils/src/core/authorize/client.ts b/packages/service-utils/src/core/authorize/client.ts index 5171f5f47cc..aa3bafe5829 100644 --- a/packages/service-utils/src/core/authorize/client.ts +++ b/packages/service-utils/src/core/authorize/client.ts @@ -51,36 +51,9 @@ export function authorizeClient( // validate domains if (origin) { if ( - // find matching domain, or if all domains allowed - // embedded-wallet.thirdweb(-dev).com is automatically allowed - // because the rpc is passed from user's domain to embedded-wallet.thirdweb.com iframe for use. - // Note this doesn't allow embedded-wallets from being used if it's disabled. The service check that runs after enforces that. - [ - ...domains, - "embedded-wallet.thirdweb.com", - "embedded-wallet.thirdweb-dev.com", - ].find((d) => { - // if any domain is allowed, we'll return true - if (d === "*") { - return true; - } - - // special rule for `localhost` - // if the domain is localhost, we'll allow any origin that starts with localhost - if (d === "localhost" && origin.startsWith("localhost")) { - return true; - } - - // If the allowedDomain has a wildcard, - // we'll check that the ending of our domain matches the wildcard - if (d.startsWith("*.")) { - // get rid of the * and check if it ends with the `..` - const domainRoot = d.slice(1); - return origin.endsWith(domainRoot); - } - - // If there's no wildcard, we'll check for an exact match - return d === origin; + authorizeDomain({ + domains, + origin, }) ) { return authResult; @@ -97,13 +70,9 @@ export function authorizeClient( // validate bundleId if (bundleId) { if ( - // find matching bundle id, or if all bundles allowed - bundleIds.find((b) => { - if (b === "*") { - return true; - } - - return b === bundleId; + authorizeBundleId({ + bundleIds, + bundleId, }) ) { return authResult; @@ -125,3 +94,55 @@ export function authorizeClient( status: 401, }; } + +// Exposed for use in validating ecosystem partners settings +export function authorizeDomain({ + domains, + origin, +}: { domains: string[]; origin: string }): boolean { + // find matching domain, or if all domains allowed + // embedded-wallet.thirdweb(-dev).com is automatically allowed + // because the rpc is passed from user's domain to embedded-wallet.thirdweb.com iframe for use. + // Note this doesn't allow embedded-wallets from being used if it's disabled. The service check that runs after enforces that. + return !![ + ...domains, + "embedded-wallet.thirdweb.com", + "embedded-wallet.thirdweb-dev.com", + ].find((d) => { + // if any domain is allowed, we'll return true + if (d === "*") { + return true; + } + + // special rule for `localhost` + // if the domain is localhost, we'll allow any origin that starts with localhost + if (d === "localhost" && origin.startsWith("localhost")) { + return true; + } + + // If the allowedDomain has a wildcard, + // we'll check that the ending of our domain matches the wildcard + if (d.startsWith("*.")) { + // get rid of the * and check if it ends with the `..` + const domainRoot = d.slice(1); + return origin.endsWith(domainRoot); + } + + // If there's no wildcard, we'll check for an exact match + return d === origin; + }); +} + +export function authorizeBundleId({ + bundleIds, + bundleId, +}: { bundleIds: string[]; bundleId: string }): boolean { + // find matching bundle id, or if all bundles allowed + return !!bundleIds.find((b) => { + if (b === "*") { + return true; + } + + return b === bundleId; + }); +} diff --git a/packages/service-utils/src/index.ts b/packages/service-utils/src/index.ts index 65bc814591b..c697f8dc8b4 100644 --- a/packages/service-utils/src/index.ts +++ b/packages/service-utils/src/index.ts @@ -1,2 +1,7 @@ // Exports the public service definitions. export * from "./core/services"; + +export { + authorizeBundleId, + authorizeDomain, +} from "./core/authorize/client"; diff --git a/packages/service-utils/src/node/index.ts b/packages/service-utils/src/node/index.ts index 7e0a2306235..e00d1eb9198 100644 --- a/packages/service-utils/src/node/index.ts +++ b/packages/service-utils/src/node/index.ts @@ -1,17 +1,19 @@ import { createHash } from "node:crypto"; import { authorize } from "../core/authorize"; -import type { ServerResponse } from "node:http"; -import type { IncomingHttpHeaders, IncomingMessage } from "node:http"; +import type { + IncomingHttpHeaders, + IncomingMessage, + ServerResponse, +} from "node:http"; import type { CoreServiceConfig } from "../core/api"; import type { AuthorizationInput } from "../core/authorize"; import type { AuthorizationResult } from "../core/authorize/types"; import type { CoreAuthInput } from "../core/types"; -export * from "../core/services"; export * from "../core/rateLimit"; +export * from "../core/services"; export * from "../core/usageLimit"; - type NodeServiceConfig = CoreServiceConfig; export type AuthInput = CoreAuthInput & {