diff --git a/.changeset/eighty-bobcats-unite.md b/.changeset/eighty-bobcats-unite.md new file mode 100644 index 00000000000..4e455785b43 --- /dev/null +++ b/.changeset/eighty-bobcats-unite.md @@ -0,0 +1,5 @@ +--- +'@clerk/backend': minor +--- + +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/index.ts b/packages/backend/src/api/endpoints/index.ts index c03875a427d..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'; diff --git a/packages/backend/src/api/factory.ts b/packages/backend/src/api/factory.ts index 6c82869a46e..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, @@ -44,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({ 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 79fbe119836..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, @@ -169,6 +170,8 @@ function jsonToObject(item: any): any { return SamlConnection.fromJSON(item); case ObjectType.SignInToken: return SignInToken.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 075e0f6fa95..feea7ce531a 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', + AgentToken: 'agent_token', Role: 'role', Permission: 'permission', BillingPayer: 'commerce_payer', @@ -512,6 +513,16 @@ export interface SignInTokenJSON extends ClerkResourceJSON { updated_at: number; } +export interface AgentTokenJSON extends ClerkResourceJSON { + object: typeof ObjectType.AgentToken; + 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/index.ts b/packages/backend/src/api/resources/index.ts index 00e119dbb21..e1c28b19df1 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';