From 1fcb8ac7fd6b106c523c5de3c79fab9232b36ffd Mon Sep 17 00:00:00 2001 From: Tom Milewski Date: Thu, 5 Feb 2026 17:12:28 -0500 Subject: [PATCH 1/5] feat(backend): Add test session tokens endpoints --- .changeset/eighty-bobcats-unite.md | 5 ++++ .../src/api/endpoints/TestSessionTokenApi.ts | 19 ++++++++++++++ packages/backend/src/api/endpoints/index.ts | 1 + packages/backend/src/api/factory.ts | 2 ++ .../backend/src/api/resources/Deserializer.ts | 3 +++ packages/backend/src/api/resources/JSON.ts | 11 ++++++++ .../src/api/resources/TestSessionToken.ts | 25 +++++++++++++++++++ packages/backend/src/api/resources/index.ts | 1 + 8 files changed, 67 insertions(+) create mode 100644 .changeset/eighty-bobcats-unite.md create mode 100644 packages/backend/src/api/endpoints/TestSessionTokenApi.ts create mode 100644 packages/backend/src/api/resources/TestSessionToken.ts diff --git a/.changeset/eighty-bobcats-unite.md b/.changeset/eighty-bobcats-unite.md new file mode 100644 index 00000000000..45e0df390c0 --- /dev/null +++ b/.changeset/eighty-bobcats-unite.md @@ -0,0 +1,5 @@ +--- +'@clerk/backend': minor +--- + +Add support for Test Session Tokens API endpoint which allows developers to create test session tokens that can be used to impersonate users during automated flows. diff --git a/packages/backend/src/api/endpoints/TestSessionTokenApi.ts b/packages/backend/src/api/endpoints/TestSessionTokenApi.ts new file mode 100644 index 00000000000..59501f0b7a1 --- /dev/null +++ b/packages/backend/src/api/endpoints/TestSessionTokenApi.ts @@ -0,0 +1,19 @@ +import type { TestSessionToken } from '../resources/TestSessionToken'; +import { AbstractAPI } from './AbstractApi'; + +type CreateTestSessionTokenParams = { + userId: string; + sessionMaxDurationInSeconds?: number; +}; + +const basePath = '/test_session_tokens'; + +export class TestSessionTokenAPI extends AbstractAPI { + public async create(params: CreateTestSessionTokenParams) { + return this.request({ + method: 'POST', + path: basePath, + bodyParams: params, + }); + } +} diff --git a/packages/backend/src/api/endpoints/index.ts b/packages/backend/src/api/endpoints/index.ts index c03875a427d..c0c7211ac3f 100644 --- a/packages/backend/src/api/endpoints/index.ts +++ b/packages/backend/src/api/endpoints/index.ts @@ -24,6 +24,7 @@ export * from './SamlConnectionApi'; export * from './SessionApi'; export * from './SignInTokenApi'; export * from './SignUpApi'; +export * from './TestSessionTokenApi'; export * from './TestingTokenApi'; export * from './UserApi'; export * from './WaitlistEntryApi'; diff --git a/packages/backend/src/api/factory.ts b/packages/backend/src/api/factory.ts index 6c82869a46e..d928cacdefe 100644 --- a/packages/backend/src/api/factory.ts +++ b/packages/backend/src/api/factory.ts @@ -25,6 +25,7 @@ import { SignInTokenAPI, SignUpAPI, TestingTokenAPI, + TestSessionTokenAPI, UserAPI, WaitlistEntryAPI, WebhookAPI, @@ -88,6 +89,7 @@ export function createBackendApiClient(options: CreateBackendApiOptions) { sessions: new SessionAPI(request), signInTokens: new SignInTokenAPI(request), signUps: new SignUpAPI(request), + testSessionTokens: new TestSessionTokenAPI(request), testingTokens: new TestingTokenAPI(request), users: new UserAPI(request), waitlistEntries: new WaitlistEntryAPI(request), diff --git a/packages/backend/src/api/resources/Deserializer.ts b/packages/backend/src/api/resources/Deserializer.ts index 79fbe119836..27ef0db7671 100644 --- a/packages/backend/src/api/resources/Deserializer.ts +++ b/packages/backend/src/api/resources/Deserializer.ts @@ -33,6 +33,7 @@ import { SignInToken, SignUpAttempt, SMSMessage, + TestSessionToken, Token, User, } from '.'; @@ -169,6 +170,8 @@ function jsonToObject(item: any): any { return SamlConnection.fromJSON(item); case ObjectType.SignInToken: return SignInToken.fromJSON(item); + case ObjectType.TestSessionToken: + return TestSessionToken.fromJSON(item); case ObjectType.SignUpAttempt: return SignUpAttempt.fromJSON(item); case ObjectType.Session: diff --git a/packages/backend/src/api/resources/JSON.ts b/packages/backend/src/api/resources/JSON.ts index 075e0f6fa95..8c3bff33185 100644 --- a/packages/backend/src/api/resources/JSON.ts +++ b/packages/backend/src/api/resources/JSON.ts @@ -62,6 +62,7 @@ export const ObjectType = { Token: 'token', TotalCount: 'total_count', TestingToken: 'testing_token', + TestSessionToken: 'test_session_token', Role: 'role', Permission: 'permission', BillingPayer: 'commerce_payer', @@ -512,6 +513,16 @@ export interface SignInTokenJSON extends ClerkResourceJSON { updated_at: number; } +export interface TestSessionTokenJSON extends ClerkResourceJSON { + object: typeof ObjectType.TestSessionToken; + user_id: string; + token: string; + status: 'pending' | 'accepted' | 'revoked'; + url: string; + created_at: number; + updated_at: number; +} + export interface SignUpJSON extends ClerkResourceJSON { object: typeof ObjectType.SignUpAttempt; id: string; diff --git a/packages/backend/src/api/resources/TestSessionToken.ts b/packages/backend/src/api/resources/TestSessionToken.ts new file mode 100644 index 00000000000..a255d8702fd --- /dev/null +++ b/packages/backend/src/api/resources/TestSessionToken.ts @@ -0,0 +1,25 @@ +import type { TestSessionTokenJSON } from './JSON'; + +export class TestSessionToken { + constructor( + readonly id: string, + readonly userId: string, + readonly token: string, + readonly status: 'pending' | 'accepted' | 'revoked', + readonly url: string, + readonly createdAt: number, + readonly updatedAt: number, + ) {} + + static fromJSON(data: TestSessionTokenJSON): TestSessionToken { + return new TestSessionToken( + data.id, + data.user_id, + data.token, + data.status, + data.url, + data.created_at, + data.updated_at, + ); + } +} diff --git a/packages/backend/src/api/resources/index.ts b/packages/backend/src/api/resources/index.ts index 00e119dbb21..9d1e8fcda86 100644 --- a/packages/backend/src/api/resources/index.ts +++ b/packages/backend/src/api/resources/index.ts @@ -55,6 +55,7 @@ export * from './SignInTokens'; export * from './SignUpAttempt'; export * from './SMSMessage'; export * from './TestingToken'; +export * from './TestSessionToken'; export * from './Token'; export * from './User'; export * from './Verification'; From 28ec9d662d06bd45d90d3be2b6c1d48398b692eb Mon Sep 17 00:00:00 2001 From: Tom Milewski Date: Thu, 5 Feb 2026 17:33:08 -0500 Subject: [PATCH 2/5] chore: Add JSDoc --- .../src/api/resources/TestSessionToken.ts | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/packages/backend/src/api/resources/TestSessionToken.ts b/packages/backend/src/api/resources/TestSessionToken.ts index a255d8702fd..27bcde94834 100644 --- a/packages/backend/src/api/resources/TestSessionToken.ts +++ b/packages/backend/src/api/resources/TestSessionToken.ts @@ -1,6 +1,23 @@ import type { TestSessionTokenJSON } from './JSON'; +/** + * Represents a test session token resource. + * + * Test session tokens are used for testing purposes and allow creating sessions + * for users without requiring full authentication flows. + */ export class TestSessionToken { + /** + * Creates a new TestSessionToken instance. + * + * @param id - The unique identifier for the test session token + * @param userId - The unique identifier of the user associated with this token + * @param token - The test session token string value + * @param status - The current status of the token: 'pending', 'accepted', or 'revoked' + * @param url - The URL associated with the test session token + * @param createdAt - Unix timestamp (in milliseconds) indicating when the token was created + * @param updatedAt - Unix timestamp (in milliseconds) indicating when the token was last updated + */ constructor( readonly id: string, readonly userId: string, @@ -11,6 +28,12 @@ export class TestSessionToken { readonly updatedAt: number, ) {} + /** + * Creates a TestSessionToken instance from a JSON object. + * + * @param data - The JSON object containing test session token data + * @returns A new TestSessionToken instance + */ static fromJSON(data: TestSessionTokenJSON): TestSessionToken { return new TestSessionToken( data.id, From 2291c84d55bf1b9b00a7a88092769d962eb83f49 Mon Sep 17 00:00:00 2001 From: Tom Milewski Date: Thu, 5 Feb 2026 17:37:00 -0500 Subject: [PATCH 3/5] chore: Add redirectUrl --- packages/backend/src/api/endpoints/TestSessionTokenApi.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/backend/src/api/endpoints/TestSessionTokenApi.ts b/packages/backend/src/api/endpoints/TestSessionTokenApi.ts index 59501f0b7a1..becd607a6fb 100644 --- a/packages/backend/src/api/endpoints/TestSessionTokenApi.ts +++ b/packages/backend/src/api/endpoints/TestSessionTokenApi.ts @@ -4,6 +4,7 @@ import { AbstractAPI } from './AbstractApi'; type CreateTestSessionTokenParams = { userId: string; sessionMaxDurationInSeconds?: number; + redirectUrl?: string; }; const basePath = '/test_session_tokens'; From f03d40c8cf225e5383d00e1d7f87885819bdc2a0 Mon Sep 17 00:00:00 2001 From: Tom Milewski Date: Fri, 6 Feb 2026 17:14:32 -0500 Subject: [PATCH 4/5] chore: Rename Test Session -> Agent --- .changeset/eighty-bobcats-unite.md | 2 +- .../src/api/endpoints/AgentTokenApi.ts | 33 ++++++++++++ .../src/api/endpoints/TestSessionTokenApi.ts | 20 -------- packages/backend/src/api/endpoints/index.ts | 2 +- packages/backend/src/api/factory.ts | 7 ++- .../backend/src/api/resources/AgentToken.ts | 50 +++++++++++++++++++ .../backend/src/api/resources/Deserializer.ts | 6 +-- packages/backend/src/api/resources/JSON.ts | 6 +-- .../src/api/resources/TestSessionToken.ts | 48 ------------------ packages/backend/src/api/resources/index.ts | 3 +- 10 files changed, 97 insertions(+), 80 deletions(-) create mode 100644 packages/backend/src/api/endpoints/AgentTokenApi.ts delete mode 100644 packages/backend/src/api/endpoints/TestSessionTokenApi.ts create mode 100644 packages/backend/src/api/resources/AgentToken.ts delete mode 100644 packages/backend/src/api/resources/TestSessionToken.ts diff --git a/.changeset/eighty-bobcats-unite.md b/.changeset/eighty-bobcats-unite.md index 45e0df390c0..4e455785b43 100644 --- a/.changeset/eighty-bobcats-unite.md +++ b/.changeset/eighty-bobcats-unite.md @@ -2,4 +2,4 @@ '@clerk/backend': minor --- -Add support for Test Session Tokens API endpoint which allows developers to create test session tokens that can be used to impersonate users during automated flows. +Add support for Agent Tokens API endpoint which allows developers to create agent tokens that can be used to impersonate users through automated flows. diff --git a/packages/backend/src/api/endpoints/AgentTokenApi.ts b/packages/backend/src/api/endpoints/AgentTokenApi.ts new file mode 100644 index 00000000000..da6ea851390 --- /dev/null +++ b/packages/backend/src/api/endpoints/AgentTokenApi.ts @@ -0,0 +1,33 @@ +import type { AgentToken } from '../resources/AgentToken'; +import { AbstractAPI } from './AbstractApi'; + +type CreateAgentTokenParams = { + /** + * The ID of the user to create an agent token for. + */ + userId: string; + /** + * The maximum duration that the session which will be created by the generated agent token should last. + * By default, the duration is 30 minutes. + */ + sessionMaxDurationInSeconds?: number; + /** + * The URL to redirect to after the agent token is consumed. + */ + redirectUrl?: string; +}; + +const basePath = '/agent_tokens'; + +export class AgentTokenAPI extends AbstractAPI { + /** + * @experimental This is an experimental API for the Agent Tokens feature that is available under a private beta, and the API is subject to change. It is advised to [pin](https://clerk.com/docs/pinning) the SDK version and the clerk-js version to avoid breaking changes. + */ + public async create(params: CreateAgentTokenParams) { + return this.request({ + method: 'POST', + path: basePath, + bodyParams: params, + }); + } +} diff --git a/packages/backend/src/api/endpoints/TestSessionTokenApi.ts b/packages/backend/src/api/endpoints/TestSessionTokenApi.ts deleted file mode 100644 index becd607a6fb..00000000000 --- a/packages/backend/src/api/endpoints/TestSessionTokenApi.ts +++ /dev/null @@ -1,20 +0,0 @@ -import type { TestSessionToken } from '../resources/TestSessionToken'; -import { AbstractAPI } from './AbstractApi'; - -type CreateTestSessionTokenParams = { - userId: string; - sessionMaxDurationInSeconds?: number; - redirectUrl?: string; -}; - -const basePath = '/test_session_tokens'; - -export class TestSessionTokenAPI extends AbstractAPI { - public async create(params: CreateTestSessionTokenParams) { - return this.request({ - method: 'POST', - path: basePath, - bodyParams: params, - }); - } -} diff --git a/packages/backend/src/api/endpoints/index.ts b/packages/backend/src/api/endpoints/index.ts index c0c7211ac3f..d5bd5e7880a 100644 --- a/packages/backend/src/api/endpoints/index.ts +++ b/packages/backend/src/api/endpoints/index.ts @@ -1,4 +1,5 @@ export * from './ActorTokenApi'; +export * from './AgentTokenApi'; export * from './AccountlessApplicationsAPI'; export * from './AbstractApi'; export * from './AllowlistIdentifierApi'; @@ -24,7 +25,6 @@ export * from './SamlConnectionApi'; export * from './SessionApi'; export * from './SignInTokenApi'; export * from './SignUpApi'; -export * from './TestSessionTokenApi'; export * from './TestingTokenApi'; export * from './UserApi'; export * from './WaitlistEntryApi'; diff --git a/packages/backend/src/api/factory.ts b/packages/backend/src/api/factory.ts index d928cacdefe..d7d90e7fca4 100644 --- a/packages/backend/src/api/factory.ts +++ b/packages/backend/src/api/factory.ts @@ -1,6 +1,7 @@ import { AccountlessApplicationAPI, ActorTokenAPI, + AgentTokenAPI, AllowlistIdentifierAPI, APIKeysAPI, BetaFeaturesAPI, @@ -25,7 +26,6 @@ import { SignInTokenAPI, SignUpAPI, TestingTokenAPI, - TestSessionTokenAPI, UserAPI, WaitlistEntryAPI, WebhookAPI, @@ -45,6 +45,10 @@ export function createBackendApiClient(options: CreateBackendApiOptions) { buildRequest({ ...options, requireSecretKey: false }), ), actorTokens: new ActorTokenAPI(request), + /** + * @experimental This is an experimental API for the Agent Tokens feature that is available under a private beta, and the API is subject to change. It is advised to [pin](https://clerk.com/docs/pinning) the SDK version and the clerk-js version to avoid breaking changes. + */ + agentTokens: new AgentTokenAPI(request), allowlistIdentifiers: new AllowlistIdentifierAPI(request), apiKeys: new APIKeysAPI( buildRequest({ @@ -89,7 +93,6 @@ export function createBackendApiClient(options: CreateBackendApiOptions) { sessions: new SessionAPI(request), signInTokens: new SignInTokenAPI(request), signUps: new SignUpAPI(request), - testSessionTokens: new TestSessionTokenAPI(request), testingTokens: new TestingTokenAPI(request), users: new UserAPI(request), waitlistEntries: new WaitlistEntryAPI(request), diff --git a/packages/backend/src/api/resources/AgentToken.ts b/packages/backend/src/api/resources/AgentToken.ts new file mode 100644 index 00000000000..519e03e20d7 --- /dev/null +++ b/packages/backend/src/api/resources/AgentToken.ts @@ -0,0 +1,50 @@ +import type { AgentTokenJSON } from './JSON'; + +/** + * Represents a agent token resource. + * + * Agent tokens are used for testing purposes and allow creating sessions + * for users without requiring full authentication flows. + */ +export class AgentToken { + constructor( + /** + * The unique identifier for the agent token. + */ + readonly id: string, + /** + * The unique identifier for the user associated with this token. + */ + readonly userId: string, + /** + * The agent token string value. + */ + readonly token: string, + /** + * The current status of the token: 'pending', 'accepted', or 'revoked'. + */ + readonly status: 'pending' | 'accepted' | 'revoked', + /** + * The URL associated with the agent token. + */ + readonly url: string, + /** + * Unix timestamp (in milliseconds) indicating when the token was created. + */ + readonly createdAt: number, + /** + * Unix timestamp (in milliseconds) indicating when the token was last updated. + */ + readonly updatedAt: number, + ) {} + + /** + * Creates a AgentToken instance from a JSON object. + * + * @param data - The JSON object containing agent token data + * @returns A new AgentToken instance + */ + static fromJSON(data: AgentTokenJSON): AgentToken { + return new AgentToken(data.id, data.user_id, data.token, data.status, data.url, data.created_at, data.updated_at); + } +} diff --git a/packages/backend/src/api/resources/Deserializer.ts b/packages/backend/src/api/resources/Deserializer.ts index 27ef0db7671..d8b0a938133 100644 --- a/packages/backend/src/api/resources/Deserializer.ts +++ b/packages/backend/src/api/resources/Deserializer.ts @@ -1,5 +1,6 @@ import { ActorToken, + AgentToken, AllowlistIdentifier, APIKey, BlocklistIdentifier, @@ -33,7 +34,6 @@ import { SignInToken, SignUpAttempt, SMSMessage, - TestSessionToken, Token, User, } from '.'; @@ -170,8 +170,8 @@ function jsonToObject(item: any): any { return SamlConnection.fromJSON(item); case ObjectType.SignInToken: return SignInToken.fromJSON(item); - case ObjectType.TestSessionToken: - return TestSessionToken.fromJSON(item); + case ObjectType.AgentToken: + return AgentToken.fromJSON(item); case ObjectType.SignUpAttempt: return SignUpAttempt.fromJSON(item); case ObjectType.Session: diff --git a/packages/backend/src/api/resources/JSON.ts b/packages/backend/src/api/resources/JSON.ts index 8c3bff33185..feea7ce531a 100644 --- a/packages/backend/src/api/resources/JSON.ts +++ b/packages/backend/src/api/resources/JSON.ts @@ -62,7 +62,7 @@ export const ObjectType = { Token: 'token', TotalCount: 'total_count', TestingToken: 'testing_token', - TestSessionToken: 'test_session_token', + AgentToken: 'agent_token', Role: 'role', Permission: 'permission', BillingPayer: 'commerce_payer', @@ -513,8 +513,8 @@ export interface SignInTokenJSON extends ClerkResourceJSON { updated_at: number; } -export interface TestSessionTokenJSON extends ClerkResourceJSON { - object: typeof ObjectType.TestSessionToken; +export interface AgentTokenJSON extends ClerkResourceJSON { + object: typeof ObjectType.AgentToken; user_id: string; token: string; status: 'pending' | 'accepted' | 'revoked'; diff --git a/packages/backend/src/api/resources/TestSessionToken.ts b/packages/backend/src/api/resources/TestSessionToken.ts deleted file mode 100644 index 27bcde94834..00000000000 --- a/packages/backend/src/api/resources/TestSessionToken.ts +++ /dev/null @@ -1,48 +0,0 @@ -import type { TestSessionTokenJSON } from './JSON'; - -/** - * Represents a test session token resource. - * - * Test session tokens are used for testing purposes and allow creating sessions - * for users without requiring full authentication flows. - */ -export class TestSessionToken { - /** - * Creates a new TestSessionToken instance. - * - * @param id - The unique identifier for the test session token - * @param userId - The unique identifier of the user associated with this token - * @param token - The test session token string value - * @param status - The current status of the token: 'pending', 'accepted', or 'revoked' - * @param url - The URL associated with the test session token - * @param createdAt - Unix timestamp (in milliseconds) indicating when the token was created - * @param updatedAt - Unix timestamp (in milliseconds) indicating when the token was last updated - */ - constructor( - readonly id: string, - readonly userId: string, - readonly token: string, - readonly status: 'pending' | 'accepted' | 'revoked', - readonly url: string, - readonly createdAt: number, - readonly updatedAt: number, - ) {} - - /** - * Creates a TestSessionToken instance from a JSON object. - * - * @param data - The JSON object containing test session token data - * @returns A new TestSessionToken instance - */ - static fromJSON(data: TestSessionTokenJSON): TestSessionToken { - return new TestSessionToken( - data.id, - data.user_id, - data.token, - data.status, - data.url, - data.created_at, - data.updated_at, - ); - } -} diff --git a/packages/backend/src/api/resources/index.ts b/packages/backend/src/api/resources/index.ts index 9d1e8fcda86..fb2bc91dff4 100644 --- a/packages/backend/src/api/resources/index.ts +++ b/packages/backend/src/api/resources/index.ts @@ -1,5 +1,6 @@ export * from './AccountlessApplication'; export * from './ActorToken'; +export * from './AgentToken'; export * from './AllowlistIdentifier'; export * from './APIKey'; export * from './BlocklistIdentifier'; @@ -54,8 +55,6 @@ export * from './Session'; export * from './SignInTokens'; export * from './SignUpAttempt'; export * from './SMSMessage'; -export * from './TestingToken'; -export * from './TestSessionToken'; export * from './Token'; export * from './User'; export * from './Verification'; From f76dbf133a9feda9589db6c5c07392dcd808cff5 Mon Sep 17 00:00:00 2001 From: Tom Milewski Date: Fri, 6 Feb 2026 17:25:26 -0500 Subject: [PATCH 5/5] fix: Re-add testing-token export --- packages/backend/src/api/resources/index.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/backend/src/api/resources/index.ts b/packages/backend/src/api/resources/index.ts index fb2bc91dff4..e1c28b19df1 100644 --- a/packages/backend/src/api/resources/index.ts +++ b/packages/backend/src/api/resources/index.ts @@ -55,6 +55,7 @@ export * from './Session'; export * from './SignInTokens'; export * from './SignUpAttempt'; export * from './SMSMessage'; +export * from './TestingToken'; export * from './Token'; export * from './User'; export * from './Verification';