diff --git a/CLAUDE.md b/CLAUDE.md index fcadd08..2f47a67 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -126,6 +126,7 @@ token.companyId; // string | undefined token.internalUserId; // string | undefined token.isClientUser; // boolean token.isInternalUser; // boolean +token.isProxying; // boolean — internal user previewing as a client (both IDs present) const client = token.ensureIsClient(); // ClientTokenPayload (throws if not client) const internal = token.ensureIsInternalUser(); // InternalUserTokenPayload (throws if not internal) diff --git a/README.md b/README.md index c26411c..cc4804b 100644 --- a/README.md +++ b/README.md @@ -358,8 +358,9 @@ token.tokenId; // string | undefined — present in some marketplace tokens token.baseUrl; // string | undefined — overrides the API base URL if set // Identity checks -token.isClientUser; // true if clientId + companyId are present +token.isClientUser; // true if clientId is present token.isInternalUser; // true if internalUserId is present +token.isProxying; // true if BOTH are present — internal user previewing as a client via CRM // Throwing guards — return narrowed payload type or throw AssemblyUnauthorizedError const clientPayload = token.ensureIsClient(); // ClientTokenPayload diff --git a/src/token/assembly-token.ts b/src/token/assembly-token.ts index 8e9942c..92d6611 100644 --- a/src/token/assembly-token.ts +++ b/src/token/assembly-token.ts @@ -117,6 +117,15 @@ export class AssemblyToken { return this.payload.internalUserId !== undefined; } + /** + * `true` when an internal user is proxying as a client (CRM "preview as client"): + * both `internalUserId` and `clientId` are present. Both `isClientUser` and + * `isInternalUser` are also `true` in this case — branch on this getter first. + */ + get isProxying(): boolean { + return this.payload.internalUserId !== undefined && this.payload.clientId !== undefined; + } + /** * Assert that the token belongs to a client user and return the narrowed payload. * @throws {AssemblyUnauthorizedError} If the token does not represent a client user. diff --git a/tests/fixtures/tokens.ts b/tests/fixtures/tokens.ts index b2fd1f4..2a33155 100644 --- a/tests/fixtures/tokens.ts +++ b/tests/fixtures/tokens.ts @@ -103,3 +103,13 @@ if (paddedWsId === undefined) { throw new Error("workspaceId missing in padded payload"); } export const BLOCK_ALIGNED_WORKSPACE_ID: string = paddedWsId; + +// --------------------------------------------------------------------------- +// Proxy token: internal user acting as a client via CRM +// --------------------------------------------------------------------------- +export const PROXY_TOKEN: string = encryptPayload(TEST_API_KEY, { + clientId: TEST_CLIENT_ID, + companyId: TEST_COMPANY_ID, + internalUserId: TEST_INTERNAL_USER_ID, + workspaceId: TEST_WORKSPACE_ID, +}); diff --git a/tests/token.test.ts b/tests/token.test.ts index f763b4b..f7bf916 100644 --- a/tests/token.test.ts +++ b/tests/token.test.ts @@ -13,6 +13,7 @@ import { BLOCK_ALIGNED_WORKSPACE_ID, CLIENT_TOKEN, INTERNAL_USER_TOKEN, + PROXY_TOKEN, TEST_API_KEY, TEST_BASE_URL, TEST_CLIENT_ID, @@ -192,6 +193,22 @@ describe("AssemblyToken getters", () => { // Identity checks // --------------------------------------------------------------------------- describe("AssemblyToken identity checks", () => { + it("proxy token (internalUserId + clientId) is flagged as proxying", () => { + const t = new AssemblyToken({ apiKey: TEST_API_KEY, token: PROXY_TOKEN }); + expect(t.isProxying).toBe(true); + expect(t.isClientUser).toBe(true); + expect(t.isInternalUser).toBe(true); + expect(t.ensureIsClient().clientId).toBe(TEST_CLIENT_ID); + expect(t.ensureIsInternalUser().internalUserId).toBe(TEST_INTERNAL_USER_ID); + }); + + it("isProxying is false for single-identity tokens", () => { + expect(new AssemblyToken({ apiKey: TEST_API_KEY, token: CLIENT_TOKEN }).isProxying).toBe(false); + expect(new AssemblyToken({ apiKey: TEST_API_KEY, token: INTERNAL_USER_TOKEN }).isProxying).toBe( + false, + ); + }); + it("isClientUser is true for client tokens", () => { const t = new AssemblyToken({ apiKey: TEST_API_KEY, token: CLIENT_TOKEN }); expect(t.isClientUser).toBe(true);