Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions .changeset/three-badgers-hunt.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
---
'@clerk/backend': patch
---

Adds the ability to create and revoke actor tokens to the Backend API client.


```ts
import { createClerkClient } from '@clerk/backend';

const clerkClient = createClerkClient(...);

const { id } = await clerkClient.actorTokens.create({...});
await clerkClient.actorTokens.revoke(id);
```
62 changes: 62 additions & 0 deletions packages/backend/src/api/endpoints/ActorTokenApi.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import { joinPaths } from '../../util/path';
import type { ActorToken } from '../resources/ActorToken';
import { AbstractAPI } from './AbstractApi';

const basePath = '/actor_tokens';

type ActorTokenActorCreateParams = {
/**
* The ID of the actor.
*/
sub: string;
/**
* Additional properties of the actor.
*/
additionalProperties?: { [k: string]: any };
};

type ActorTokenCreateParams = {
/**
* The ID of the user being impersonated.
*/
userId: string;
/**
* The actor payload. It needs to include a sub property which should contain the ID of the actor.
*
* @remarks
* This whole payload will be also included in the JWT session token.
*/
actor: ActorTokenActorCreateParams;
/**
* Optional parameter to specify the life duration of the actor token in seconds.
*
* @remarks
* By default, the duration is 1 hour.
*/
expiresInSeconds?: number | undefined;
/**
* The maximum duration that the session which will be created by the generated actor token should last.
*
* @remarks
* By default, the duration of a session created via an actor token, lasts 30 minutes.
*/
sessionMaxDurationInSeconds?: number | undefined;
};

export class ActorTokenAPI extends AbstractAPI {
public async create(params: ActorTokenCreateParams) {
return this.request<ActorToken>({
method: 'POST',
path: basePath,
bodyParams: params,
});
}

public async revoke(actorTokenId: string) {
this.requireId(actorTokenId);
return this.request<ActorToken>({
method: 'POST',
path: joinPaths(basePath, actorTokenId, 'revoke'),
});
}
}
1 change: 1 addition & 0 deletions packages/backend/src/api/endpoints/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export * from './ActorTokenApi';
export * from './AccountlessApplicationsAPI';
export * from './AbstractApi';
export * from './AllowlistIdentifierApi';
Expand Down
2 changes: 2 additions & 0 deletions packages/backend/src/api/factory.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import {
AccountlessApplicationAPI,
ActorTokenAPI,
AllowlistIdentifierAPI,
BlocklistIdentifierAPI,
ClientAPI,
Expand Down Expand Up @@ -32,6 +33,7 @@ export function createBackendApiClient(options: CreateBackendApiOptions) {
__experimental_accountlessApplications: new AccountlessApplicationAPI(
buildRequest({ ...options, requireSecretKey: false }),
),
actorTokens: new ActorTokenAPI(request),
allowlistIdentifiers: new AllowlistIdentifierAPI(request),
blocklistIdentifiers: new BlocklistIdentifierAPI(request),
clients: new ClientAPI(request),
Expand Down
28 changes: 28 additions & 0 deletions packages/backend/src/api/resources/ActorToken.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import type { ActorTokenStatus } from './Enums';
import type { ActorTokenJSON } from './JSON';

export class ActorToken {
constructor(
readonly id: string,
readonly status: ActorTokenStatus,
readonly userId: string,
readonly actor: Record<string, unknown> | null,
readonly token: string | null | undefined,
readonly url: string | null | undefined,
readonly createdAt: number,
readonly updatedAt: number,
) {}

static fromJSON(data: ActorTokenJSON): ActorToken {
return new ActorToken(
data.id,
data.status,
data.user_id,
data.actor,
data.token,
data.url,
data.created_at,
data.updated_at,
);
}
}
3 changes: 3 additions & 0 deletions packages/backend/src/api/resources/Deserializer.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import {
ActorToken,
AllowlistIdentifier,
BlocklistIdentifier,
Client,
Expand Down Expand Up @@ -73,6 +74,8 @@ function jsonToObject(item: any): any {
switch (item.object) {
case ObjectType.AccountlessApplication:
return AccountlessApplication.fromJSON(item);
case ObjectType.ActorToken:
return ActorToken.fromJSON(item);
case ObjectType.AllowlistIdentifier:
return AllowlistIdentifier.fromJSON(item);
case ObjectType.BlocklistIdentifier:
Expand Down
7 changes: 7 additions & 0 deletions packages/backend/src/api/resources/Enums.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,13 @@ export type SignUpStatus = 'missing_requirements' | 'complete' | 'abandoned';

export type InvitationStatus = 'pending' | 'accepted' | 'revoked' | 'expired';

export const ActorTokenStatus = {
Pending: 'pending',
Accepted: 'accepted',
Revoked: 'revoked',
} as const;
export type ActorTokenStatus = (typeof ActorTokenStatus)[keyof typeof ActorTokenStatus];

export type AllowlistIdentifierType = 'email_address' | 'phone_number' | 'web3_wallet';

export type BlocklistIdentifierType = AllowlistIdentifierType;
14 changes: 14 additions & 0 deletions packages/backend/src/api/resources/JSON.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import type {
ActorTokenStatus,
AllowlistIdentifierType,
BlocklistIdentifierType,
InvitationStatus,
Expand All @@ -13,6 +14,7 @@ import type {

export const ObjectType = {
AccountlessApplication: 'accountless_application',
ActorToken: 'actor_token',
AllowlistIdentifier: 'allowlist_identifier',
BlocklistIdentifier: 'blocklist_identifier',
Client: 'client',
Expand Down Expand Up @@ -74,6 +76,18 @@ export interface AccountlessApplicationJSON extends ClerkResourceJSON {
api_keys_url: string;
}

export interface ActorTokenJSON extends ClerkResourceJSON {
object: typeof ObjectType.ActorToken;
id: string;
status: ActorTokenStatus;
user_id: string;
actor: Record<string, unknown> | null;
token?: string | null;
url?: string | null;
created_at: number;
updated_at: number;
}

export interface AllowlistIdentifierJSON extends ClerkResourceJSON {
object: typeof ObjectType.AllowlistIdentifier;
identifier: string;
Expand Down
1 change: 1 addition & 0 deletions packages/backend/src/api/resources/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export * from './ActorToken';
export * from './AccountlessApplication';
export * from './AllowlistIdentifier';
export * from './BlocklistIdentifier';
Expand Down
2 changes: 2 additions & 0 deletions packages/backend/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ export type { VerifyTokenOptions } from './tokens/verify';
* JSON types
*/
export type {
ActorTokenJSON,
AccountlessApplicationJSON,
ClerkResourceJSON,
TokenJSON,
Expand Down Expand Up @@ -98,6 +99,7 @@ export type {
* Resources
*/
export type {
ActorToken,
AccountlessApplication,
AllowlistIdentifier,
BlocklistIdentifier,
Expand Down