diff --git a/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/wallets/user-wallets/configuration/api/revoke-sessions.ts b/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/wallets/user-wallets/configuration/api/revoke-sessions.ts index b1a20c0c799..d02af678058 100644 --- a/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/wallets/user-wallets/configuration/api/revoke-sessions.ts +++ b/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/wallets/user-wallets/configuration/api/revoke-sessions.ts @@ -2,7 +2,10 @@ import "server-only"; import { getAuthToken } from "@/api/auth-token"; -import { THIRDWEB_INAPP_WALLET_DOMAIN } from "@/constants/urls"; +import { + THIRDWEB_API_HOST, + THIRDWEB_INAPP_WALLET_DOMAIN, +} from "@/constants/urls"; export type RevokeSessionsTarget = | { type: "email" | "phone" | "walletAddress" | "userId"; value: string } @@ -17,10 +20,25 @@ type RevokeSessionsResult = } | { success: false; error: string }; +const SECRET_KEY_HASH_PATTERN = /^[0-9a-f]{64}$/; + +function inAppWalletHost() { + if (process.env.NEXT_PUBLIC_IN_APP_WALLET_URL) { + return THIRDWEB_INAPP_WALLET_DOMAIN; + } + let apiHost = ""; + try { + apiHost = new URL(THIRDWEB_API_HOST).host; + } catch {} + return apiHost === "api.thirdweb.com" + ? "embedded-wallet.thirdweb.com" + : THIRDWEB_INAPP_WALLET_DOMAIN; +} + export async function revokeUserWalletSessions(params: { teamId: string; clientId: string; - secretKey: string; + secretKeyHash: string; target: RevokeSessionsTarget; }): Promise { const token = await getAuthToken(); @@ -28,38 +46,36 @@ export async function revokeUserWalletSessions(params: { return { error: "Unauthorized", success: false }; } - const secretKey = params.secretKey.trim(); - const { target } = params; - if (!secretKey || (target.type !== "allUsers" && !target.value.trim())) { + const { secretKeyHash, target } = params; + if ( + !SECRET_KEY_HASH_PATTERN.test(secretKeyHash) || + (target.type !== "allUsers" && !target.value.trim()) + ) { return { error: "Missing required fields", success: false }; } - const protocol = THIRDWEB_INAPP_WALLET_DOMAIN.startsWith("localhost") - ? "http" - : "https"; + const host = inAppWalletHost(); + const protocol = host.startsWith("localhost") ? "http" : "https"; let res: Response; try { - res = await fetch( - `${protocol}://${THIRDWEB_INAPP_WALLET_DOMAIN}/api/v1/users/revoke-sessions`, - { - body: JSON.stringify({ - clientId: params.clientId, - secretKey, - ...(target.type === "allUsers" - ? { allUsers: true } - : { [target.type]: target.value.trim() }), - }), - cache: "no-store", - headers: { - Authorization: `Bearer ${token}`, - "Content-Type": "application/json", - "x-client-id": params.clientId, - "x-thirdweb-team-id": params.teamId, - }, - method: "POST", + res = await fetch(`${protocol}://${host}/api/v1/users/revoke-sessions`, { + body: JSON.stringify({ + clientId: params.clientId, + secretKeyHash, + ...(target.type === "allUsers" + ? { allUsers: true } + : { [target.type]: target.value.trim() }), + }), + cache: "no-store", + headers: { + Authorization: `Bearer ${token}`, + "Content-Type": "application/json", + "x-client-id": params.clientId, + "x-thirdweb-team-id": params.teamId, }, - ); + method: "POST", + }); } catch { return { error: "Failed to reach the wallet service", success: false }; } diff --git a/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/wallets/user-wallets/configuration/components/revoke-sessions-card.tsx b/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/wallets/user-wallets/configuration/components/revoke-sessions-card.tsx index 240faf3c68d..19211a9dc58 100644 --- a/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/wallets/user-wallets/configuration/components/revoke-sessions-card.tsx +++ b/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/wallets/user-wallets/configuration/components/revoke-sessions-card.tsx @@ -21,6 +21,16 @@ import { type IdentifierType = "email" | "phone" | "walletAddress" | "userId"; +async function sha256Hex(value: string) { + const digest = await crypto.subtle.digest( + "SHA-256", + new TextEncoder().encode(value), + ); + return Array.from(new Uint8Array(digest), (byte) => + byte.toString(16).padStart(2, "0"), + ).join(""); +} + const identifierOptions: { value: IdentifierType; label: string; @@ -56,7 +66,7 @@ export function RevokeSessionsCard(props: { : { type: identifierType, value: identifier }; const result = await revokeUserWalletSessions({ clientId: props.clientId, - secretKey, + secretKeyHash: await sha256Hex(secretKey.trim()), target, teamId: props.teamId, });