diff --git a/lib/edge/uid2_token.ts b/lib/edge/uid2_token.ts index cb3d08a..a7091ce 100644 --- a/lib/edge/uid2_token.ts +++ b/lib/edge/uid2_token.ts @@ -10,6 +10,15 @@ type Uid2TokenResponse = { RefreshResponseKey: string; }; +type Uid2RefreshBody = { + advertising_token: string; + refresh_token: string; + identity_expires: number; + refresh_from: number; + refresh_expires: number; + refresh_response_key: string; +}; + function Uid2Token(config: ResolvedConfig, id: string): Promise { return fetch("/uid2/token", config, { method: "POST", @@ -20,6 +29,29 @@ function Uid2Token(config: ResolvedConfig, id: string): Promise { + const encryptedBytes = Uint8Array.from(atob(encryptedBase64), (c) => c.charCodeAt(0)); + const keyBytes = Uint8Array.from(atob(responseKeyBase64), (c) => c.charCodeAt(0)); + const nonce = encryptedBytes.slice(0, 12); + const ciphertext = encryptedBytes.slice(12); + const cryptoKey = await crypto.subtle.importKey("raw", keyBytes, { name: "AES-GCM" }, false, ["decrypt"]); + const decryptedBuffer = await crypto.subtle.decrypt({ name: "AES-GCM", iv: nonce }, cryptoKey, ciphertext); + return new TextDecoder().decode(decryptedBuffer); +} + +async function Uid2Refresh(refreshToken: string, refreshResponseKey: string): Promise { + const response = await globalThis.fetch("https://prod.uidapi.com/v2/token/refresh", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: refreshToken, + }); + if (!response.ok) return null; + const encrypted = await response.text(); + const decrypted = await decryptUid2Response(encrypted, refreshResponseKey); + const { body } = JSON.parse(decrypted) as { body?: Uid2RefreshBody }; + return body ?? null; +} + +export { Uid2Token, Uid2Refresh }; export default Uid2Token; -export type { Uid2TokenResponse }; +export type { Uid2TokenResponse, Uid2RefreshBody }; diff --git a/lib/sdk.ts b/lib/sdk.ts index 3f4ba40..3b69198 100644 --- a/lib/sdk.ts +++ b/lib/sdk.ts @@ -6,7 +6,7 @@ import type { ProfileTraits } from "./edge/profile"; import type { PageContextConfig, ContextData } from "./core/context"; import { extractContext, normalizeContextConfig } from "./core/context"; import { Identify } from "./edge/identify"; -import { Uid2Token, Uid2TokenResponse } from "./edge/uid2_token"; +import { Uid2Token, Uid2TokenResponse, Uid2Refresh, Uid2RefreshBody } from "./edge/uid2_token"; import { Resolve, ResolveResponse } from "./edge/resolve"; import { Site, SiteResponse, SiteFromCache } from "./edge/site"; import { isObject } from "./core/utils"; @@ -73,6 +73,10 @@ class OptableSDK { return Uid2Token(this.dcn, id); } + async uid2Refresh(refreshToken: string, refreshResponseKey: string): Promise { + return Uid2Refresh(refreshToken, refreshResponseKey); + } + async targeting(input: string | TargetingRequest = "__passport__"): Promise { const request = normalizeTargetingRequest(input);