From ef07008dac9b5f6d1638074d37a95f34b31db25e Mon Sep 17 00:00:00 2001 From: "Garen J. Torikian" Date: Thu, 26 Mar 2026 11:46:32 -0400 Subject: [PATCH 01/17] Introduce AdminPortal --- src/admin-portal/admin-portal.spec.ts | 46 +++++++++++++++++++ src/admin-portal/admin-portal.ts | 32 +++++++++++++ .../fixtures/generate-link.fixture.json | 12 +++++ .../fixtures/intent-options.fixture.json | 6 +++ .../portal-link-response.fixture.json | 3 ++ .../fixtures/sso-intent-options.fixture.json | 4 ++ .../interfaces/generate-link.interface.ts | 38 +++++++++++++++ src/admin-portal/interfaces/index.ts | 6 +++ .../interfaces/intent-options.interface.ts | 15 ++++++ .../portal-link-response.interface.ts | 10 ++++ .../sso-intent-options.interface.ts | 13 ++++++ .../serializers/generate-link.serializer.ts | 36 +++++++++++++++ .../serializers/intent-options.serializer.ts | 22 +++++++++ .../portal-link-response.serializer.ts | 18 ++++++++ .../sso-intent-options.serializer.ts | 20 ++++++++ .../generate-portal-link-intent.interface.ts | 1 + src/portal/portal.ts | 1 + 17 files changed, 283 insertions(+) create mode 100644 src/admin-portal/admin-portal.spec.ts create mode 100644 src/admin-portal/admin-portal.ts create mode 100644 src/admin-portal/fixtures/generate-link.fixture.json create mode 100644 src/admin-portal/fixtures/intent-options.fixture.json create mode 100644 src/admin-portal/fixtures/portal-link-response.fixture.json create mode 100644 src/admin-portal/fixtures/sso-intent-options.fixture.json create mode 100644 src/admin-portal/interfaces/generate-link.interface.ts create mode 100644 src/admin-portal/interfaces/index.ts create mode 100644 src/admin-portal/interfaces/intent-options.interface.ts create mode 100644 src/admin-portal/interfaces/portal-link-response.interface.ts create mode 100644 src/admin-portal/interfaces/sso-intent-options.interface.ts create mode 100644 src/admin-portal/serializers/generate-link.serializer.ts create mode 100644 src/admin-portal/serializers/intent-options.serializer.ts create mode 100644 src/admin-portal/serializers/portal-link-response.serializer.ts create mode 100644 src/admin-portal/serializers/sso-intent-options.serializer.ts diff --git a/src/admin-portal/admin-portal.spec.ts b/src/admin-portal/admin-portal.spec.ts new file mode 100644 index 000000000..5743c9aa0 --- /dev/null +++ b/src/admin-portal/admin-portal.spec.ts @@ -0,0 +1,46 @@ +// This file is auto-generated by oagen. Do not edit. + +import fetch from 'jest-fetch-mock'; +import { + fetchOnce, + fetchURL, + fetchMethod, + fetchBody, + testUnauthorized, +} from '../common/utils/test-utils'; +import { WorkOS } from '../workos'; + +import portalLinkResponseFixture from './fixtures/portal-link-response.fixture.json'; + +const workos = new WorkOS('sk_test_Sz3IQjepeSWaI4cMS4ms4sMuU'); + +describe('AdminPortal', () => { + beforeEach(() => fetch.resetMocks()); + + describe('generatePortalLink', () => { + it('sends the correct request and returns result', async () => { + fetchOnce(portalLinkResponseFixture); + + const result = await workos.adminPortal.generatePortalLink({ + organization: 'test_organization', + }); + + expect(fetchMethod()).toBe('POST'); + expect(new URL(String(fetchURL())).pathname).toBe( + '/portal/generate_link', + ); + expect(fetchBody()).toEqual( + expect.objectContaining({ organization: 'test_organization' }), + ); + expect(result.link).toBe( + 'https://setup.workos.com?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...', + ); + }); + + testUnauthorized(() => + workos.adminPortal.generatePortalLink({ + organization: 'test_organization', + }), + ); + }); +}); diff --git a/src/admin-portal/admin-portal.ts b/src/admin-portal/admin-portal.ts new file mode 100644 index 000000000..bfcfcf291 --- /dev/null +++ b/src/admin-portal/admin-portal.ts @@ -0,0 +1,32 @@ +// This file is auto-generated by oagen. Do not edit. + +import type { WorkOS } from '../workos'; +import type { + PortalLinkResponse, + PortalLinkResponseWire, +} from './interfaces/portal-link-response.interface'; +import type { GenerateLink } from './interfaces/generate-link.interface'; +import { deserializePortalLinkResponse } from './serializers/portal-link-response.serializer'; +import { serializeGenerateLink } from './serializers/generate-link.serializer'; + +export class AdminPortal { + constructor(private readonly workos: WorkOS) {} + + /** + * Generate a Portal Link + * + * Generate a Portal Link scoped to an Organization. + * @param payload - Object containing organization. + * @returns {PortalLinkResponse} + * @throws {BadRequestException} 400 + * @throws {NotFoundException} 404 + * @throws {UnprocessableEntityException} 422 + */ + async generatePortalLink(payload: GenerateLink): Promise { + const { data } = await this.workos.post( + '/portal/generate_link', + serializeGenerateLink(payload), + ); + return deserializePortalLinkResponse(data); + } +} diff --git a/src/admin-portal/fixtures/generate-link.fixture.json b/src/admin-portal/fixtures/generate-link.fixture.json new file mode 100644 index 000000000..f7fd7b0ad --- /dev/null +++ b/src/admin-portal/fixtures/generate-link.fixture.json @@ -0,0 +1,12 @@ +{ + "return_url": "https://example.com/admin-portal/return", + "success_url": "https://example.com/admin-portal/success", + "organization": "org_01EHZNVPK3SFK441A1RGBFSHRT", + "intent": "sso", + "intent_options": { + "sso": { + "bookmark_slug": "chatgpt", + "provider_type": "GoogleSAML" + } + } +} diff --git a/src/admin-portal/fixtures/intent-options.fixture.json b/src/admin-portal/fixtures/intent-options.fixture.json new file mode 100644 index 000000000..31bad9130 --- /dev/null +++ b/src/admin-portal/fixtures/intent-options.fixture.json @@ -0,0 +1,6 @@ +{ + "sso": { + "bookmark_slug": "chatgpt", + "provider_type": "GoogleSAML" + } +} diff --git a/src/admin-portal/fixtures/portal-link-response.fixture.json b/src/admin-portal/fixtures/portal-link-response.fixture.json new file mode 100644 index 000000000..811f32a96 --- /dev/null +++ b/src/admin-portal/fixtures/portal-link-response.fixture.json @@ -0,0 +1,3 @@ +{ + "link": "https://setup.workos.com?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." +} diff --git a/src/admin-portal/fixtures/sso-intent-options.fixture.json b/src/admin-portal/fixtures/sso-intent-options.fixture.json new file mode 100644 index 000000000..79ab315ed --- /dev/null +++ b/src/admin-portal/fixtures/sso-intent-options.fixture.json @@ -0,0 +1,4 @@ +{ + "bookmark_slug": "chatgpt", + "provider_type": "GoogleSAML" +} diff --git a/src/admin-portal/interfaces/generate-link.interface.ts b/src/admin-portal/interfaces/generate-link.interface.ts new file mode 100644 index 000000000..89e47e23f --- /dev/null +++ b/src/admin-portal/interfaces/generate-link.interface.ts @@ -0,0 +1,38 @@ +// This file is auto-generated by oagen. Do not edit. + +import type { + IntentOptions, + IntentOptionsResponse, +} from './intent-options.interface'; +import type { GenerateLinkIntent } from '../../common/interfaces/generate-link-intent.interface'; + +export interface GenerateLink { + /** The URL to go to when an admin clicks on your logo in the Admin Portal. If not specified, the return URL configured on the [Redirects](https://dashboard.workos.com/redirects) page will be used. */ + returnUrl?: string; + /** The URL to redirect the admin to when they finish setup. If not specified, the success URL configured on the [Redirects](https://dashboard.workos.com/redirects) page will be used. */ + successUrl?: string; + /** An [Organization](https://workos.com/docs/reference/organization) identifier. */ + organization: string; + /** + * + * The intent of the Admin Portal. + * - `sso` - Launch Admin Portal for creating SSO connections + * - `dsync` - Launch Admin Portal for creating Directory Sync connections + * - `audit_logs` - Launch Admin Portal for viewing Audit Logs + * - `log_streams` - Launch Admin Portal for creating Log Streams + * - `domain_verification` - Launch Admin Portal for Domain Verification + * - `certificate_renewal` - Launch Admin Portal for renewing SAML Certificates + * - `bring_your_own_key` - Launch Admin Portal for configuring Bring Your Own Key + */ + intent?: GenerateLinkIntent; + /** Options to configure the Admin Portal based on the intent. */ + intentOptions?: IntentOptions; +} + +export interface GenerateLinkResponse { + return_url?: string; + success_url?: string; + organization: string; + intent?: GenerateLinkIntent; + intent_options?: IntentOptionsResponse; +} diff --git a/src/admin-portal/interfaces/index.ts b/src/admin-portal/interfaces/index.ts new file mode 100644 index 000000000..eafac3f0c --- /dev/null +++ b/src/admin-portal/interfaces/index.ts @@ -0,0 +1,6 @@ +// This file is auto-generated by oagen. Do not edit. + +export * from './generate-link.interface'; +export * from './intent-options.interface'; +export * from './portal-link-response.interface'; +export * from './sso-intent-options.interface'; diff --git a/src/admin-portal/interfaces/intent-options.interface.ts b/src/admin-portal/interfaces/intent-options.interface.ts new file mode 100644 index 000000000..229e9e1af --- /dev/null +++ b/src/admin-portal/interfaces/intent-options.interface.ts @@ -0,0 +1,15 @@ +// This file is auto-generated by oagen. Do not edit. + +import type { + SSOIntentOptions, + SSOIntentOptionsResponse, +} from './sso-intent-options.interface'; + +export interface IntentOptions { + /** SSO-specific options for the Admin Portal. */ + sso: SSOIntentOptions; +} + +export interface IntentOptionsResponse { + sso: SSOIntentOptionsResponse; +} diff --git a/src/admin-portal/interfaces/portal-link-response.interface.ts b/src/admin-portal/interfaces/portal-link-response.interface.ts new file mode 100644 index 000000000..a7ea95f54 --- /dev/null +++ b/src/admin-portal/interfaces/portal-link-response.interface.ts @@ -0,0 +1,10 @@ +// This file is auto-generated by oagen. Do not edit. + +export interface PortalLinkResponse { + /** An ephemeral link to initiate the Admin Portal. */ + link: string; +} + +export interface PortalLinkResponseWire { + link: string; +} diff --git a/src/admin-portal/interfaces/sso-intent-options.interface.ts b/src/admin-portal/interfaces/sso-intent-options.interface.ts new file mode 100644 index 000000000..6450eabb9 --- /dev/null +++ b/src/admin-portal/interfaces/sso-intent-options.interface.ts @@ -0,0 +1,13 @@ +// This file is auto-generated by oagen. Do not edit. + +export interface SSOIntentOptions { + /** The bookmark slug to use for SSO. */ + bookmarkSlug?: string; + /** The SSO provider type to configure. */ + providerType?: 'GoogleSAML'; +} + +export interface SSOIntentOptionsResponse { + bookmark_slug?: string; + provider_type?: 'GoogleSAML'; +} diff --git a/src/admin-portal/serializers/generate-link.serializer.ts b/src/admin-portal/serializers/generate-link.serializer.ts new file mode 100644 index 000000000..c96f34ca9 --- /dev/null +++ b/src/admin-portal/serializers/generate-link.serializer.ts @@ -0,0 +1,36 @@ +// This file is auto-generated by oagen. Do not edit. + +import type { + GenerateLink, + GenerateLinkResponse, +} from '../interfaces/generate-link.interface'; +import { + deserializeIntentOptions, + serializeIntentOptions, +} from './intent-options.serializer'; + +export const deserializeGenerateLink = ( + response: GenerateLinkResponse, +): GenerateLink => ({ + returnUrl: response.return_url, + successUrl: response.success_url, + organization: response.organization, + intent: response.intent, + intentOptions: + response.intent_options != null + ? deserializeIntentOptions(response.intent_options) + : undefined, +}); + +export const serializeGenerateLink = ( + model: GenerateLink, +): GenerateLinkResponse => ({ + return_url: model.returnUrl, + success_url: model.successUrl, + organization: model.organization, + intent: model.intent, + intent_options: + model.intentOptions != null + ? serializeIntentOptions(model.intentOptions) + : undefined, +}); diff --git a/src/admin-portal/serializers/intent-options.serializer.ts b/src/admin-portal/serializers/intent-options.serializer.ts new file mode 100644 index 000000000..a7d16e1df --- /dev/null +++ b/src/admin-portal/serializers/intent-options.serializer.ts @@ -0,0 +1,22 @@ +// This file is auto-generated by oagen. Do not edit. + +import type { + IntentOptions, + IntentOptionsResponse, +} from '../interfaces/intent-options.interface'; +import { + deserializeSSOIntentOptions, + serializeSSOIntentOptions, +} from './sso-intent-options.serializer'; + +export const deserializeIntentOptions = ( + response: IntentOptionsResponse, +): IntentOptions => ({ + sso: deserializeSSOIntentOptions(response.sso), +}); + +export const serializeIntentOptions = ( + model: IntentOptions, +): IntentOptionsResponse => ({ + sso: serializeSSOIntentOptions(model.sso), +}); diff --git a/src/admin-portal/serializers/portal-link-response.serializer.ts b/src/admin-portal/serializers/portal-link-response.serializer.ts new file mode 100644 index 000000000..75a96c945 --- /dev/null +++ b/src/admin-portal/serializers/portal-link-response.serializer.ts @@ -0,0 +1,18 @@ +// This file is auto-generated by oagen. Do not edit. + +import type { + PortalLinkResponse, + PortalLinkResponseWire, +} from '../interfaces/portal-link-response.interface'; + +export const deserializePortalLinkResponse = ( + response: PortalLinkResponseWire, +): PortalLinkResponse => ({ + link: response.link, +}); + +export const serializePortalLinkResponse = ( + model: PortalLinkResponse, +): PortalLinkResponseWire => ({ + link: model.link, +}); diff --git a/src/admin-portal/serializers/sso-intent-options.serializer.ts b/src/admin-portal/serializers/sso-intent-options.serializer.ts new file mode 100644 index 000000000..5c36252fa --- /dev/null +++ b/src/admin-portal/serializers/sso-intent-options.serializer.ts @@ -0,0 +1,20 @@ +// This file is auto-generated by oagen. Do not edit. + +import type { + SSOIntentOptions, + SSOIntentOptionsResponse, +} from '../interfaces/sso-intent-options.interface'; + +export const deserializeSSOIntentOptions = ( + response: SSOIntentOptionsResponse, +): SSOIntentOptions => ({ + bookmarkSlug: response.bookmark_slug, + providerType: response.provider_type, +}); + +export const serializeSSOIntentOptions = ( + model: SSOIntentOptions, +): SSOIntentOptionsResponse => ({ + bookmark_slug: model.bookmarkSlug, + provider_type: model.providerType, +}); diff --git a/src/portal/interfaces/generate-portal-link-intent.interface.ts b/src/portal/interfaces/generate-portal-link-intent.interface.ts index 5b1dc2e98..2ed62e10e 100644 --- a/src/portal/interfaces/generate-portal-link-intent.interface.ts +++ b/src/portal/interfaces/generate-portal-link-intent.interface.ts @@ -1,3 +1,4 @@ +/** @deprecated Use `AdminPortal` instead. */ export enum GeneratePortalLinkIntent { AuditLogs = 'audit_logs', DomainVerification = 'domain_verification', diff --git a/src/portal/portal.ts b/src/portal/portal.ts index fb466131f..63eb25b39 100644 --- a/src/portal/portal.ts +++ b/src/portal/portal.ts @@ -1,6 +1,7 @@ import { WorkOS } from '../workos'; import { GeneratePortalLinkIntent } from './interfaces/generate-portal-link-intent.interface'; +/** @deprecated Use `AdminPortal` instead. */ export class Portal { constructor(private readonly workos: WorkOS) {} From 44a8b0121d2799ce79fbecf71e795b56c2c538ff Mon Sep 17 00:00:00 2001 From: "Garen J. Torikian" Date: Thu, 26 Mar 2026 14:24:19 -0400 Subject: [PATCH 02/17] Introduce Permissions --- src/authorization/authorization.ts | 5 + .../authorization-permission.fixture.json | 11 ++ ...eate-authorization-permission.fixture.json | 6 + ...list-authorization-permission.fixture.json | 19 +++ .../fixtures/permission.fixture.json | 11 ++ ...date-authorization-permission.fixture.json | 4 + .../authorization-permission.interface.ts | 34 ++++ ...eate-authorization-permission.interface.ts | 19 +++ src/permissions/interfaces/index.ts | 5 + .../interfaces/permission.interface.ts | 9 + ...date-authorization-permission.interface.ts | 10 ++ src/permissions/permissions.spec.ts | 160 ++++++++++++++++++ src/permissions/permissions.ts | 117 +++++++++++++ .../authorization-permission.serializer.ts | 34 ++++ ...ate-authorization-permission.serializer.ts | 24 +++ .../serializers/permission.serializer.ts | 6 + ...ate-authorization-permission.serializer.ts | 6 + 17 files changed, 480 insertions(+) create mode 100644 src/permissions/fixtures/authorization-permission.fixture.json create mode 100644 src/permissions/fixtures/create-authorization-permission.fixture.json create mode 100644 src/permissions/fixtures/list-authorization-permission.fixture.json create mode 100644 src/permissions/fixtures/permission.fixture.json create mode 100644 src/permissions/fixtures/update-authorization-permission.fixture.json create mode 100644 src/permissions/interfaces/authorization-permission.interface.ts create mode 100644 src/permissions/interfaces/create-authorization-permission.interface.ts create mode 100644 src/permissions/interfaces/index.ts create mode 100644 src/permissions/interfaces/permission.interface.ts create mode 100644 src/permissions/interfaces/update-authorization-permission.interface.ts create mode 100644 src/permissions/permissions.spec.ts create mode 100644 src/permissions/permissions.ts create mode 100644 src/permissions/serializers/authorization-permission.serializer.ts create mode 100644 src/permissions/serializers/create-authorization-permission.serializer.ts create mode 100644 src/permissions/serializers/permission.serializer.ts create mode 100644 src/permissions/serializers/update-authorization-permission.serializer.ts diff --git a/src/authorization/authorization.ts b/src/authorization/authorization.ts index 534a31e71..158c060a6 100644 --- a/src/authorization/authorization.ts +++ b/src/authorization/authorization.ts @@ -230,6 +230,7 @@ export class Authorization { ); } + /** @deprecated Use `workos.permissions.create()` instead. */ async createPermission( options: CreatePermissionOptions, ): Promise { @@ -240,6 +241,7 @@ export class Authorization { return deserializePermission(data); } + /** @deprecated Use `workos.permissions.list()` instead. */ async listPermissions( options?: ListPermissionsOptions, ): Promise { @@ -257,6 +259,7 @@ export class Authorization { }; } + /** @deprecated Use `workos.permissions.find()` instead. */ async getPermission(slug: string): Promise { const { data } = await this.workos.get( `/authorization/permissions/${slug}`, @@ -264,6 +267,7 @@ export class Authorization { return deserializePermission(data); } + /** @deprecated Use `workos.permissions.update()` instead. */ async updatePermission( slug: string, options: UpdatePermissionOptions, @@ -275,6 +279,7 @@ export class Authorization { return deserializePermission(data); } + /** @deprecated Use `workos.permissions.delete()` instead. */ async deletePermission(slug: string): Promise { await this.workos.delete(`/authorization/permissions/${slug}`); } diff --git a/src/permissions/fixtures/authorization-permission.fixture.json b/src/permissions/fixtures/authorization-permission.fixture.json new file mode 100644 index 000000000..0424eed6e --- /dev/null +++ b/src/permissions/fixtures/authorization-permission.fixture.json @@ -0,0 +1,11 @@ +{ + "object": "permission", + "id": "perm_01HXYZ123456789ABCDEFGHIJ", + "slug": "documents:read", + "name": "View Documents", + "description": "Allows viewing document contents", + "system": false, + "resource_type_slug": "workspace", + "created_at": "2026-01-15T12:00:00.000Z", + "updated_at": "2026-01-15T12:00:00.000Z" +} diff --git a/src/permissions/fixtures/create-authorization-permission.fixture.json b/src/permissions/fixtures/create-authorization-permission.fixture.json new file mode 100644 index 000000000..6c9dc2aed --- /dev/null +++ b/src/permissions/fixtures/create-authorization-permission.fixture.json @@ -0,0 +1,6 @@ +{ + "slug": "documents:read", + "name": "View Documents", + "description": "Allows viewing document contents", + "resource_type_slug": "document" +} diff --git a/src/permissions/fixtures/list-authorization-permission.fixture.json b/src/permissions/fixtures/list-authorization-permission.fixture.json new file mode 100644 index 000000000..348f9d773 --- /dev/null +++ b/src/permissions/fixtures/list-authorization-permission.fixture.json @@ -0,0 +1,19 @@ +{ + "data": [ + { + "object": "permission", + "id": "perm_01HXYZ123456789ABCDEFGHIJ", + "slug": "documents:read", + "name": "View Documents", + "description": "Allows viewing document contents", + "system": false, + "resource_type_slug": "workspace", + "created_at": "2026-01-15T12:00:00.000Z", + "updated_at": "2026-01-15T12:00:00.000Z" + } + ], + "list_metadata": { + "before": null, + "after": null + } +} diff --git a/src/permissions/fixtures/permission.fixture.json b/src/permissions/fixtures/permission.fixture.json new file mode 100644 index 000000000..e4ac8b822 --- /dev/null +++ b/src/permissions/fixtures/permission.fixture.json @@ -0,0 +1,11 @@ +{ + "object": "permission", + "id": "perm_01HXYZ123456789ABCDEFGHIJ", + "slug": "documents:read", + "name": "View Documents", + "description": "Allows viewing document contents", + "system": false, + "resource_type_slug": "document", + "created_at": "2026-01-15T12:00:00.000Z", + "updated_at": "2026-01-15T12:00:00.000Z" +} diff --git a/src/permissions/fixtures/update-authorization-permission.fixture.json b/src/permissions/fixtures/update-authorization-permission.fixture.json new file mode 100644 index 000000000..5aed776b7 --- /dev/null +++ b/src/permissions/fixtures/update-authorization-permission.fixture.json @@ -0,0 +1,4 @@ +{ + "name": "View Documents", + "description": "Allows viewing document contents" +} diff --git a/src/permissions/interfaces/authorization-permission.interface.ts b/src/permissions/interfaces/authorization-permission.interface.ts new file mode 100644 index 000000000..ac727d924 --- /dev/null +++ b/src/permissions/interfaces/authorization-permission.interface.ts @@ -0,0 +1,34 @@ +// This file is auto-generated by oagen. Do not edit. + +export interface AuthorizationPermission { + /** Distinguishes the Permission object. */ + object: 'permission'; + /** Unique identifier of the Permission. */ + id: string; + /** A unique key to reference the permission. Must be lowercase and contain only letters, numbers, hyphens, underscores, colons, periods, and asterisks. */ + slug: string; + /** A descriptive name for the Permission. */ + name: string; + /** An optional description of the Permission. */ + description: string | null; + /** Whether the permission is a system permission. System permissions are managed by WorkOS and cannot be deleted. */ + system: boolean; + /** The slug of the resource type associated with the permission. */ + resourceTypeSlug: string; + /** An ISO 8601 timestamp. */ + createdAt: string; + /** An ISO 8601 timestamp. */ + updatedAt: string; +} + +export interface AuthorizationPermissionResponse { + object: 'permission'; + id: string; + slug: string; + name: string; + description: string | null; + system: boolean; + resource_type_slug: string; + created_at: string; + updated_at: string; +} diff --git a/src/permissions/interfaces/create-authorization-permission.interface.ts b/src/permissions/interfaces/create-authorization-permission.interface.ts new file mode 100644 index 000000000..ddcfb825e --- /dev/null +++ b/src/permissions/interfaces/create-authorization-permission.interface.ts @@ -0,0 +1,19 @@ +// This file is auto-generated by oagen. Do not edit. + +export interface CreateAuthorizationPermission { + /** A unique key to reference the permission. Must be lowercase and contain only letters, numbers, hyphens, underscores, colons, periods, and asterisks. */ + slug: string; + /** A descriptive name for the Permission. */ + name: string; + /** An optional description of the Permission. */ + description?: string | null; + /** The slug of the resource type this permission is scoped to. */ + resourceTypeSlug?: string; +} + +export interface CreateAuthorizationPermissionResponse { + slug: string; + name: string; + description?: string | null; + resource_type_slug?: string; +} diff --git a/src/permissions/interfaces/index.ts b/src/permissions/interfaces/index.ts new file mode 100644 index 000000000..8f156997c --- /dev/null +++ b/src/permissions/interfaces/index.ts @@ -0,0 +1,5 @@ +// This file is auto-generated by oagen. Do not edit. + +export * from './authorization-permission.interface'; +export * from './create-authorization-permission.interface'; +export * from './update-authorization-permission.interface'; diff --git a/src/permissions/interfaces/permission.interface.ts b/src/permissions/interfaces/permission.interface.ts new file mode 100644 index 000000000..864e38042 --- /dev/null +++ b/src/permissions/interfaces/permission.interface.ts @@ -0,0 +1,9 @@ +// This file is auto-generated by oagen. Do not edit. + +import type { + AuthorizationPermission, + AuthorizationPermissionResponse, +} from './authorization-permission.interface'; + +export type Permission = AuthorizationPermission; +export type PermissionResponse = AuthorizationPermissionResponse; diff --git a/src/permissions/interfaces/update-authorization-permission.interface.ts b/src/permissions/interfaces/update-authorization-permission.interface.ts new file mode 100644 index 000000000..f15bcb0fa --- /dev/null +++ b/src/permissions/interfaces/update-authorization-permission.interface.ts @@ -0,0 +1,10 @@ +// This file is auto-generated by oagen. Do not edit. + +import type { + UpdateOrganizationRole, + UpdateOrganizationRoleResponse, +} from '../../authorization/interfaces/update-organization-role.interface'; + +export type UpdateAuthorizationPermission = UpdateOrganizationRole; +export type UpdateAuthorizationPermissionResponse = + UpdateOrganizationRoleResponse; diff --git a/src/permissions/permissions.spec.ts b/src/permissions/permissions.spec.ts new file mode 100644 index 000000000..6b17fe6c2 --- /dev/null +++ b/src/permissions/permissions.spec.ts @@ -0,0 +1,160 @@ +// This file is auto-generated by oagen. Do not edit. + +import fetch from 'jest-fetch-mock'; +import { + fetchOnce, + fetchURL, + fetchMethod, + fetchSearchParams, + fetchBody, + testEmptyResults, + testPaginationParams, + testUnauthorized, +} from '../common/utils/test-utils'; +import { WorkOS } from '../workos'; + +import listAuthorizationPermissionFixture from './fixtures/list-authorization-permission.fixture.json'; +import permissionFixture from './fixtures/permission.fixture.json'; +import authorizationPermissionFixture from './fixtures/authorization-permission.fixture.json'; + +const workos = new WorkOS('sk_test_Sz3IQjepeSWaI4cMS4ms4sMuU'); + +function expectAuthorizationPermission(result: any) { + expect(result.object).toBe('permission'); + expect(result.id).toBe('perm_01HXYZ123456789ABCDEFGHIJ'); + expect(result.slug).toBe('documents:read'); + expect(result.name).toBe('View Documents'); + expect(result.description).toBe('Allows viewing document contents'); + expect(result.system).toBe(false); + expect(result.resourceTypeSlug).toBe('workspace'); + expect(result.createdAt).toBe('2026-01-15T12:00:00.000Z'); + expect(result.updatedAt).toBe('2026-01-15T12:00:00.000Z'); +} + +describe('Permissions', () => { + beforeEach(() => fetch.resetMocks()); + + describe('list', () => { + it('returns paginated results', async () => { + fetchOnce(listAuthorizationPermissionFixture); + + const { data, listMetadata } = await workos.permissions.list(); + + expect(fetchMethod()).toBe('GET'); + expect(new URL(String(fetchURL())).pathname).toBe( + '/authorization/permissions', + ); + expect(fetchSearchParams()).toHaveProperty('order'); + expect(Array.isArray(data)).toBe(true); + expect(listMetadata).toBeDefined(); + expect(data.length).toBeGreaterThan(0); + expectAuthorizationPermission(data[0]); + }); + + testEmptyResults(() => workos.permissions.list()); + + testPaginationParams( + (opts) => workos.permissions.list(opts), + listAuthorizationPermissionFixture, + ); + + testUnauthorized(() => workos.permissions.list()); + }); + + describe('create', () => { + it('sends the correct request and returns result', async () => { + fetchOnce(permissionFixture); + + const result = await workos.permissions.create({ + slug: 'test_slug', + name: 'Test', + }); + + expect(fetchMethod()).toBe('POST'); + expect(new URL(String(fetchURL())).pathname).toBe( + '/authorization/permissions', + ); + expect(fetchBody()).toEqual( + expect.objectContaining({ slug: 'test_slug', name: 'Test' }), + ); + expect(result.object).toBe('permission'); + expect(result.id).toBe('perm_01HXYZ123456789ABCDEFGHIJ'); + expect(result.slug).toBe('documents:read'); + expect(result.name).toBe('View Documents'); + expect(result.description).toBe('Allows viewing document contents'); + expect(result.system).toBe(false); + expect(result.resourceTypeSlug).toBe('document'); + expect(result.createdAt).toBe('2026-01-15T12:00:00.000Z'); + expect(result.updatedAt).toBe('2026-01-15T12:00:00.000Z'); + }); + + testUnauthorized(() => + workos.permissions.create({ slug: 'test_slug', name: 'Test' }), + ); + + it('throws UnprocessableEntityException on 422', async () => { + fetchOnce('', { status: 422 }); + await expect( + workos.permissions.create({ slug: 'test_slug', name: 'Test' }), + ).rejects.toThrow(); + }); + }); + + describe('find', () => { + it('returns the expected result', async () => { + fetchOnce(authorizationPermissionFixture); + + const result = await workos.permissions.find('test_slug'); + + expect(fetchMethod()).toBe('GET'); + expect(new URL(String(fetchURL())).pathname).toBe( + '/authorization/permissions/test_slug', + ); + expectAuthorizationPermission(result); + }); + + testUnauthorized(() => workos.permissions.find('test_slug')); + + it('throws NotFoundException on 404', async () => { + fetchOnce('', { status: 404 }); + await expect(workos.permissions.find('test_slug')).rejects.toThrow(); + }); + }); + + describe('update', () => { + it('sends the correct request and returns result', async () => { + fetchOnce(authorizationPermissionFixture); + + const result = await workos.permissions.update('test_slug', {}); + + expect(fetchMethod()).toBe('PATCH'); + expect(new URL(String(fetchURL())).pathname).toBe( + '/authorization/permissions/test_slug', + ); + expect(fetchBody()).toBeDefined(); + expectAuthorizationPermission(result); + }); + + testUnauthorized(() => workos.permissions.update('test_slug', {})); + + it('throws UnprocessableEntityException on 422', async () => { + fetchOnce('', { status: 422 }); + await expect( + workos.permissions.update('test_slug', {}), + ).rejects.toThrow(); + }); + }); + + describe('delete', () => { + it('sends a DELETE request', async () => { + fetchOnce({}, { status: 204 }); + + await workos.permissions.delete('test_slug'); + + expect(fetchMethod()).toBe('DELETE'); + expect(new URL(String(fetchURL())).pathname).toBe( + '/authorization/permissions/test_slug', + ); + }); + }); +}); diff --git a/src/permissions/permissions.ts b/src/permissions/permissions.ts new file mode 100644 index 000000000..b7b8539d4 --- /dev/null +++ b/src/permissions/permissions.ts @@ -0,0 +1,117 @@ +// This file is auto-generated by oagen. Do not edit. + +import type { WorkOS } from '../workos'; +import type { PaginationOptions } from '../common/interfaces/pagination-options.interface'; +import type { AutoPaginatable } from '../common/utils/pagination'; +import { createPaginatedList } from '../common/utils/fetch-and-deserialize'; +import type { + AuthorizationPermission, + AuthorizationPermissionResponse, +} from './interfaces/authorization-permission.interface'; +import type { + Permission, + PermissionResponse, +} from './interfaces/permission.interface'; +import type { CreateAuthorizationPermission } from './interfaces/create-authorization-permission.interface'; +import type { UpdateAuthorizationPermission } from './interfaces/update-authorization-permission.interface'; +import { deserializeAuthorizationPermission } from './serializers/authorization-permission.serializer'; +import { deserializePermission } from './serializers/permission.serializer'; +import { serializeCreateAuthorizationPermission } from './serializers/create-authorization-permission.serializer'; +import { serializeUpdateAuthorizationPermission } from './serializers/update-authorization-permission.serializer'; + +export class Permissions { + constructor(private readonly workos: WorkOS) {} + + /** + * List permissions + * + * Get a list of all permissions in your WorkOS environment. + * @param options - Pagination and filter options. + * @returns {AutoPaginatable} + * @throws {NotFoundException} 404 + */ + async list( + options?: PaginationOptions, + ): Promise> { + return createPaginatedList< + AuthorizationPermissionResponse, + AuthorizationPermission, + PaginationOptions + >( + this.workos, + '/authorization/permissions', + deserializeAuthorizationPermission, + options, + ); + } + + /** + * Create a permission + * + * Create a new permission in your WorkOS environment. The permission can then be assigned to environment roles and organization roles. + * @param payload - Object containing slug, name. + * @returns {Permission} + * @throws {BadRequestException} 400 + * @throws {NotFoundException} 404 + * @throws {ConflictException} 409 + * @throws {UnprocessableEntityException} 422 + */ + async create(payload: CreateAuthorizationPermission): Promise { + const { data } = await this.workos.post( + '/authorization/permissions', + serializeCreateAuthorizationPermission(payload), + ); + return deserializePermission(data); + } + + /** + * Get a permission + * + * Retrieve a permission by its unique slug. + * @param slug - A unique key to reference the permission. Must be lowercase and contain only letters, numbers, hyphens, underscores, colons, periods, and asterisks. + * @example "documents:read" + * @returns {AuthorizationPermission} + * @throws {NotFoundException} 404 + */ + async find(slug: string): Promise { + const { data } = await this.workos.get( + `/authorization/permissions/${slug}`, + ); + return deserializeAuthorizationPermission(data); + } + + /** + * Update a permission + * + * Update an existing permission. Only the fields provided in the request body will be updated. + * @param slug - A unique key to reference the permission. Must be lowercase and contain only letters, numbers, hyphens, underscores, colons, periods, and asterisks. + * @example "documents:read" + * @param payload - The request body. + * @returns {AuthorizationPermission} + * @throws {NotFoundException} 404 + * @throws {UnprocessableEntityException} 422 + */ + async update( + slug: string, + payload: UpdateAuthorizationPermission, + ): Promise { + const { data } = await this.workos.patch( + `/authorization/permissions/${slug}`, + serializeUpdateAuthorizationPermission(payload), + ); + return deserializeAuthorizationPermission(data); + } + + /** + * Delete a permission + * + * Delete an existing permission. System permissions cannot be deleted. + * @param slug - A unique key to reference the permission. Must be lowercase and contain only letters, numbers, hyphens, underscores, colons, periods, and asterisks. + * @example "documents:read" + * @returns {void} + * @throws {NotFoundException} 404 + */ + async delete(slug: string): Promise { + await this.workos.delete(`/authorization/permissions/${slug}`); + } +} diff --git a/src/permissions/serializers/authorization-permission.serializer.ts b/src/permissions/serializers/authorization-permission.serializer.ts new file mode 100644 index 000000000..1f228af35 --- /dev/null +++ b/src/permissions/serializers/authorization-permission.serializer.ts @@ -0,0 +1,34 @@ +// This file is auto-generated by oagen. Do not edit. + +import type { + AuthorizationPermission, + AuthorizationPermissionResponse, +} from '../interfaces/authorization-permission.interface'; + +export const deserializeAuthorizationPermission = ( + response: AuthorizationPermissionResponse, +): AuthorizationPermission => ({ + object: response.object, + id: response.id, + slug: response.slug, + name: response.name, + description: response.description ?? null, + system: response.system, + resourceTypeSlug: response.resource_type_slug, + createdAt: response.created_at, + updatedAt: response.updated_at, +}); + +export const serializeAuthorizationPermission = ( + model: AuthorizationPermission, +): AuthorizationPermissionResponse => ({ + object: model.object, + id: model.id, + slug: model.slug, + name: model.name, + description: model.description, + system: model.system, + resource_type_slug: model.resourceTypeSlug, + created_at: model.createdAt, + updated_at: model.updatedAt, +}); diff --git a/src/permissions/serializers/create-authorization-permission.serializer.ts b/src/permissions/serializers/create-authorization-permission.serializer.ts new file mode 100644 index 000000000..1d1346562 --- /dev/null +++ b/src/permissions/serializers/create-authorization-permission.serializer.ts @@ -0,0 +1,24 @@ +// This file is auto-generated by oagen. Do not edit. + +import type { + CreateAuthorizationPermission, + CreateAuthorizationPermissionResponse, +} from '../interfaces/create-authorization-permission.interface'; + +export const deserializeCreateAuthorizationPermission = ( + response: CreateAuthorizationPermissionResponse, +): CreateAuthorizationPermission => ({ + slug: response.slug, + name: response.name, + description: response.description ?? null, + resourceTypeSlug: response.resource_type_slug, +}); + +export const serializeCreateAuthorizationPermission = ( + model: CreateAuthorizationPermission, +): CreateAuthorizationPermissionResponse => ({ + slug: model.slug, + name: model.name, + description: model.description ?? null, + resource_type_slug: model.resourceTypeSlug, +}); diff --git a/src/permissions/serializers/permission.serializer.ts b/src/permissions/serializers/permission.serializer.ts new file mode 100644 index 000000000..78f0d3f24 --- /dev/null +++ b/src/permissions/serializers/permission.serializer.ts @@ -0,0 +1,6 @@ +// This file is auto-generated by oagen. Do not edit. + +export { + deserializeAuthorizationPermission as deserializePermission, + serializeAuthorizationPermission as serializePermission, +} from './authorization-permission.serializer'; diff --git a/src/permissions/serializers/update-authorization-permission.serializer.ts b/src/permissions/serializers/update-authorization-permission.serializer.ts new file mode 100644 index 000000000..078a2f9b8 --- /dev/null +++ b/src/permissions/serializers/update-authorization-permission.serializer.ts @@ -0,0 +1,6 @@ +// This file is auto-generated by oagen. Do not edit. + +export { + deserializeUpdateOrganizationRole as deserializeUpdateAuthorizationPermission, + serializeUpdateOrganizationRole as serializeUpdateAuthorizationPermission, +} from '../../authorization/serializers/update-organization-role.serializer'; From 13677c0eda06ffc1ef0ce9a962ac3d234a5a81c9 Mon Sep 17 00:00:00 2001 From: "Garen J. Torikian" Date: Thu, 26 Mar 2026 14:27:07 -0400 Subject: [PATCH 03/17] Introduce Radar --- ...ntry-already-present-response.fixture.json | 3 + ...dar-standalone-assess-request.fixture.json | 9 ++ ...lete-radar-list-entry-request.fixture.json | 3 + .../radar-standalone-response.fixture.json | 7 + ...-update-radar-attempt-request.fixture.json | 4 + ...one-update-radar-list-request.fixture.json | 3 + src/radar/interfaces/index.ts | 8 ++ ...ntry-already-present-response.interface.ts | 10 ++ ...dar-standalone-assess-request.interface.ts | 31 +++++ ...lete-radar-list-entry-request.interface.ts | 11 ++ .../radar-standalone-response.interface.ts | 26 ++++ ...-update-radar-attempt-request.interface.ts | 13 ++ ...one-update-radar-list-request.interface.ts | 10 ++ src/radar/radar.spec.ts | 115 ++++++++++++++++ src/radar/radar.ts | 127 ++++++++++++++++++ ...try-already-present-response.serializer.ts | 18 +++ ...ar-standalone-assess-request.serializer.ts | 30 +++++ ...ete-radar-list-entry-request.serializer.ts | 6 + .../radar-standalone-response.serializer.ts | 26 ++++ ...update-radar-attempt-request.serializer.ts | 20 +++ ...ne-update-radar-list-request.serializer.ts | 18 +++ 21 files changed, 498 insertions(+) create mode 100644 src/radar/fixtures/radar-list-entry-already-present-response.fixture.json create mode 100644 src/radar/fixtures/radar-standalone-assess-request.fixture.json create mode 100644 src/radar/fixtures/radar-standalone-delete-radar-list-entry-request.fixture.json create mode 100644 src/radar/fixtures/radar-standalone-response.fixture.json create mode 100644 src/radar/fixtures/radar-standalone-update-radar-attempt-request.fixture.json create mode 100644 src/radar/fixtures/radar-standalone-update-radar-list-request.fixture.json create mode 100644 src/radar/interfaces/index.ts create mode 100644 src/radar/interfaces/radar-list-entry-already-present-response.interface.ts create mode 100644 src/radar/interfaces/radar-standalone-assess-request.interface.ts create mode 100644 src/radar/interfaces/radar-standalone-delete-radar-list-entry-request.interface.ts create mode 100644 src/radar/interfaces/radar-standalone-response.interface.ts create mode 100644 src/radar/interfaces/radar-standalone-update-radar-attempt-request.interface.ts create mode 100644 src/radar/interfaces/radar-standalone-update-radar-list-request.interface.ts create mode 100644 src/radar/radar.spec.ts create mode 100644 src/radar/radar.ts create mode 100644 src/radar/serializers/radar-list-entry-already-present-response.serializer.ts create mode 100644 src/radar/serializers/radar-standalone-assess-request.serializer.ts create mode 100644 src/radar/serializers/radar-standalone-delete-radar-list-entry-request.serializer.ts create mode 100644 src/radar/serializers/radar-standalone-response.serializer.ts create mode 100644 src/radar/serializers/radar-standalone-update-radar-attempt-request.serializer.ts create mode 100644 src/radar/serializers/radar-standalone-update-radar-list-request.serializer.ts diff --git a/src/radar/fixtures/radar-list-entry-already-present-response.fixture.json b/src/radar/fixtures/radar-list-entry-already-present-response.fixture.json new file mode 100644 index 000000000..d20390c36 --- /dev/null +++ b/src/radar/fixtures/radar-list-entry-already-present-response.fixture.json @@ -0,0 +1,3 @@ +{ + "message": "Entry already present in list" +} diff --git a/src/radar/fixtures/radar-standalone-assess-request.fixture.json b/src/radar/fixtures/radar-standalone-assess-request.fixture.json new file mode 100644 index 000000000..6f3673e82 --- /dev/null +++ b/src/radar/fixtures/radar-standalone-assess-request.fixture.json @@ -0,0 +1,9 @@ +{ + "ip_address": "49.78.240.97", + "user_agent": "Mozilla/5.0", + "email": "user@example.com", + "auth_method": "Password", + "action": "login", + "device_fingerprint": "fp_abc123", + "bot_score": "0.1" +} diff --git a/src/radar/fixtures/radar-standalone-delete-radar-list-entry-request.fixture.json b/src/radar/fixtures/radar-standalone-delete-radar-list-entry-request.fixture.json new file mode 100644 index 000000000..a123eb26f --- /dev/null +++ b/src/radar/fixtures/radar-standalone-delete-radar-list-entry-request.fixture.json @@ -0,0 +1,3 @@ +{ + "entry": "198.51.100.42" +} diff --git a/src/radar/fixtures/radar-standalone-response.fixture.json b/src/radar/fixtures/radar-standalone-response.fixture.json new file mode 100644 index 000000000..1670848e1 --- /dev/null +++ b/src/radar/fixtures/radar-standalone-response.fixture.json @@ -0,0 +1,7 @@ +{ + "verdict": "block", + "reason": "Detected enabled Radar control", + "attempt_id": "radar_att_01HZBC6N1EB1ZY7KG32X", + "control": "bot_detection", + "blocklist_type": "ip_address" +} diff --git a/src/radar/fixtures/radar-standalone-update-radar-attempt-request.fixture.json b/src/radar/fixtures/radar-standalone-update-radar-attempt-request.fixture.json new file mode 100644 index 000000000..b9598708a --- /dev/null +++ b/src/radar/fixtures/radar-standalone-update-radar-attempt-request.fixture.json @@ -0,0 +1,4 @@ +{ + "challenge_status": "success", + "attempt_status": "success" +} diff --git a/src/radar/fixtures/radar-standalone-update-radar-list-request.fixture.json b/src/radar/fixtures/radar-standalone-update-radar-list-request.fixture.json new file mode 100644 index 000000000..a123eb26f --- /dev/null +++ b/src/radar/fixtures/radar-standalone-update-radar-list-request.fixture.json @@ -0,0 +1,3 @@ +{ + "entry": "198.51.100.42" +} diff --git a/src/radar/interfaces/index.ts b/src/radar/interfaces/index.ts new file mode 100644 index 000000000..34baa9936 --- /dev/null +++ b/src/radar/interfaces/index.ts @@ -0,0 +1,8 @@ +// This file is auto-generated by oagen. Do not edit. + +export * from './radar-list-entry-already-present-response.interface'; +export * from './radar-standalone-assess-request.interface'; +export * from './radar-standalone-delete-radar-list-entry-request.interface'; +export * from './radar-standalone-response.interface'; +export * from './radar-standalone-update-radar-attempt-request.interface'; +export * from './radar-standalone-update-radar-list-request.interface'; diff --git a/src/radar/interfaces/radar-list-entry-already-present-response.interface.ts b/src/radar/interfaces/radar-list-entry-already-present-response.interface.ts new file mode 100644 index 000000000..06bdba196 --- /dev/null +++ b/src/radar/interfaces/radar-list-entry-already-present-response.interface.ts @@ -0,0 +1,10 @@ +// This file is auto-generated by oagen. Do not edit. + +export interface RadarListEntryAlreadyPresentResponse { + /** A message indicating the entry already exists. */ + message: string; +} + +export interface RadarListEntryAlreadyPresentResponseWire { + message: string; +} diff --git a/src/radar/interfaces/radar-standalone-assess-request.interface.ts b/src/radar/interfaces/radar-standalone-assess-request.interface.ts new file mode 100644 index 000000000..0e5c3c5c1 --- /dev/null +++ b/src/radar/interfaces/radar-standalone-assess-request.interface.ts @@ -0,0 +1,31 @@ +// This file is auto-generated by oagen. Do not edit. + +import type { RadarStandaloneAssessRequestAuthMethod } from '../../common/interfaces/radar-standalone-assess-request-auth-method.interface'; +import type { RadarStandaloneAssessRequestAction } from '../../common/interfaces/radar-standalone-assess-request-action.interface'; + +export interface RadarStandaloneAssessRequest { + /** The IP address of the request to assess. */ + ipAddress: string; + /** The user agent string of the request to assess. */ + userAgent: string; + /** The email address of the user making the request. */ + email: string; + /** The authentication method being used. */ + authMethod: RadarStandaloneAssessRequestAuthMethod; + /** The action being performed. */ + action: RadarStandaloneAssessRequestAction; + /** An optional device fingerprint for the request. */ + deviceFingerprint?: string; + /** An optional bot detection score for the request. */ + botScore?: string; +} + +export interface RadarStandaloneAssessRequestResponse { + ip_address: string; + user_agent: string; + email: string; + auth_method: RadarStandaloneAssessRequestAuthMethod; + action: RadarStandaloneAssessRequestAction; + device_fingerprint?: string; + bot_score?: string; +} diff --git a/src/radar/interfaces/radar-standalone-delete-radar-list-entry-request.interface.ts b/src/radar/interfaces/radar-standalone-delete-radar-list-entry-request.interface.ts new file mode 100644 index 000000000..c891d5c2b --- /dev/null +++ b/src/radar/interfaces/radar-standalone-delete-radar-list-entry-request.interface.ts @@ -0,0 +1,11 @@ +// This file is auto-generated by oagen. Do not edit. + +import type { + RadarStandaloneUpdateRadarListRequest, + RadarStandaloneUpdateRadarListRequestResponse, +} from './radar-standalone-update-radar-list-request.interface'; + +export type RadarStandaloneDeleteRadarListEntryRequest = + RadarStandaloneUpdateRadarListRequest; +export type RadarStandaloneDeleteRadarListEntryRequestResponse = + RadarStandaloneUpdateRadarListRequestResponse; diff --git a/src/radar/interfaces/radar-standalone-response.interface.ts b/src/radar/interfaces/radar-standalone-response.interface.ts new file mode 100644 index 000000000..8693db365 --- /dev/null +++ b/src/radar/interfaces/radar-standalone-response.interface.ts @@ -0,0 +1,26 @@ +// This file is auto-generated by oagen. Do not edit. + +import type { RadarStandaloneResponseVerdict } from '../../common/interfaces/radar-standalone-response-verdict.interface'; +import type { RadarStandaloneResponseControl } from '../../common/interfaces/radar-standalone-response-control.interface'; +import type { RadarStandaloneResponseBlocklistType } from '../../common/interfaces/radar-standalone-response-blocklist-type.interface'; + +export interface RadarStandaloneResponse { + /** The verdict of the risk assessment. */ + verdict: RadarStandaloneResponseVerdict; + /** A human-readable reason for the verdict. */ + reason: string; + /** Unique identifier of the authentication attempt. */ + attemptId: string; + /** The Radar control that triggered the verdict. Only present if the verdict is `block` or `challenge`. */ + control?: RadarStandaloneResponseControl; + /** The type of blocklist entry that triggered the verdict. Only present if the control is `restriction`. */ + blocklistType?: RadarStandaloneResponseBlocklistType; +} + +export interface RadarStandaloneResponseWire { + verdict: RadarStandaloneResponseVerdict; + reason: string; + attempt_id: string; + control?: RadarStandaloneResponseControl; + blocklist_type?: RadarStandaloneResponseBlocklistType; +} diff --git a/src/radar/interfaces/radar-standalone-update-radar-attempt-request.interface.ts b/src/radar/interfaces/radar-standalone-update-radar-attempt-request.interface.ts new file mode 100644 index 000000000..815829f74 --- /dev/null +++ b/src/radar/interfaces/radar-standalone-update-radar-attempt-request.interface.ts @@ -0,0 +1,13 @@ +// This file is auto-generated by oagen. Do not edit. + +export interface RadarStandaloneUpdateRadarAttemptRequest { + /** Set to `"success"` to mark the challenge as completed. */ + challengeStatus?: 'success'; + /** Set to `"success"` to mark the authentication attempt as successful. */ + attemptStatus?: 'success'; +} + +export interface RadarStandaloneUpdateRadarAttemptRequestResponse { + challenge_status?: 'success'; + attempt_status?: 'success'; +} diff --git a/src/radar/interfaces/radar-standalone-update-radar-list-request.interface.ts b/src/radar/interfaces/radar-standalone-update-radar-list-request.interface.ts new file mode 100644 index 000000000..7abe700f0 --- /dev/null +++ b/src/radar/interfaces/radar-standalone-update-radar-list-request.interface.ts @@ -0,0 +1,10 @@ +// This file is auto-generated by oagen. Do not edit. + +export interface RadarStandaloneUpdateRadarListRequest { + /** The value to add to the list. Must match the format of the list type (e.g. a valid IP address for `ip_address`, a valid email for `email`). */ + entry: string; +} + +export interface RadarStandaloneUpdateRadarListRequestResponse { + entry: string; +} diff --git a/src/radar/radar.spec.ts b/src/radar/radar.spec.ts new file mode 100644 index 000000000..76d78cad6 --- /dev/null +++ b/src/radar/radar.spec.ts @@ -0,0 +1,115 @@ +// This file is auto-generated by oagen. Do not edit. + +import fetch from 'jest-fetch-mock'; +import { + fetchOnce, + fetchURL, + fetchMethod, + fetchBody, + testUnauthorized, +} from '../common/utils/test-utils'; +import { WorkOS } from '../workos'; + +import radarStandaloneResponseFixture from './fixtures/radar-standalone-response.fixture.json'; +import radarListEntryAlreadyPresentResponseFixture from './fixtures/radar-list-entry-already-present-response.fixture.json'; + +const workos = new WorkOS('sk_test_Sz3IQjepeSWaI4cMS4ms4sMuU'); + +describe('Radar', () => { + beforeEach(() => fetch.resetMocks()); + + describe('assess', () => { + it('sends the correct request and returns result', async () => { + fetchOnce(radarStandaloneResponseFixture); + + const result = await workos.radar.assess({ + ipAddress: 'test_ip_address', + userAgent: 'test_user_agent', + email: 'test@example.com', + authMethod: 'Password', + action: 'login', + }); + + expect(fetchMethod()).toBe('POST'); + expect(new URL(String(fetchURL())).pathname).toBe('/radar/attempts'); + expect(fetchBody()).toEqual( + expect.objectContaining({ + ip_address: 'test_ip_address', + user_agent: 'test_user_agent', + email: 'test@example.com', + auth_method: 'Password', + action: 'login', + }), + ); + expect(result.verdict).toBe('block'); + expect(result.reason).toBe('Detected enabled Radar control'); + expect(result.attemptId).toBe('radar_att_01HZBC6N1EB1ZY7KG32X'); + }); + + testUnauthorized(() => + workos.radar.assess({ + ipAddress: 'test_ip_address', + userAgent: 'test_user_agent', + email: 'test@example.com', + authMethod: 'Password', + action: 'login', + }), + ); + }); + + describe('updateRadarAttempt', () => { + it('sends the request', async () => { + fetchOnce({}); + + await workos.radar.updateRadarAttempt('test_id', {}); + + expect(fetchMethod()).toBe('PUT'); + expect(new URL(String(fetchURL())).pathname).toBe( + '/radar/attempts/test_id', + ); + }); + }); + + describe('updateRadarList', () => { + it('sends the correct request and returns result', async () => { + fetchOnce(radarListEntryAlreadyPresentResponseFixture); + + const result = await workos.radar.updateRadarList('ip_address', 'block', { + entry: 'test_entry', + }); + + expect(fetchMethod()).toBe('POST'); + expect(new URL(String(fetchURL())).pathname).toBe( + '/radar/lists/ip_address/block', + ); + expect(fetchBody()).toEqual( + expect.objectContaining({ entry: 'test_entry' }), + ); + expect(result.message).toBe('Entry already present in list'); + }); + + testUnauthorized(() => + workos.radar.updateRadarList('ip_address', 'block', { + entry: 'test_entry', + }), + ); + }); + + describe('deleteRadarListEntry', () => { + it('sends a DELETE request', async () => { + fetchOnce({}, { status: 204 }); + + await workos.radar.deleteRadarListEntry('ip_address', 'block', { + entry: 'test_entry', + }); + + expect(fetchMethod()).toBe('DELETE'); + expect(new URL(String(fetchURL())).pathname).toBe( + '/radar/lists/ip_address/block', + ); + expect(fetchBody()).toEqual( + expect.objectContaining({ entry: 'test_entry' }), + ); + }); + }); +}); diff --git a/src/radar/radar.ts b/src/radar/radar.ts new file mode 100644 index 000000000..d9749403f --- /dev/null +++ b/src/radar/radar.ts @@ -0,0 +1,127 @@ +// This file is auto-generated by oagen. Do not edit. + +import type { WorkOS } from '../workos'; +import type { + RadarStandaloneResponse, + RadarStandaloneResponseWire, +} from './interfaces/radar-standalone-response.interface'; +import type { + RadarListEntryAlreadyPresentResponse, + RadarListEntryAlreadyPresentResponseWire, +} from './interfaces/radar-list-entry-already-present-response.interface'; +import type { RadarStandaloneAssessRequest } from './interfaces/radar-standalone-assess-request.interface'; +import type { RadarStandaloneUpdateRadarAttemptRequest } from './interfaces/radar-standalone-update-radar-attempt-request.interface'; +import type { RadarStandaloneUpdateRadarListRequest } from './interfaces/radar-standalone-update-radar-list-request.interface'; +import type { RadarStandaloneDeleteRadarListEntryRequest } from './interfaces/radar-standalone-delete-radar-list-entry-request.interface'; +import { deserializeRadarStandaloneResponse } from './serializers/radar-standalone-response.serializer'; +import { deserializeRadarListEntryAlreadyPresentResponse } from './serializers/radar-list-entry-already-present-response.serializer'; +import { serializeRadarStandaloneAssessRequest } from './serializers/radar-standalone-assess-request.serializer'; +import { serializeRadarStandaloneUpdateRadarAttemptRequest } from './serializers/radar-standalone-update-radar-attempt-request.serializer'; +import { serializeRadarStandaloneUpdateRadarListRequest } from './serializers/radar-standalone-update-radar-list-request.serializer'; +import { serializeRadarStandaloneDeleteRadarListEntryRequest } from './serializers/radar-standalone-delete-radar-list-entry-request.serializer'; + +export class Radar { + constructor(private readonly workos: WorkOS) {} + + /** + * Create an attempt + * + * Assess a request for risk using the Radar engine and receive a verdict. + * @param payload - Object containing ipAddress, userAgent, email, authMethod, action. + * @returns {RadarStandaloneResponse} + * @throws {BadRequestException} 400 + */ + async assess( + payload: RadarStandaloneAssessRequest, + ): Promise { + const { data } = await this.workos.post( + '/radar/attempts', + serializeRadarStandaloneAssessRequest(payload), + ); + return deserializeRadarStandaloneResponse(data); + } + + /** + * Update a Radar attempt + * + * You may optionally inform Radar that an authentication attempt or challenge was successful using this endpoint. Some Radar controls depend on tracking recent successful attempts, such as impossible travel. + * @param id - The unique identifier of the Radar attempt to update. + * @example "radar_att_01HZBC6N1EB1ZY7KG32X" + * @param payload - The request body. + * @returns {void} + * @throws {BadRequestException} 400 + * @throws {NotFoundException} 404 + */ + async updateRadarAttempt( + id: string, + payload: RadarStandaloneUpdateRadarAttemptRequest, + ): Promise { + await this.workos.put( + `/radar/attempts/${id}`, + serializeRadarStandaloneUpdateRadarAttemptRequest(payload), + ); + } + + /** + * Add an entry to a Radar list + * + * Add an entry to a Radar list. + * @param type - The type of the Radar list (e.g. ip_address, domain, email). + * @example "ip_address" + * @param action - The list action indicating whether to add the entry to the allow or block list. + * @example "block" + * @param payload - Object containing entry. + * @returns {RadarListEntryAlreadyPresentResponse} + * @throws {BadRequestException} 400 + */ + async updateRadarList( + type: + | 'ip_address' + | 'domain' + | 'email' + | 'device' + | 'user_agent' + | 'device_fingerprint' + | 'country', + action: 'block' | 'allow', + payload: RadarStandaloneUpdateRadarListRequest, + ): Promise { + const { data } = + await this.workos.post( + `/radar/lists/${type}/${action}`, + serializeRadarStandaloneUpdateRadarListRequest(payload), + ); + return deserializeRadarListEntryAlreadyPresentResponse(data); + } + + /** + * Remove an entry from a Radar list + * + * Remove an entry from a Radar list. + * @param type - The type of the Radar list (e.g. ip_address, domain, email). + * @example "ip_address" + * @param action - The list action indicating whether to remove the entry from the allow or block list. + * @example "block" + * @param payload - Object containing entry. + * @returns {void} + * @throws {BadRequestException} 400 + * @throws {NotFoundException} 404 + */ + async deleteRadarListEntry( + type: + | 'ip_address' + | 'domain' + | 'email' + | 'device' + | 'user_agent' + | 'device_fingerprint' + | 'country', + action: 'block' | 'allow', + payload: RadarStandaloneDeleteRadarListEntryRequest, + ): Promise { + await this.workos.deleteWithBody( + `/radar/lists/${type}/${action}`, + serializeRadarStandaloneDeleteRadarListEntryRequest(payload), + ); + } +} diff --git a/src/radar/serializers/radar-list-entry-already-present-response.serializer.ts b/src/radar/serializers/radar-list-entry-already-present-response.serializer.ts new file mode 100644 index 000000000..48eaf29aa --- /dev/null +++ b/src/radar/serializers/radar-list-entry-already-present-response.serializer.ts @@ -0,0 +1,18 @@ +// This file is auto-generated by oagen. Do not edit. + +import type { + RadarListEntryAlreadyPresentResponse, + RadarListEntryAlreadyPresentResponseWire, +} from '../interfaces/radar-list-entry-already-present-response.interface'; + +export const deserializeRadarListEntryAlreadyPresentResponse = ( + response: RadarListEntryAlreadyPresentResponseWire, +): RadarListEntryAlreadyPresentResponse => ({ + message: response.message, +}); + +export const serializeRadarListEntryAlreadyPresentResponse = ( + model: RadarListEntryAlreadyPresentResponse, +): RadarListEntryAlreadyPresentResponseWire => ({ + message: model.message, +}); diff --git a/src/radar/serializers/radar-standalone-assess-request.serializer.ts b/src/radar/serializers/radar-standalone-assess-request.serializer.ts new file mode 100644 index 000000000..8df63d779 --- /dev/null +++ b/src/radar/serializers/radar-standalone-assess-request.serializer.ts @@ -0,0 +1,30 @@ +// This file is auto-generated by oagen. Do not edit. + +import type { + RadarStandaloneAssessRequest, + RadarStandaloneAssessRequestResponse, +} from '../interfaces/radar-standalone-assess-request.interface'; + +export const deserializeRadarStandaloneAssessRequest = ( + response: RadarStandaloneAssessRequestResponse, +): RadarStandaloneAssessRequest => ({ + ipAddress: response.ip_address, + userAgent: response.user_agent, + email: response.email, + authMethod: response.auth_method, + action: response.action, + deviceFingerprint: response.device_fingerprint, + botScore: response.bot_score, +}); + +export const serializeRadarStandaloneAssessRequest = ( + model: RadarStandaloneAssessRequest, +): RadarStandaloneAssessRequestResponse => ({ + ip_address: model.ipAddress, + user_agent: model.userAgent, + email: model.email, + auth_method: model.authMethod, + action: model.action, + device_fingerprint: model.deviceFingerprint, + bot_score: model.botScore, +}); diff --git a/src/radar/serializers/radar-standalone-delete-radar-list-entry-request.serializer.ts b/src/radar/serializers/radar-standalone-delete-radar-list-entry-request.serializer.ts new file mode 100644 index 000000000..0b5fc3c5a --- /dev/null +++ b/src/radar/serializers/radar-standalone-delete-radar-list-entry-request.serializer.ts @@ -0,0 +1,6 @@ +// This file is auto-generated by oagen. Do not edit. + +export { + deserializeRadarStandaloneUpdateRadarListRequest as deserializeRadarStandaloneDeleteRadarListEntryRequest, + serializeRadarStandaloneUpdateRadarListRequest as serializeRadarStandaloneDeleteRadarListEntryRequest, +} from './radar-standalone-update-radar-list-request.serializer'; diff --git a/src/radar/serializers/radar-standalone-response.serializer.ts b/src/radar/serializers/radar-standalone-response.serializer.ts new file mode 100644 index 000000000..f53a56728 --- /dev/null +++ b/src/radar/serializers/radar-standalone-response.serializer.ts @@ -0,0 +1,26 @@ +// This file is auto-generated by oagen. Do not edit. + +import type { + RadarStandaloneResponse, + RadarStandaloneResponseWire, +} from '../interfaces/radar-standalone-response.interface'; + +export const deserializeRadarStandaloneResponse = ( + response: RadarStandaloneResponseWire, +): RadarStandaloneResponse => ({ + verdict: response.verdict, + reason: response.reason, + attemptId: response.attempt_id, + control: response.control, + blocklistType: response.blocklist_type, +}); + +export const serializeRadarStandaloneResponse = ( + model: RadarStandaloneResponse, +): RadarStandaloneResponseWire => ({ + verdict: model.verdict, + reason: model.reason, + attempt_id: model.attemptId, + control: model.control, + blocklist_type: model.blocklistType, +}); diff --git a/src/radar/serializers/radar-standalone-update-radar-attempt-request.serializer.ts b/src/radar/serializers/radar-standalone-update-radar-attempt-request.serializer.ts new file mode 100644 index 000000000..5c16e5685 --- /dev/null +++ b/src/radar/serializers/radar-standalone-update-radar-attempt-request.serializer.ts @@ -0,0 +1,20 @@ +// This file is auto-generated by oagen. Do not edit. + +import type { + RadarStandaloneUpdateRadarAttemptRequest, + RadarStandaloneUpdateRadarAttemptRequestResponse, +} from '../interfaces/radar-standalone-update-radar-attempt-request.interface'; + +export const deserializeRadarStandaloneUpdateRadarAttemptRequest = ( + response: RadarStandaloneUpdateRadarAttemptRequestResponse, +): RadarStandaloneUpdateRadarAttemptRequest => ({ + challengeStatus: response.challenge_status, + attemptStatus: response.attempt_status, +}); + +export const serializeRadarStandaloneUpdateRadarAttemptRequest = ( + model: RadarStandaloneUpdateRadarAttemptRequest, +): RadarStandaloneUpdateRadarAttemptRequestResponse => ({ + challenge_status: model.challengeStatus, + attempt_status: model.attemptStatus, +}); diff --git a/src/radar/serializers/radar-standalone-update-radar-list-request.serializer.ts b/src/radar/serializers/radar-standalone-update-radar-list-request.serializer.ts new file mode 100644 index 000000000..2c964d833 --- /dev/null +++ b/src/radar/serializers/radar-standalone-update-radar-list-request.serializer.ts @@ -0,0 +1,18 @@ +// This file is auto-generated by oagen. Do not edit. + +import type { + RadarStandaloneUpdateRadarListRequest, + RadarStandaloneUpdateRadarListRequestResponse, +} from '../interfaces/radar-standalone-update-radar-list-request.interface'; + +export const deserializeRadarStandaloneUpdateRadarListRequest = ( + response: RadarStandaloneUpdateRadarListRequestResponse, +): RadarStandaloneUpdateRadarListRequest => ({ + entry: response.entry, +}); + +export const serializeRadarStandaloneUpdateRadarListRequest = ( + model: RadarStandaloneUpdateRadarListRequest, +): RadarStandaloneUpdateRadarListRequestResponse => ({ + entry: model.entry, +}); From 9f071dc8b4af11f2cfe11bad3bf99b4b5dae7a27 Mon Sep 17 00:00:00 2001 From: "Garen J. Torikian" Date: Thu, 26 Mar 2026 14:39:27 -0400 Subject: [PATCH 04/17] Introduce Webhook events --- .../create-webhook-endpoint.fixture.json | 4 + .../list-webhook-endpoint-json.fixture.json | 18 +++ .../update-webhook-endpoint.fixture.json | 5 + .../webhook-endpoint-json.fixture.json | 10 ++ .../create-webhook-endpoint.interface.ts | 15 ++ src/webhooks/interfaces/index.ts | 5 + .../update-webhook-endpoint.interface.ts | 19 +++ .../webhook-endpoint-json.interface.ts | 33 +++++ .../create-webhook-endpoint.serializer.ts | 20 +++ .../update-webhook-endpoint.serializer.ts | 22 +++ .../webhook-endpoint-json.serializer.ts | 32 +++++ src/webhooks/webhooks-endpoints.spec.ts | 134 ++++++++++++++++++ src/webhooks/webhooks-endpoints.ts | 94 ++++++++++++ 13 files changed, 411 insertions(+) create mode 100644 src/webhooks/fixtures/create-webhook-endpoint.fixture.json create mode 100644 src/webhooks/fixtures/list-webhook-endpoint-json.fixture.json create mode 100644 src/webhooks/fixtures/update-webhook-endpoint.fixture.json create mode 100644 src/webhooks/fixtures/webhook-endpoint-json.fixture.json create mode 100644 src/webhooks/interfaces/create-webhook-endpoint.interface.ts create mode 100644 src/webhooks/interfaces/index.ts create mode 100644 src/webhooks/interfaces/update-webhook-endpoint.interface.ts create mode 100644 src/webhooks/interfaces/webhook-endpoint-json.interface.ts create mode 100644 src/webhooks/serializers/create-webhook-endpoint.serializer.ts create mode 100644 src/webhooks/serializers/update-webhook-endpoint.serializer.ts create mode 100644 src/webhooks/serializers/webhook-endpoint-json.serializer.ts create mode 100644 src/webhooks/webhooks-endpoints.spec.ts create mode 100644 src/webhooks/webhooks-endpoints.ts diff --git a/src/webhooks/fixtures/create-webhook-endpoint.fixture.json b/src/webhooks/fixtures/create-webhook-endpoint.fixture.json new file mode 100644 index 000000000..81a07af09 --- /dev/null +++ b/src/webhooks/fixtures/create-webhook-endpoint.fixture.json @@ -0,0 +1,4 @@ +{ + "endpoint_url": "https://example.com/webhooks", + "events": ["user.created", "dsync.user.created"] +} diff --git a/src/webhooks/fixtures/list-webhook-endpoint-json.fixture.json b/src/webhooks/fixtures/list-webhook-endpoint-json.fixture.json new file mode 100644 index 000000000..e4587c5c1 --- /dev/null +++ b/src/webhooks/fixtures/list-webhook-endpoint-json.fixture.json @@ -0,0 +1,18 @@ +{ + "data": [ + { + "object": "webhook_endpoint", + "id": "we_0123456789", + "endpoint_url": "https://example.com/webhooks", + "secret": "whsec_0FWAiVGkEfGBqqsJH4aNAGBJ4", + "status": "enabled", + "events": ["user.created", "dsync.user.created"], + "created_at": "2026-01-15T12:00:00.000Z", + "updated_at": "2026-01-15T12:00:00.000Z" + } + ], + "list_metadata": { + "before": null, + "after": null + } +} diff --git a/src/webhooks/fixtures/update-webhook-endpoint.fixture.json b/src/webhooks/fixtures/update-webhook-endpoint.fixture.json new file mode 100644 index 000000000..e73642781 --- /dev/null +++ b/src/webhooks/fixtures/update-webhook-endpoint.fixture.json @@ -0,0 +1,5 @@ +{ + "endpoint_url": "https://example.com/webhooks", + "status": "enabled", + "events": ["user.created", "dsync.user.created"] +} diff --git a/src/webhooks/fixtures/webhook-endpoint-json.fixture.json b/src/webhooks/fixtures/webhook-endpoint-json.fixture.json new file mode 100644 index 000000000..592c7cd23 --- /dev/null +++ b/src/webhooks/fixtures/webhook-endpoint-json.fixture.json @@ -0,0 +1,10 @@ +{ + "object": "webhook_endpoint", + "id": "we_0123456789", + "endpoint_url": "https://example.com/webhooks", + "secret": "whsec_0FWAiVGkEfGBqqsJH4aNAGBJ4", + "status": "enabled", + "events": ["user.created", "dsync.user.created"], + "created_at": "2026-01-15T12:00:00.000Z", + "updated_at": "2026-01-15T12:00:00.000Z" +} diff --git a/src/webhooks/interfaces/create-webhook-endpoint.interface.ts b/src/webhooks/interfaces/create-webhook-endpoint.interface.ts new file mode 100644 index 000000000..55813e1ec --- /dev/null +++ b/src/webhooks/interfaces/create-webhook-endpoint.interface.ts @@ -0,0 +1,15 @@ +// This file is auto-generated by oagen. Do not edit. + +import type { CreateWebhookEndpointEvents } from '../../common/interfaces/create-webhook-endpoint-events.interface'; + +export interface CreateWebhookEndpoint { + /** The HTTPS URL where webhooks will be sent. */ + endpointUrl: string; + /** The events that the Webhook Endpoint is subscribed to. */ + events: CreateWebhookEndpointEvents[]; +} + +export interface CreateWebhookEndpointResponse { + endpoint_url: string; + events: CreateWebhookEndpointEvents[]; +} diff --git a/src/webhooks/interfaces/index.ts b/src/webhooks/interfaces/index.ts new file mode 100644 index 000000000..cb2832263 --- /dev/null +++ b/src/webhooks/interfaces/index.ts @@ -0,0 +1,5 @@ +// This file is auto-generated by oagen. Do not edit. + +export * from './create-webhook-endpoint.interface'; +export * from './update-webhook-endpoint.interface'; +export * from './webhook-endpoint-json.interface'; diff --git a/src/webhooks/interfaces/update-webhook-endpoint.interface.ts b/src/webhooks/interfaces/update-webhook-endpoint.interface.ts new file mode 100644 index 000000000..be19e7f3e --- /dev/null +++ b/src/webhooks/interfaces/update-webhook-endpoint.interface.ts @@ -0,0 +1,19 @@ +// This file is auto-generated by oagen. Do not edit. + +import type { UpdateWebhookEndpointStatus } from '../../common/interfaces/update-webhook-endpoint-status.interface'; +import type { UpdateWebhookEndpointEvents } from '../../common/interfaces/update-webhook-endpoint-events.interface'; + +export interface UpdateWebhookEndpoint { + /** The HTTPS URL where webhooks will be sent. */ + endpointUrl?: string; + /** Whether the Webhook Endpoint is enabled or disabled. */ + status?: UpdateWebhookEndpointStatus; + /** The events that the Webhook Endpoint is subscribed to. */ + events?: UpdateWebhookEndpointEvents[]; +} + +export interface UpdateWebhookEndpointResponse { + endpoint_url?: string; + status?: UpdateWebhookEndpointStatus; + events?: UpdateWebhookEndpointEvents[]; +} diff --git a/src/webhooks/interfaces/webhook-endpoint-json.interface.ts b/src/webhooks/interfaces/webhook-endpoint-json.interface.ts new file mode 100644 index 000000000..ad0e2878e --- /dev/null +++ b/src/webhooks/interfaces/webhook-endpoint-json.interface.ts @@ -0,0 +1,33 @@ +// This file is auto-generated by oagen. Do not edit. + +import type { WebhookEndpointJsonStatus } from '../../common/interfaces/webhook-endpoint-json-status.interface'; + +export interface WebhookEndpointJson { + /** Distinguishes the Webhook Endpoint object. */ + object: 'webhook_endpoint'; + /** Unique identifier of the Webhook Endpoint. */ + id: string; + /** The URL to which webhooks are sent. */ + endpointUrl: string; + /** The secret used to sign webhook payloads. */ + secret: string; + /** Whether the Webhook Endpoint is enabled or disabled. */ + status: WebhookEndpointJsonStatus; + /** The events that the Webhook Endpoint is subscribed to. */ + events: string[]; + /** An ISO 8601 timestamp. */ + createdAt: string; + /** An ISO 8601 timestamp. */ + updatedAt: string; +} + +export interface WebhookEndpointJsonResponse { + object: 'webhook_endpoint'; + id: string; + endpoint_url: string; + secret: string; + status: WebhookEndpointJsonStatus; + events: string[]; + created_at: string; + updated_at: string; +} diff --git a/src/webhooks/serializers/create-webhook-endpoint.serializer.ts b/src/webhooks/serializers/create-webhook-endpoint.serializer.ts new file mode 100644 index 000000000..d7fed4fe4 --- /dev/null +++ b/src/webhooks/serializers/create-webhook-endpoint.serializer.ts @@ -0,0 +1,20 @@ +// This file is auto-generated by oagen. Do not edit. + +import type { + CreateWebhookEndpoint, + CreateWebhookEndpointResponse, +} from '../interfaces/create-webhook-endpoint.interface'; + +export const deserializeCreateWebhookEndpoint = ( + response: CreateWebhookEndpointResponse, +): CreateWebhookEndpoint => ({ + endpointUrl: response.endpoint_url, + events: response.events, +}); + +export const serializeCreateWebhookEndpoint = ( + model: CreateWebhookEndpoint, +): CreateWebhookEndpointResponse => ({ + endpoint_url: model.endpointUrl, + events: model.events, +}); diff --git a/src/webhooks/serializers/update-webhook-endpoint.serializer.ts b/src/webhooks/serializers/update-webhook-endpoint.serializer.ts new file mode 100644 index 000000000..981692ad8 --- /dev/null +++ b/src/webhooks/serializers/update-webhook-endpoint.serializer.ts @@ -0,0 +1,22 @@ +// This file is auto-generated by oagen. Do not edit. + +import type { + UpdateWebhookEndpoint, + UpdateWebhookEndpointResponse, +} from '../interfaces/update-webhook-endpoint.interface'; + +export const deserializeUpdateWebhookEndpoint = ( + response: UpdateWebhookEndpointResponse, +): UpdateWebhookEndpoint => ({ + endpointUrl: response.endpoint_url, + status: response.status, + events: response.events, +}); + +export const serializeUpdateWebhookEndpoint = ( + model: UpdateWebhookEndpoint, +): UpdateWebhookEndpointResponse => ({ + endpoint_url: model.endpointUrl, + status: model.status, + events: model.events, +}); diff --git a/src/webhooks/serializers/webhook-endpoint-json.serializer.ts b/src/webhooks/serializers/webhook-endpoint-json.serializer.ts new file mode 100644 index 000000000..048c10f77 --- /dev/null +++ b/src/webhooks/serializers/webhook-endpoint-json.serializer.ts @@ -0,0 +1,32 @@ +// This file is auto-generated by oagen. Do not edit. + +import type { + WebhookEndpointJson, + WebhookEndpointJsonResponse, +} from '../interfaces/webhook-endpoint-json.interface'; + +export const deserializeWebhookEndpointJson = ( + response: WebhookEndpointJsonResponse, +): WebhookEndpointJson => ({ + object: response.object, + id: response.id, + endpointUrl: response.endpoint_url, + secret: response.secret, + status: response.status, + events: response.events, + createdAt: response.created_at, + updatedAt: response.updated_at, +}); + +export const serializeWebhookEndpointJson = ( + model: WebhookEndpointJson, +): WebhookEndpointJsonResponse => ({ + object: model.object, + id: model.id, + endpoint_url: model.endpointUrl, + secret: model.secret, + status: model.status, + events: model.events, + created_at: model.createdAt, + updated_at: model.updatedAt, +}); diff --git a/src/webhooks/webhooks-endpoints.spec.ts b/src/webhooks/webhooks-endpoints.spec.ts new file mode 100644 index 000000000..11a7f3236 --- /dev/null +++ b/src/webhooks/webhooks-endpoints.spec.ts @@ -0,0 +1,134 @@ +// This file is auto-generated by oagen. Do not edit. + +import fetch from 'jest-fetch-mock'; +import { + fetchOnce, + fetchURL, + fetchMethod, + fetchSearchParams, + fetchBody, + testEmptyResults, + testPaginationParams, + testUnauthorized, +} from '../common/utils/test-utils'; +import { WorkOS } from '../workos'; + +import listWebhookEndpointJsonFixture from './fixtures/list-webhook-endpoint-json.fixture.json'; +import webhookEndpointJsonFixture from './fixtures/webhook-endpoint-json.fixture.json'; + +const workos = new WorkOS('sk_test_Sz3IQjepeSWaI4cMS4ms4sMuU'); + +function expectWebhookEndpointJson(result: any) { + expect(result.object).toBe('webhook_endpoint'); + expect(result.id).toBe('we_0123456789'); + expect(result.endpointUrl).toBe('https://example.com/webhooks'); + expect(result.secret).toBe('whsec_0FWAiVGkEfGBqqsJH4aNAGBJ4'); + expect(result.status).toBe('enabled'); + expect(result.events).toEqual(['user.created', 'dsync.user.created']); + expect(result.createdAt).toBe('2026-01-15T12:00:00.000Z'); + expect(result.updatedAt).toBe('2026-01-15T12:00:00.000Z'); +} + +describe('WebhooksEndpoints', () => { + beforeEach(() => fetch.resetMocks()); + + describe('list', () => { + it('returns paginated results', async () => { + fetchOnce(listWebhookEndpointJsonFixture); + + const { data, listMetadata } = await workos.webhooksEndpoints.list(); + + expect(fetchMethod()).toBe('GET'); + expect(new URL(String(fetchURL())).pathname).toBe('/webhook_endpoints'); + expect(fetchSearchParams()).toHaveProperty('order'); + expect(Array.isArray(data)).toBe(true); + expect(listMetadata).toBeDefined(); + expect(data.length).toBeGreaterThan(0); + expectWebhookEndpointJson(data[0]); + }); + + testEmptyResults(() => workos.webhooksEndpoints.list()); + + testPaginationParams( + (opts) => workos.webhooksEndpoints.list(opts), + listWebhookEndpointJsonFixture, + ); + + testUnauthorized(() => workos.webhooksEndpoints.list()); + }); + + describe('create', () => { + it('sends the correct request and returns result', async () => { + fetchOnce(webhookEndpointJsonFixture); + + const result = await workos.webhooksEndpoints.create({ + endpointUrl: 'https://example.com', + events: ['authentication.email_verification_succeeded'], + }); + + expect(fetchMethod()).toBe('POST'); + expect(new URL(String(fetchURL())).pathname).toBe('/webhook_endpoints'); + expect(fetchBody()).toEqual( + expect.objectContaining({ + endpoint_url: 'https://example.com', + events: ['authentication.email_verification_succeeded'], + }), + ); + expectWebhookEndpointJson(result); + }); + + testUnauthorized(() => + workos.webhooksEndpoints.create({ + endpointUrl: 'https://example.com', + events: ['authentication.email_verification_succeeded'], + }), + ); + + it('throws UnprocessableEntityException on 422', async () => { + fetchOnce('', { status: 422 }); + await expect( + workos.webhooksEndpoints.create({ + endpointUrl: 'https://example.com', + events: ['authentication.email_verification_succeeded'], + }), + ).rejects.toThrow(); + }); + }); + + describe('update', () => { + it('sends the correct request and returns result', async () => { + fetchOnce(webhookEndpointJsonFixture); + + const result = await workos.webhooksEndpoints.update('test_id', {}); + + expect(fetchMethod()).toBe('PATCH'); + expect(new URL(String(fetchURL())).pathname).toBe( + '/webhook_endpoints/test_id', + ); + expect(fetchBody()).toBeDefined(); + expectWebhookEndpointJson(result); + }); + + testUnauthorized(() => workos.webhooksEndpoints.update('test_id', {})); + + it('throws UnprocessableEntityException on 422', async () => { + fetchOnce('', { status: 422 }); + await expect( + workos.webhooksEndpoints.update('test_id', {}), + ).rejects.toThrow(); + }); + }); + + describe('delete', () => { + it('sends a DELETE request', async () => { + fetchOnce({}, { status: 204 }); + + await workos.webhooksEndpoints.delete('test_id'); + + expect(fetchMethod()).toBe('DELETE'); + expect(new URL(String(fetchURL())).pathname).toBe( + '/webhook_endpoints/test_id', + ); + }); + }); +}); diff --git a/src/webhooks/webhooks-endpoints.ts b/src/webhooks/webhooks-endpoints.ts new file mode 100644 index 000000000..3273b0f47 --- /dev/null +++ b/src/webhooks/webhooks-endpoints.ts @@ -0,0 +1,94 @@ +// This file is auto-generated by oagen. Do not edit. + +import type { WorkOS } from '../workos'; +import type { PaginationOptions } from '../common/interfaces/pagination-options.interface'; +import type { AutoPaginatable } from '../common/utils/pagination'; +import { createPaginatedList } from '../common/utils/fetch-and-deserialize'; +import type { + WebhookEndpointJson, + WebhookEndpointJsonResponse, +} from './interfaces/webhook-endpoint-json.interface'; +import type { CreateWebhookEndpoint } from './interfaces/create-webhook-endpoint.interface'; +import type { UpdateWebhookEndpoint } from './interfaces/update-webhook-endpoint.interface'; +import { deserializeWebhookEndpointJson } from './serializers/webhook-endpoint-json.serializer'; +import { serializeCreateWebhookEndpoint } from './serializers/create-webhook-endpoint.serializer'; +import { serializeUpdateWebhookEndpoint } from './serializers/update-webhook-endpoint.serializer'; + +export class WebhooksEndpoints { + constructor(private readonly workos: WorkOS) {} + + /** + * List Webhook Endpoints + * + * Get a list of all of your existing webhook endpoints. + * @param options - Pagination and filter options. + * @returns {AutoPaginatable} + */ + async list( + options?: PaginationOptions, + ): Promise> { + return createPaginatedList< + WebhookEndpointJsonResponse, + WebhookEndpointJson, + PaginationOptions + >( + this.workos, + '/webhook_endpoints', + deserializeWebhookEndpointJson, + options, + ); + } + + /** + * Create a Webhook Endpoint + * + * Create a new webhook endpoint to receive event notifications. + * @param payload - Object containing endpointUrl, events. + * @returns {WebhookEndpointJson} + * @throws {ConflictException} 409 + * @throws {UnprocessableEntityException} 422 + */ + async create(payload: CreateWebhookEndpoint): Promise { + const { data } = await this.workos.post( + '/webhook_endpoints', + serializeCreateWebhookEndpoint(payload), + ); + return deserializeWebhookEndpointJson(data); + } + + /** + * Update a Webhook Endpoint + * + * Update the properties of an existing webhook endpoint. + * @param id - Unique identifier of the Webhook Endpoint. + * @example "we_0123456789" + * @param payload - The request body. + * @returns {WebhookEndpointJson} + * @throws {NotFoundException} 404 + * @throws {ConflictException} 409 + * @throws {UnprocessableEntityException} 422 + */ + async update( + id: string, + payload: UpdateWebhookEndpoint, + ): Promise { + const { data } = await this.workos.patch( + `/webhook_endpoints/${id}`, + serializeUpdateWebhookEndpoint(payload), + ); + return deserializeWebhookEndpointJson(data); + } + + /** + * Delete a Webhook Endpoint + * + * Delete an existing webhook endpoint. + * @param id - Unique identifier of the Webhook Endpoint. + * @example "we_0123456789" + * @returns {void} + * @throws {NotFoundException} 404 + */ + async delete(id: string): Promise { + await this.workos.delete(`/webhook_endpoints/${id}`); + } +} From 675fb15af91a1b41139766568a6320bcc06dbaab Mon Sep 17 00:00:00 2001 From: "Garen J. Torikian" Date: Thu, 26 Mar 2026 14:40:51 -0400 Subject: [PATCH 05/17] Introduce WorkOS Connect --- .../application-client-secrets.spec.ts | 91 ++++++++++++ .../application-client-secrets.ts | 72 +++++++++ src/workos-connect/applications.spec.ts | 139 ++++++++++++++++++ src/workos-connect/applications.ts | 128 ++++++++++++++++ ...ication-credentials-list-item.fixture.json | 8 + .../fixtures/connect-application.fixture.json | 10 ++ .../create-application-secret.fixture.json | 1 + .../create-m2m-application.fixture.json | 7 + .../create-oauth-application.fixture.json | 15 ++ ...ternal-auth-complete-response.fixture.json | 3 + .../list-connect-application.fixture.json | 18 +++ ...ew-connect-application-secret.fixture.json | 9 ++ .../fixtures/redirect-uri.fixture.json | 8 + .../update-oauth-application.fixture.json | 11 ++ .../user-consent-option-choice.fixture.json | 4 + .../fixtures/user-consent-option.fixture.json | 11 ++ ...user-management-login-request.fixture.json | 26 ++++ .../fixtures/user-object.fixture.json | 10 ++ ...ication-credentials-list-item.interface.ts | 25 ++++ .../connect-application.interface.ts | 31 ++++ .../create-application-secret.interface.ts | 5 + .../create-m2m-application.interface.ts | 22 +++ .../create-oauth-application.interface.ts | 36 +++++ ...ternal-auth-complete-response.interface.ts | 10 ++ src/workos-connect/interfaces/index.ts | 15 ++ ...ew-connect-application-secret.interface.ts | 28 ++++ .../interfaces/redirect-uri.interface.ts | 25 ++++ .../update-oauth-application.interface.ts | 24 +++ .../user-consent-option-choice.interface.ts | 13 ++ .../user-consent-option.interface.ts | 24 +++ ...user-management-login-request.interface.ts | 22 +++ .../interfaces/user-object.interface.ts | 22 +++ ...cation-credentials-list-item.serializer.ts | 28 ++++ .../connect-application.serializer.ts | 32 ++++ .../create-application-secret.serializer.ts | 14 ++ .../create-m2m-application.serializer.ts | 26 ++++ .../create-oauth-application.serializer.ts | 42 ++++++ ...ernal-auth-complete-response.serializer.ts | 18 +++ ...w-connect-application-secret.serializer.ts | 30 ++++ .../serializers/redirect-uri.serializer.ts | 28 ++++ .../update-oauth-application.serializer.ts | 34 +++++ .../user-consent-option-choice.serializer.ts | 20 +++ .../user-consent-option.serializer.ts | 28 ++++ ...ser-management-login-request.serializer.ts | 36 +++++ .../serializers/user-object.serializer.ts | 24 +++ src/workos-connect/work-os-connect.spec.ts | 51 +++++++ src/workos-connect/work-os-connect.ts | 43 ++++++ 47 files changed, 1327 insertions(+) create mode 100644 src/workos-connect/application-client-secrets.spec.ts create mode 100644 src/workos-connect/application-client-secrets.ts create mode 100644 src/workos-connect/applications.spec.ts create mode 100644 src/workos-connect/applications.ts create mode 100644 src/workos-connect/fixtures/application-credentials-list-item.fixture.json create mode 100644 src/workos-connect/fixtures/connect-application.fixture.json create mode 100644 src/workos-connect/fixtures/create-application-secret.fixture.json create mode 100644 src/workos-connect/fixtures/create-m2m-application.fixture.json create mode 100644 src/workos-connect/fixtures/create-oauth-application.fixture.json create mode 100644 src/workos-connect/fixtures/external-auth-complete-response.fixture.json create mode 100644 src/workos-connect/fixtures/list-connect-application.fixture.json create mode 100644 src/workos-connect/fixtures/new-connect-application-secret.fixture.json create mode 100644 src/workos-connect/fixtures/redirect-uri.fixture.json create mode 100644 src/workos-connect/fixtures/update-oauth-application.fixture.json create mode 100644 src/workos-connect/fixtures/user-consent-option-choice.fixture.json create mode 100644 src/workos-connect/fixtures/user-consent-option.fixture.json create mode 100644 src/workos-connect/fixtures/user-management-login-request.fixture.json create mode 100644 src/workos-connect/fixtures/user-object.fixture.json create mode 100644 src/workos-connect/interfaces/application-credentials-list-item.interface.ts create mode 100644 src/workos-connect/interfaces/connect-application.interface.ts create mode 100644 src/workos-connect/interfaces/create-application-secret.interface.ts create mode 100644 src/workos-connect/interfaces/create-m2m-application.interface.ts create mode 100644 src/workos-connect/interfaces/create-oauth-application.interface.ts create mode 100644 src/workos-connect/interfaces/external-auth-complete-response.interface.ts create mode 100644 src/workos-connect/interfaces/index.ts create mode 100644 src/workos-connect/interfaces/new-connect-application-secret.interface.ts create mode 100644 src/workos-connect/interfaces/redirect-uri.interface.ts create mode 100644 src/workos-connect/interfaces/update-oauth-application.interface.ts create mode 100644 src/workos-connect/interfaces/user-consent-option-choice.interface.ts create mode 100644 src/workos-connect/interfaces/user-consent-option.interface.ts create mode 100644 src/workos-connect/interfaces/user-management-login-request.interface.ts create mode 100644 src/workos-connect/interfaces/user-object.interface.ts create mode 100644 src/workos-connect/serializers/application-credentials-list-item.serializer.ts create mode 100644 src/workos-connect/serializers/connect-application.serializer.ts create mode 100644 src/workos-connect/serializers/create-application-secret.serializer.ts create mode 100644 src/workos-connect/serializers/create-m2m-application.serializer.ts create mode 100644 src/workos-connect/serializers/create-oauth-application.serializer.ts create mode 100644 src/workos-connect/serializers/external-auth-complete-response.serializer.ts create mode 100644 src/workos-connect/serializers/new-connect-application-secret.serializer.ts create mode 100644 src/workos-connect/serializers/redirect-uri.serializer.ts create mode 100644 src/workos-connect/serializers/update-oauth-application.serializer.ts create mode 100644 src/workos-connect/serializers/user-consent-option-choice.serializer.ts create mode 100644 src/workos-connect/serializers/user-consent-option.serializer.ts create mode 100644 src/workos-connect/serializers/user-management-login-request.serializer.ts create mode 100644 src/workos-connect/serializers/user-object.serializer.ts create mode 100644 src/workos-connect/work-os-connect.spec.ts create mode 100644 src/workos-connect/work-os-connect.ts diff --git a/src/workos-connect/application-client-secrets.spec.ts b/src/workos-connect/application-client-secrets.spec.ts new file mode 100644 index 000000000..8ddfec808 --- /dev/null +++ b/src/workos-connect/application-client-secrets.spec.ts @@ -0,0 +1,91 @@ +// This file is auto-generated by oagen. Do not edit. + +import fetch from 'jest-fetch-mock'; +import { + fetchOnce, + fetchURL, + fetchMethod, + fetchBody, + testUnauthorized, +} from '../common/utils/test-utils'; +import { WorkOS } from '../workos'; + +import applicationCredentialsListItemFixture from './fixtures/application-credentials-list-item.fixture.json'; +import newConnectApplicationSecretFixture from './fixtures/new-connect-application-secret.fixture.json'; + +const workos = new WorkOS('sk_test_Sz3IQjepeSWaI4cMS4ms4sMuU'); + +describe('ApplicationClientSecrets', () => { + beforeEach(() => fetch.resetMocks()); + + describe('list', () => { + it('returns the expected result', async () => { + fetchOnce(applicationCredentialsListItemFixture); + + const result = await workos.applicationClientSecrets.list('test_id'); + + expect(fetchMethod()).toBe('GET'); + expect(new URL(String(fetchURL())).pathname).toBe( + '/connect/applications/test_id/client_secrets', + ); + expect(result.object).toBe('connect_application_secret'); + expect(result.id).toBe('secret_01J9Q2Z3X4Y5W6V7U8T9S0R1Q'); + expect(result.secretHint).toBe('abc123'); + expect(result.lastUsedAt).toBe(null); + expect(result.createdAt).toBe('2026-01-15T12:00:00.000Z'); + expect(result.updatedAt).toBe('2026-01-15T12:00:00.000Z'); + }); + + testUnauthorized(() => workos.applicationClientSecrets.list('test_id')); + }); + + describe('create', () => { + it('sends the correct request and returns result', async () => { + fetchOnce(newConnectApplicationSecretFixture); + + const result = await workos.applicationClientSecrets.create( + 'test_id', + {}, + ); + + expect(fetchMethod()).toBe('POST'); + expect(new URL(String(fetchURL())).pathname).toBe( + '/connect/applications/test_id/client_secrets', + ); + expect(fetchBody()).toBeDefined(); + expect(result.object).toBe('connect_application_secret'); + expect(result.id).toBe('secret_01J9Q2Z3X4Y5W6V7U8T9S0R1Q'); + expect(result.secretHint).toBe('abc123'); + expect(result.lastUsedAt).toBe(null); + expect(result.createdAt).toBe('2026-01-15T12:00:00.000Z'); + expect(result.updatedAt).toBe('2026-01-15T12:00:00.000Z'); + expect(result.secret).toBe( + 'abc123def456ghi789jkl012mno345pqr678stu901vwx234yz', + ); + }); + + testUnauthorized(() => + workos.applicationClientSecrets.create('test_id', {}), + ); + + it('throws UnprocessableEntityException on 422', async () => { + fetchOnce('', { status: 422 }); + await expect( + workos.applicationClientSecrets.create('test_id', {}), + ).rejects.toThrow(); + }); + }); + + describe('delete', () => { + it('sends a DELETE request', async () => { + fetchOnce({}, { status: 204 }); + + await workos.applicationClientSecrets.delete('test_id'); + + expect(fetchMethod()).toBe('DELETE'); + expect(new URL(String(fetchURL())).pathname).toBe( + '/connect/client_secrets/test_id', + ); + }); + }); +}); diff --git a/src/workos-connect/application-client-secrets.ts b/src/workos-connect/application-client-secrets.ts new file mode 100644 index 000000000..afe58e2ae --- /dev/null +++ b/src/workos-connect/application-client-secrets.ts @@ -0,0 +1,72 @@ +// This file is auto-generated by oagen. Do not edit. + +import type { WorkOS } from '../workos'; +import type { + ApplicationCredentialsListItem, + ApplicationCredentialsListItemResponse, +} from './interfaces/application-credentials-list-item.interface'; +import type { + NewConnectApplicationSecret, + NewConnectApplicationSecretResponse, +} from './interfaces/new-connect-application-secret.interface'; +import type { CreateApplicationSecret } from './interfaces/create-application-secret.interface'; +import { deserializeApplicationCredentialsListItem } from './serializers/application-credentials-list-item.serializer'; +import { deserializeNewConnectApplicationSecret } from './serializers/new-connect-application-secret.serializer'; +import { serializeCreateApplicationSecret } from './serializers/create-application-secret.serializer'; + +export class ApplicationClientSecrets { + constructor(private readonly workos: WorkOS) {} + + /** + * List Client Secrets for a Connect Application + * + * List all client secrets associated with a Connect Application. + * @param id - The application ID or client ID of the Connect Application. + * @example "conn_app_01HXYZ123456789ABCDEFGHIJ" + * @returns {ApplicationCredentialsListItem} + * @throws {NotFoundException} 404 + */ + async list(id: string): Promise { + const { data } = + await this.workos.get( + `/connect/applications/${id}/client_secrets`, + ); + return deserializeApplicationCredentialsListItem(data); + } + + /** + * Create a new client secret for a Connect Application + * + * Create new secrets for a Connect Application. + * @param id - The application ID or client ID of the Connect Application. + * @example "conn_app_01HXYZ123456789ABCDEFGHIJ" + * @param payload - The request body. + * @returns {NewConnectApplicationSecret} + * @throws {NotFoundException} 404 + * @throws {UnprocessableEntityException} 422 + */ + async create( + id: string, + payload: CreateApplicationSecret, + ): Promise { + const { data } = + await this.workos.post( + `/connect/applications/${id}/client_secrets`, + serializeCreateApplicationSecret(payload), + ); + return deserializeNewConnectApplicationSecret(data); + } + + /** + * Delete a Client Secret + * + * Delete (revoke) an existing client secret. + * @param id - The unique ID of the client secret. + * @example "secret_01J9Q2Z3X4Y5W6V7U8T9S0R1Q" + * @returns {void} + * @throws {NotFoundException} 404 + */ + async delete(id: string): Promise { + await this.workos.delete(`/connect/client_secrets/${id}`); + } +} diff --git a/src/workos-connect/applications.spec.ts b/src/workos-connect/applications.spec.ts new file mode 100644 index 000000000..ab07f194d --- /dev/null +++ b/src/workos-connect/applications.spec.ts @@ -0,0 +1,139 @@ +// This file is auto-generated by oagen. Do not edit. + +import fetch from 'jest-fetch-mock'; +import { + fetchOnce, + fetchURL, + fetchMethod, + fetchSearchParams, + fetchBody, + testEmptyResults, + testPaginationParams, + testUnauthorized, +} from '../common/utils/test-utils'; +import { WorkOS } from '../workos'; + +import listConnectApplicationFixture from './fixtures/list-connect-application.fixture.json'; +import connectApplicationFixture from './fixtures/connect-application.fixture.json'; + +const workos = new WorkOS('sk_test_Sz3IQjepeSWaI4cMS4ms4sMuU'); + +function expectConnectApplication(result: any) { + expect(result.object).toBe('connect_application'); + expect(result.id).toBe('conn_app_01HXYZ123456789ABCDEFGHIJ'); + expect(result.clientId).toBe('client_01HXYZ123456789ABCDEFGHIJ'); + expect(result.description).toBe('An application for managing user access'); + expect(result.name).toBe('My Application'); + expect(result.scopes).toEqual(['openid', 'profile', 'email']); + expect(result.createdAt).toBe('2026-01-15T12:00:00.000Z'); + expect(result.updatedAt).toBe('2026-01-15T12:00:00.000Z'); +} + +describe('Applications', () => { + beforeEach(() => fetch.resetMocks()); + + describe('list', () => { + it('returns paginated results', async () => { + fetchOnce(listConnectApplicationFixture); + + const { data, listMetadata } = await workos.applications.list(); + + expect(fetchMethod()).toBe('GET'); + expect(new URL(String(fetchURL())).pathname).toBe( + '/connect/applications', + ); + expect(fetchSearchParams()).toHaveProperty('order'); + expect(Array.isArray(data)).toBe(true); + expect(listMetadata).toBeDefined(); + expect(data.length).toBeGreaterThan(0); + expectConnectApplication(data[0]); + }); + + testEmptyResults(() => workos.applications.list()); + + testPaginationParams( + (opts) => workos.applications.list(opts), + listConnectApplicationFixture, + ); + + testUnauthorized(() => workos.applications.list()); + }); + + describe('create', () => { + it('sends the correct request and returns result', async () => { + fetchOnce(connectApplicationFixture); + + const result = await workos.applications.create({} as any); + + expect(fetchMethod()).toBe('POST'); + expect(new URL(String(fetchURL())).pathname).toBe( + '/connect/applications', + ); + expect(fetchBody()).toBeDefined(); + expectConnectApplication(result); + }); + + testUnauthorized(() => workos.applications.create({} as any)); + + it('throws UnprocessableEntityException on 422', async () => { + fetchOnce('', { status: 422 }); + await expect(workos.applications.create({} as any)).rejects.toThrow(); + }); + }); + + describe('find', () => { + it('returns the expected result', async () => { + fetchOnce(connectApplicationFixture); + + const result = await workos.applications.find('test_id'); + + expect(fetchMethod()).toBe('GET'); + expect(new URL(String(fetchURL())).pathname).toBe( + '/connect/applications/test_id', + ); + expectConnectApplication(result); + }); + + testUnauthorized(() => workos.applications.find('test_id')); + + it('throws NotFoundException on 404', async () => { + fetchOnce('', { status: 404 }); + await expect(workos.applications.find('test_id')).rejects.toThrow(); + }); + }); + + describe('update', () => { + it('sends the correct request and returns result', async () => { + fetchOnce(connectApplicationFixture); + + const result = await workos.applications.update('test_id', {}); + + expect(fetchMethod()).toBe('PUT'); + expect(new URL(String(fetchURL())).pathname).toBe( + '/connect/applications/test_id', + ); + expect(fetchBody()).toBeDefined(); + expectConnectApplication(result); + }); + + testUnauthorized(() => workos.applications.update('test_id', {})); + + it('throws UnprocessableEntityException on 422', async () => { + fetchOnce('', { status: 422 }); + await expect(workos.applications.update('test_id', {})).rejects.toThrow(); + }); + }); + + describe('delete', () => { + it('sends a DELETE request', async () => { + fetchOnce({}, { status: 204 }); + + await workos.applications.delete('test_id'); + + expect(fetchMethod()).toBe('DELETE'); + expect(new URL(String(fetchURL())).pathname).toBe( + '/connect/applications/test_id', + ); + }); + }); +}); diff --git a/src/workos-connect/applications.ts b/src/workos-connect/applications.ts new file mode 100644 index 000000000..aa6d6f537 --- /dev/null +++ b/src/workos-connect/applications.ts @@ -0,0 +1,128 @@ +// This file is auto-generated by oagen. Do not edit. + +import type { WorkOS } from '../workos'; +import type { PaginationOptions } from '../common/interfaces/pagination-options.interface'; +import type { AutoPaginatable } from '../common/utils/pagination'; +import { createPaginatedList } from '../common/utils/fetch-and-deserialize'; +import type { + ConnectApplication, + ConnectApplicationResponse, +} from './interfaces/connect-application.interface'; +import type { CreateOAuthApplication } from './interfaces/create-oauth-application.interface'; +import type { CreateM2MApplication } from './interfaces/create-m2m-application.interface'; +import type { UpdateOAuthApplication } from './interfaces/update-oauth-application.interface'; +import { deserializeConnectApplication } from './serializers/connect-application.serializer'; +import { serializeCreateOAuthApplication } from './serializers/create-oauth-application.serializer'; +import { serializeCreateM2MApplication } from './serializers/create-m2m-application.serializer'; +import { serializeUpdateOAuthApplication } from './serializers/update-oauth-application.serializer'; + +export interface ApplicationsListOptions extends PaginationOptions { + /** Filter Connect Applications by organization ID. */ + organizationId?: string; +} + +export class Applications { + constructor(private readonly workos: WorkOS) {} + + /** + * List Connect Applications + * + * List all Connect Applications in the current environment with optional filtering. + * @param options - Pagination and filter options. + * @returns {AutoPaginatable} + * @throws {UnprocessableEntityException} 422 + */ + async list( + options?: ApplicationsListOptions, + ): Promise> { + return createPaginatedList< + ConnectApplicationResponse, + ConnectApplication, + ApplicationsListOptions + >( + this.workos, + '/connect/applications', + deserializeConnectApplication, + options, + ); + } + + /** + * Create a Connect Application + * + * Create a new Connect Application. Supports both OAuth and Machine-to-Machine (M2M) application types. + * @param payload - The request body. + * @returns {ConnectApplication} + * @throws {NotFoundException} 404 + * @throws {UnprocessableEntityException} 422 + */ + async create( + payload: CreateOAuthApplication | CreateM2MApplication, + ): Promise { + const { data } = await this.workos.post( + '/connect/applications', + (() => { + switch ((payload as any).applicationType) { + case 'oauth': + return serializeCreateOAuthApplication(payload as any); + case 'm2m': + return serializeCreateM2MApplication(payload as any); + default: + return payload; + } + })(), + ); + return deserializeConnectApplication(data); + } + + /** + * Get a Connect Application + * + * Retrieve details for a specific Connect Application by ID or client ID. + * @param id - The application ID or client ID of the Connect Application. + * @example "conn_app_01HXYZ123456789ABCDEFGHIJ" + * @returns {ConnectApplication} + * @throws {NotFoundException} 404 + */ + async find(id: string): Promise { + const { data } = await this.workos.get( + `/connect/applications/${id}`, + ); + return deserializeConnectApplication(data); + } + + /** + * Update a Connect Application + * + * Update an existing Connect Application. For OAuth applications, you can update redirect URIs. For all applications, you can update the name, description, and scopes. + * @param id - The application ID or client ID of the Connect Application. + * @example "conn_app_01HXYZ123456789ABCDEFGHIJ" + * @param payload - The request body. + * @returns {ConnectApplication} + * @throws {NotFoundException} 404 + * @throws {UnprocessableEntityException} 422 + */ + async update( + id: string, + payload: UpdateOAuthApplication, + ): Promise { + const { data } = await this.workos.put( + `/connect/applications/${id}`, + serializeUpdateOAuthApplication(payload), + ); + return deserializeConnectApplication(data); + } + + /** + * Delete a Connect Application + * + * Delete an existing Connect Application. + * @param id - The application ID or client ID of the Connect Application. + * @example "conn_app_01HXYZ123456789ABCDEFGHIJ" + * @returns {void} + * @throws {NotFoundException} 404 + */ + async delete(id: string): Promise { + await this.workos.delete(`/connect/applications/${id}`); + } +} diff --git a/src/workos-connect/fixtures/application-credentials-list-item.fixture.json b/src/workos-connect/fixtures/application-credentials-list-item.fixture.json new file mode 100644 index 000000000..4c03cc2e5 --- /dev/null +++ b/src/workos-connect/fixtures/application-credentials-list-item.fixture.json @@ -0,0 +1,8 @@ +{ + "object": "connect_application_secret", + "id": "secret_01J9Q2Z3X4Y5W6V7U8T9S0R1Q", + "secret_hint": "abc123", + "last_used_at": null, + "created_at": "2026-01-15T12:00:00.000Z", + "updated_at": "2026-01-15T12:00:00.000Z" +} diff --git a/src/workos-connect/fixtures/connect-application.fixture.json b/src/workos-connect/fixtures/connect-application.fixture.json new file mode 100644 index 000000000..4cd701a43 --- /dev/null +++ b/src/workos-connect/fixtures/connect-application.fixture.json @@ -0,0 +1,10 @@ +{ + "object": "connect_application", + "id": "conn_app_01HXYZ123456789ABCDEFGHIJ", + "client_id": "client_01HXYZ123456789ABCDEFGHIJ", + "description": "An application for managing user access", + "name": "My Application", + "scopes": ["openid", "profile", "email"], + "created_at": "2026-01-15T12:00:00.000Z", + "updated_at": "2026-01-15T12:00:00.000Z" +} diff --git a/src/workos-connect/fixtures/create-application-secret.fixture.json b/src/workos-connect/fixtures/create-application-secret.fixture.json new file mode 100644 index 000000000..0967ef424 --- /dev/null +++ b/src/workos-connect/fixtures/create-application-secret.fixture.json @@ -0,0 +1 @@ +{} diff --git a/src/workos-connect/fixtures/create-m2m-application.fixture.json b/src/workos-connect/fixtures/create-m2m-application.fixture.json new file mode 100644 index 000000000..130fc51a9 --- /dev/null +++ b/src/workos-connect/fixtures/create-m2m-application.fixture.json @@ -0,0 +1,7 @@ +{ + "name": "My Application", + "application_type": "m2m", + "description": "An application for managing user access", + "scopes": ["openid", "profile", "email"], + "organization_id": "org_01EHZNVPK3SFK441A1RGBFSHRT" +} diff --git a/src/workos-connect/fixtures/create-oauth-application.fixture.json b/src/workos-connect/fixtures/create-oauth-application.fixture.json new file mode 100644 index 000000000..28f4bb301 --- /dev/null +++ b/src/workos-connect/fixtures/create-oauth-application.fixture.json @@ -0,0 +1,15 @@ +{ + "name": "My Application", + "application_type": "oauth", + "description": "An application for managing user access", + "scopes": ["openid", "profile", "email"], + "redirect_uris": [ + { + "uri": "https://example.com/callback", + "default": true + } + ], + "uses_pkce": true, + "is_first_party": true, + "organization_id": "org_01EHZNVPK3SFK441A1RGBFSHRT" +} diff --git a/src/workos-connect/fixtures/external-auth-complete-response.fixture.json b/src/workos-connect/fixtures/external-auth-complete-response.fixture.json new file mode 100644 index 000000000..bda60f725 --- /dev/null +++ b/src/workos-connect/fixtures/external-auth-complete-response.fixture.json @@ -0,0 +1,3 @@ +{ + "redirect_uri": "https://your-authkit-domain.workos.com/oauth/authorize/complete?state=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdGF0ZSI6InJhbmRvbV9zdGF0ZV9zdHJpbmciLCJpYXQiOjE3NDI2MDQ4NTN9.abc123def456ghi789" +} diff --git a/src/workos-connect/fixtures/list-connect-application.fixture.json b/src/workos-connect/fixtures/list-connect-application.fixture.json new file mode 100644 index 000000000..53bec71d3 --- /dev/null +++ b/src/workos-connect/fixtures/list-connect-application.fixture.json @@ -0,0 +1,18 @@ +{ + "data": [ + { + "object": "connect_application", + "id": "conn_app_01HXYZ123456789ABCDEFGHIJ", + "client_id": "client_01HXYZ123456789ABCDEFGHIJ", + "description": "An application for managing user access", + "name": "My Application", + "scopes": ["openid", "profile", "email"], + "created_at": "2026-01-15T12:00:00.000Z", + "updated_at": "2026-01-15T12:00:00.000Z" + } + ], + "list_metadata": { + "before": null, + "after": null + } +} diff --git a/src/workos-connect/fixtures/new-connect-application-secret.fixture.json b/src/workos-connect/fixtures/new-connect-application-secret.fixture.json new file mode 100644 index 000000000..9b772b11c --- /dev/null +++ b/src/workos-connect/fixtures/new-connect-application-secret.fixture.json @@ -0,0 +1,9 @@ +{ + "object": "connect_application_secret", + "id": "secret_01J9Q2Z3X4Y5W6V7U8T9S0R1Q", + "secret_hint": "abc123", + "last_used_at": null, + "created_at": "2026-01-15T12:00:00.000Z", + "updated_at": "2026-01-15T12:00:00.000Z", + "secret": "abc123def456ghi789jkl012mno345pqr678stu901vwx234yz" +} diff --git a/src/workos-connect/fixtures/redirect-uri.fixture.json b/src/workos-connect/fixtures/redirect-uri.fixture.json new file mode 100644 index 000000000..dcd6ad8d6 --- /dev/null +++ b/src/workos-connect/fixtures/redirect-uri.fixture.json @@ -0,0 +1,8 @@ +{ + "object": "redirect_uri", + "id": "ruri_01EHZNVPK3SFK441A1RGBFSHRT", + "uri": "https://example.com/callback", + "default": true, + "created_at": "2026-01-15T12:00:00.000Z", + "updated_at": "2026-01-15T12:00:00.000Z" +} diff --git a/src/workos-connect/fixtures/update-oauth-application.fixture.json b/src/workos-connect/fixtures/update-oauth-application.fixture.json new file mode 100644 index 000000000..160cacad0 --- /dev/null +++ b/src/workos-connect/fixtures/update-oauth-application.fixture.json @@ -0,0 +1,11 @@ +{ + "name": "My Application", + "description": "An application for managing user access", + "scopes": ["openid", "profile", "email"], + "redirect_uris": [ + { + "uri": "https://example.com/callback", + "default": true + } + ] +} diff --git a/src/workos-connect/fixtures/user-consent-option-choice.fixture.json b/src/workos-connect/fixtures/user-consent-option-choice.fixture.json new file mode 100644 index 000000000..c5f1f838c --- /dev/null +++ b/src/workos-connect/fixtures/user-consent-option-choice.fixture.json @@ -0,0 +1,4 @@ +{ + "value": "accepted", + "label": "I accept the Terms of Service" +} diff --git a/src/workos-connect/fixtures/user-consent-option.fixture.json b/src/workos-connect/fixtures/user-consent-option.fixture.json new file mode 100644 index 000000000..ba21c6190 --- /dev/null +++ b/src/workos-connect/fixtures/user-consent-option.fixture.json @@ -0,0 +1,11 @@ +{ + "claim": "tos_accepted", + "type": "enum", + "label": "Terms of Service", + "choices": [ + { + "value": "accepted", + "label": "I accept the Terms of Service" + } + ] +} diff --git a/src/workos-connect/fixtures/user-management-login-request.fixture.json b/src/workos-connect/fixtures/user-management-login-request.fixture.json new file mode 100644 index 000000000..952fbbf45 --- /dev/null +++ b/src/workos-connect/fixtures/user-management-login-request.fixture.json @@ -0,0 +1,26 @@ +{ + "external_auth_id": "ext_auth_01HXYZ123456789ABCDEFGHIJ", + "user": { + "id": "user_12345", + "email": "marcelina.davis@example.com", + "first_name": "Marcelina", + "last_name": "Davis", + "metadata": { + "department": "Engineering", + "role": "Developer" + } + }, + "user_consent_options": [ + { + "claim": "tos_accepted", + "type": "enum", + "label": "Terms of Service", + "choices": [ + { + "value": "accepted", + "label": "I accept the Terms of Service" + } + ] + } + ] +} diff --git a/src/workos-connect/fixtures/user-object.fixture.json b/src/workos-connect/fixtures/user-object.fixture.json new file mode 100644 index 000000000..27b814fd2 --- /dev/null +++ b/src/workos-connect/fixtures/user-object.fixture.json @@ -0,0 +1,10 @@ +{ + "id": "user_12345", + "email": "marcelina.davis@example.com", + "first_name": "Marcelina", + "last_name": "Davis", + "metadata": { + "department": "Engineering", + "role": "Developer" + } +} diff --git a/src/workos-connect/interfaces/application-credentials-list-item.interface.ts b/src/workos-connect/interfaces/application-credentials-list-item.interface.ts new file mode 100644 index 000000000..c06b29616 --- /dev/null +++ b/src/workos-connect/interfaces/application-credentials-list-item.interface.ts @@ -0,0 +1,25 @@ +// This file is auto-generated by oagen. Do not edit. + +export interface ApplicationCredentialsListItem { + /** Distinguishes the connect application secret object. */ + object: 'connect_application_secret'; + /** The unique ID of the client secret. */ + id: string; + /** A hint showing the last few characters of the secret value. */ + secretHint: string; + /** The timestamp when the client secret was last used, or null if never used. */ + lastUsedAt: string | null; + /** An ISO 8601 timestamp. */ + createdAt: string; + /** An ISO 8601 timestamp. */ + updatedAt: string; +} + +export interface ApplicationCredentialsListItemResponse { + object: 'connect_application_secret'; + id: string; + secret_hint: string; + last_used_at: string | null; + created_at: string; + updated_at: string; +} diff --git a/src/workos-connect/interfaces/connect-application.interface.ts b/src/workos-connect/interfaces/connect-application.interface.ts new file mode 100644 index 000000000..26551a22d --- /dev/null +++ b/src/workos-connect/interfaces/connect-application.interface.ts @@ -0,0 +1,31 @@ +// This file is auto-generated by oagen. Do not edit. + +export interface ConnectApplication { + /** Distinguishes the connect application object. */ + object: 'connect_application'; + /** The unique ID of the connect application. */ + id: string; + /** The client ID of the connect application. */ + clientId: string; + /** A description of the connect application. */ + description: string | null; + /** The name of the connect application. */ + name: string; + /** The scopes available for this application. */ + scopes: string[]; + /** An ISO 8601 timestamp. */ + createdAt: string; + /** An ISO 8601 timestamp. */ + updatedAt: string; +} + +export interface ConnectApplicationResponse { + object: 'connect_application'; + id: string; + client_id: string; + description: string | null; + name: string; + scopes: string[]; + created_at: string; + updated_at: string; +} diff --git a/src/workos-connect/interfaces/create-application-secret.interface.ts b/src/workos-connect/interfaces/create-application-secret.interface.ts new file mode 100644 index 000000000..3265d757a --- /dev/null +++ b/src/workos-connect/interfaces/create-application-secret.interface.ts @@ -0,0 +1,5 @@ +// This file is auto-generated by oagen. Do not edit. + +export type CreateApplicationSecret = object; + +export type CreateApplicationSecretResponse = object; diff --git a/src/workos-connect/interfaces/create-m2m-application.interface.ts b/src/workos-connect/interfaces/create-m2m-application.interface.ts new file mode 100644 index 000000000..307232690 --- /dev/null +++ b/src/workos-connect/interfaces/create-m2m-application.interface.ts @@ -0,0 +1,22 @@ +// This file is auto-generated by oagen. Do not edit. + +export interface CreateM2MApplication { + /** The name of the application. */ + name: string; + /** The type of application to create. */ + applicationType: 'm2m'; + /** A description for the application. */ + description?: string | null; + /** The OAuth scopes granted to the application. */ + scopes?: string[] | null; + /** The organization ID this application belongs to. */ + organizationId: string; +} + +export interface CreateM2MApplicationResponse { + name: string; + application_type: 'm2m'; + description?: string | null; + scopes?: string[] | null; + organization_id: string; +} diff --git a/src/workos-connect/interfaces/create-oauth-application.interface.ts b/src/workos-connect/interfaces/create-oauth-application.interface.ts new file mode 100644 index 000000000..b50d788ea --- /dev/null +++ b/src/workos-connect/interfaces/create-oauth-application.interface.ts @@ -0,0 +1,36 @@ +// This file is auto-generated by oagen. Do not edit. + +import type { + RedirectUri, + RedirectUriResponse, +} from './redirect-uri.interface'; + +export interface CreateOAuthApplication { + /** The name of the application. */ + name: string; + /** The type of application to create. */ + applicationType: 'oauth'; + /** A description for the application. */ + description?: string | null; + /** The OAuth scopes granted to the application. */ + scopes?: string[] | null; + /** Redirect URIs for the application. */ + redirectUris?: RedirectUri[] | null; + /** Whether the application uses PKCE (Proof Key for Code Exchange). */ + usesPkce?: boolean | null; + /** Whether this is a first-party application. Third-party applications require an organization_id. */ + isFirstParty: boolean; + /** The organization ID this application belongs to. Required when is_first_party is false. */ + organizationId?: string | null; +} + +export interface CreateOAuthApplicationResponse { + name: string; + application_type: 'oauth'; + description?: string | null; + scopes?: string[] | null; + redirect_uris?: RedirectUriResponse[] | null; + uses_pkce?: boolean | null; + is_first_party: boolean; + organization_id?: string | null; +} diff --git a/src/workos-connect/interfaces/external-auth-complete-response.interface.ts b/src/workos-connect/interfaces/external-auth-complete-response.interface.ts new file mode 100644 index 000000000..67a663e5d --- /dev/null +++ b/src/workos-connect/interfaces/external-auth-complete-response.interface.ts @@ -0,0 +1,10 @@ +// This file is auto-generated by oagen. Do not edit. + +export interface ExternalAuthCompleteResponse { + /** URI to redirect the user back to AuthKit to complete the OAuth flow. */ + redirectUri: string; +} + +export interface ExternalAuthCompleteResponseWire { + redirect_uri: string; +} diff --git a/src/workos-connect/interfaces/index.ts b/src/workos-connect/interfaces/index.ts new file mode 100644 index 000000000..d70f57db7 --- /dev/null +++ b/src/workos-connect/interfaces/index.ts @@ -0,0 +1,15 @@ +// This file is auto-generated by oagen. Do not edit. + +export * from './application-credentials-list-item.interface'; +export * from './connect-application.interface'; +export * from './create-application-secret.interface'; +export * from './create-m2m-application.interface'; +export * from './create-oauth-application.interface'; +export * from './external-auth-complete-response.interface'; +export * from './new-connect-application-secret.interface'; +export * from './redirect-uri.interface'; +export * from './update-oauth-application.interface'; +export * from './user-consent-option-choice.interface'; +export * from './user-consent-option.interface'; +export * from './user-management-login-request.interface'; +export * from './user-object.interface'; diff --git a/src/workos-connect/interfaces/new-connect-application-secret.interface.ts b/src/workos-connect/interfaces/new-connect-application-secret.interface.ts new file mode 100644 index 000000000..3064fd13d --- /dev/null +++ b/src/workos-connect/interfaces/new-connect-application-secret.interface.ts @@ -0,0 +1,28 @@ +// This file is auto-generated by oagen. Do not edit. + +export interface NewConnectApplicationSecret { + /** Distinguishes the connect application secret object. */ + object: 'connect_application_secret'; + /** The unique ID of the client secret. */ + id: string; + /** A hint showing the last few characters of the secret value. */ + secretHint: string; + /** The timestamp when the client secret was last used, or null if never used. */ + lastUsedAt: string | null; + /** An ISO 8601 timestamp. */ + createdAt: string; + /** An ISO 8601 timestamp. */ + updatedAt: string; + /** The plaintext secret value. Only returned at creation time and cannot be retrieved later. */ + secret: string; +} + +export interface NewConnectApplicationSecretResponse { + object: 'connect_application_secret'; + id: string; + secret_hint: string; + last_used_at: string | null; + created_at: string; + updated_at: string; + secret: string; +} diff --git a/src/workos-connect/interfaces/redirect-uri.interface.ts b/src/workos-connect/interfaces/redirect-uri.interface.ts new file mode 100644 index 000000000..d32ed8e25 --- /dev/null +++ b/src/workos-connect/interfaces/redirect-uri.interface.ts @@ -0,0 +1,25 @@ +// This file is auto-generated by oagen. Do not edit. + +export interface RedirectUri { + /** The object type. */ + object: 'redirect_uri'; + /** The ID of the redirect URI. */ + id: string; + /** The redirect URI. */ + uri: string; + /** Whether this is the default redirect URI. */ + default: boolean; + /** The timestamp when the redirect URI was created. */ + createdAt: string; + /** The timestamp when the redirect URI was last updated. */ + updatedAt: string; +} + +export interface RedirectUriResponse { + object: 'redirect_uri'; + id: string; + uri: string; + default: boolean; + created_at: string; + updated_at: string; +} diff --git a/src/workos-connect/interfaces/update-oauth-application.interface.ts b/src/workos-connect/interfaces/update-oauth-application.interface.ts new file mode 100644 index 000000000..9fd5c8090 --- /dev/null +++ b/src/workos-connect/interfaces/update-oauth-application.interface.ts @@ -0,0 +1,24 @@ +// This file is auto-generated by oagen. Do not edit. + +import type { + RedirectUri, + RedirectUriResponse, +} from './redirect-uri.interface'; + +export interface UpdateOAuthApplication { + /** The name of the application. */ + name?: string; + /** A description for the application. */ + description?: string | null; + /** The OAuth scopes granted to the application. */ + scopes?: string[] | null; + /** Updated redirect URIs for the application. OAuth applications only. */ + redirectUris?: RedirectUri[] | null; +} + +export interface UpdateOAuthApplicationResponse { + name?: string; + description?: string | null; + scopes?: string[] | null; + redirect_uris?: RedirectUriResponse[] | null; +} diff --git a/src/workos-connect/interfaces/user-consent-option-choice.interface.ts b/src/workos-connect/interfaces/user-consent-option-choice.interface.ts new file mode 100644 index 000000000..0e5ef7d54 --- /dev/null +++ b/src/workos-connect/interfaces/user-consent-option-choice.interface.ts @@ -0,0 +1,13 @@ +// This file is auto-generated by oagen. Do not edit. + +export interface UserConsentOptionChoice { + /** The value of this choice. */ + value?: string; + /** A human-readable label for this choice. */ + label?: string; +} + +export interface UserConsentOptionChoiceResponse { + value?: string; + label?: string; +} diff --git a/src/workos-connect/interfaces/user-consent-option.interface.ts b/src/workos-connect/interfaces/user-consent-option.interface.ts new file mode 100644 index 000000000..1a9690b17 --- /dev/null +++ b/src/workos-connect/interfaces/user-consent-option.interface.ts @@ -0,0 +1,24 @@ +// This file is auto-generated by oagen. Do not edit. + +import type { + UserConsentOptionChoice, + UserConsentOptionChoiceResponse, +} from './user-consent-option-choice.interface'; + +export interface UserConsentOption { + /** The claim name for this consent option. */ + claim: string; + /** The type of consent option. */ + type: 'enum'; + /** A human-readable label for this consent option. */ + label: string; + /** The available choices for this consent option. */ + choices: UserConsentOptionChoice[]; +} + +export interface UserConsentOptionResponse { + claim: string; + type: 'enum'; + label: string; + choices: UserConsentOptionChoiceResponse[]; +} diff --git a/src/workos-connect/interfaces/user-management-login-request.interface.ts b/src/workos-connect/interfaces/user-management-login-request.interface.ts new file mode 100644 index 000000000..8f37d4c9d --- /dev/null +++ b/src/workos-connect/interfaces/user-management-login-request.interface.ts @@ -0,0 +1,22 @@ +// This file is auto-generated by oagen. Do not edit. + +import type { UserObject, UserObjectResponse } from './user-object.interface'; +import type { + UserConsentOption, + UserConsentOptionResponse, +} from './user-consent-option.interface'; + +export interface UserManagementLoginRequest { + /** Identifier provided when AuthKit redirected to your login page. */ + externalAuthId: string; + /** The user to create or update in AuthKit. */ + user: UserObject; + /** Array of [User Consent Options](https://workos.com/docs/reference/workos-connect/standalone/user-consent-options) to store with the session. */ + userConsentOptions?: UserConsentOption[]; +} + +export interface UserManagementLoginRequestResponse { + external_auth_id: string; + user: UserObjectResponse; + user_consent_options?: UserConsentOptionResponse[]; +} diff --git a/src/workos-connect/interfaces/user-object.interface.ts b/src/workos-connect/interfaces/user-object.interface.ts new file mode 100644 index 000000000..c28a23e2d --- /dev/null +++ b/src/workos-connect/interfaces/user-object.interface.ts @@ -0,0 +1,22 @@ +// This file is auto-generated by oagen. Do not edit. + +export interface UserObject { + /** Your application's user identifier, which will be stored as an [`external_id`](https://workos.com/docs/authkit/metadata/external-identifiers). Used for upserting and deduplication. */ + id: string; + /** The user's email address. */ + email: string; + /** The user's first name. */ + firstName?: string; + /** The user's last name. */ + lastName?: string; + /** A set of key-value pairs to attach to the user. */ + metadata?: Record; +} + +export interface UserObjectResponse { + id: string; + email: string; + first_name?: string; + last_name?: string; + metadata?: Record; +} diff --git a/src/workos-connect/serializers/application-credentials-list-item.serializer.ts b/src/workos-connect/serializers/application-credentials-list-item.serializer.ts new file mode 100644 index 000000000..0c6dd084f --- /dev/null +++ b/src/workos-connect/serializers/application-credentials-list-item.serializer.ts @@ -0,0 +1,28 @@ +// This file is auto-generated by oagen. Do not edit. + +import type { + ApplicationCredentialsListItem, + ApplicationCredentialsListItemResponse, +} from '../interfaces/application-credentials-list-item.interface'; + +export const deserializeApplicationCredentialsListItem = ( + response: ApplicationCredentialsListItemResponse, +): ApplicationCredentialsListItem => ({ + object: response.object, + id: response.id, + secretHint: response.secret_hint, + lastUsedAt: response.last_used_at ?? null, + createdAt: response.created_at, + updatedAt: response.updated_at, +}); + +export const serializeApplicationCredentialsListItem = ( + model: ApplicationCredentialsListItem, +): ApplicationCredentialsListItemResponse => ({ + object: model.object, + id: model.id, + secret_hint: model.secretHint, + last_used_at: model.lastUsedAt, + created_at: model.createdAt, + updated_at: model.updatedAt, +}); diff --git a/src/workos-connect/serializers/connect-application.serializer.ts b/src/workos-connect/serializers/connect-application.serializer.ts new file mode 100644 index 000000000..0319d485c --- /dev/null +++ b/src/workos-connect/serializers/connect-application.serializer.ts @@ -0,0 +1,32 @@ +// This file is auto-generated by oagen. Do not edit. + +import type { + ConnectApplication, + ConnectApplicationResponse, +} from '../interfaces/connect-application.interface'; + +export const deserializeConnectApplication = ( + response: ConnectApplicationResponse, +): ConnectApplication => ({ + object: response.object, + id: response.id, + clientId: response.client_id, + description: response.description ?? null, + name: response.name, + scopes: response.scopes, + createdAt: response.created_at, + updatedAt: response.updated_at, +}); + +export const serializeConnectApplication = ( + model: ConnectApplication, +): ConnectApplicationResponse => ({ + object: model.object, + id: model.id, + client_id: model.clientId, + description: model.description, + name: model.name, + scopes: model.scopes, + created_at: model.createdAt, + updated_at: model.updatedAt, +}); diff --git a/src/workos-connect/serializers/create-application-secret.serializer.ts b/src/workos-connect/serializers/create-application-secret.serializer.ts new file mode 100644 index 000000000..d55dad86e --- /dev/null +++ b/src/workos-connect/serializers/create-application-secret.serializer.ts @@ -0,0 +1,14 @@ +// This file is auto-generated by oagen. Do not edit. + +import type { + CreateApplicationSecret, + CreateApplicationSecretResponse, +} from '../interfaces/create-application-secret.interface'; + +export const deserializeCreateApplicationSecret = ( + _response: CreateApplicationSecretResponse, +): CreateApplicationSecret => ({}); + +export const serializeCreateApplicationSecret = ( + _model: CreateApplicationSecret, +): CreateApplicationSecretResponse => ({}); diff --git a/src/workos-connect/serializers/create-m2m-application.serializer.ts b/src/workos-connect/serializers/create-m2m-application.serializer.ts new file mode 100644 index 000000000..6b219da6d --- /dev/null +++ b/src/workos-connect/serializers/create-m2m-application.serializer.ts @@ -0,0 +1,26 @@ +// This file is auto-generated by oagen. Do not edit. + +import type { + CreateM2MApplication, + CreateM2MApplicationResponse, +} from '../interfaces/create-m2m-application.interface'; + +export const deserializeCreateM2MApplication = ( + response: CreateM2MApplicationResponse, +): CreateM2MApplication => ({ + name: response.name, + applicationType: response.application_type, + description: response.description ?? null, + scopes: response.scopes ?? null, + organizationId: response.organization_id, +}); + +export const serializeCreateM2MApplication = ( + model: CreateM2MApplication, +): CreateM2MApplicationResponse => ({ + name: model.name, + application_type: model.applicationType, + description: model.description ?? null, + scopes: model.scopes ?? null, + organization_id: model.organizationId, +}); diff --git a/src/workos-connect/serializers/create-oauth-application.serializer.ts b/src/workos-connect/serializers/create-oauth-application.serializer.ts new file mode 100644 index 000000000..4896feb6a --- /dev/null +++ b/src/workos-connect/serializers/create-oauth-application.serializer.ts @@ -0,0 +1,42 @@ +// This file is auto-generated by oagen. Do not edit. + +import type { + CreateOAuthApplication, + CreateOAuthApplicationResponse, +} from '../interfaces/create-oauth-application.interface'; +import { + deserializeRedirectUri, + serializeRedirectUri, +} from './redirect-uri.serializer'; + +export const deserializeCreateOAuthApplication = ( + response: CreateOAuthApplicationResponse, +): CreateOAuthApplication => ({ + name: response.name, + applicationType: response.application_type, + description: response.description ?? null, + scopes: response.scopes ?? null, + redirectUris: + response.redirect_uris != null + ? response.redirect_uris.map(deserializeRedirectUri) + : null, + usesPkce: response.uses_pkce ?? null, + isFirstParty: response.is_first_party, + organizationId: response.organization_id ?? null, +}); + +export const serializeCreateOAuthApplication = ( + model: CreateOAuthApplication, +): CreateOAuthApplicationResponse => ({ + name: model.name, + application_type: model.applicationType, + description: model.description ?? null, + scopes: model.scopes ?? null, + redirect_uris: + model.redirectUris != null + ? model.redirectUris.map(serializeRedirectUri) + : null, + uses_pkce: model.usesPkce ?? null, + is_first_party: model.isFirstParty, + organization_id: model.organizationId ?? null, +}); diff --git a/src/workos-connect/serializers/external-auth-complete-response.serializer.ts b/src/workos-connect/serializers/external-auth-complete-response.serializer.ts new file mode 100644 index 000000000..c74e57d5c --- /dev/null +++ b/src/workos-connect/serializers/external-auth-complete-response.serializer.ts @@ -0,0 +1,18 @@ +// This file is auto-generated by oagen. Do not edit. + +import type { + ExternalAuthCompleteResponse, + ExternalAuthCompleteResponseWire, +} from '../interfaces/external-auth-complete-response.interface'; + +export const deserializeExternalAuthCompleteResponse = ( + response: ExternalAuthCompleteResponseWire, +): ExternalAuthCompleteResponse => ({ + redirectUri: response.redirect_uri, +}); + +export const serializeExternalAuthCompleteResponse = ( + model: ExternalAuthCompleteResponse, +): ExternalAuthCompleteResponseWire => ({ + redirect_uri: model.redirectUri, +}); diff --git a/src/workos-connect/serializers/new-connect-application-secret.serializer.ts b/src/workos-connect/serializers/new-connect-application-secret.serializer.ts new file mode 100644 index 000000000..c1b6fbb36 --- /dev/null +++ b/src/workos-connect/serializers/new-connect-application-secret.serializer.ts @@ -0,0 +1,30 @@ +// This file is auto-generated by oagen. Do not edit. + +import type { + NewConnectApplicationSecret, + NewConnectApplicationSecretResponse, +} from '../interfaces/new-connect-application-secret.interface'; + +export const deserializeNewConnectApplicationSecret = ( + response: NewConnectApplicationSecretResponse, +): NewConnectApplicationSecret => ({ + object: response.object, + id: response.id, + secretHint: response.secret_hint, + lastUsedAt: response.last_used_at ?? null, + createdAt: response.created_at, + updatedAt: response.updated_at, + secret: response.secret, +}); + +export const serializeNewConnectApplicationSecret = ( + model: NewConnectApplicationSecret, +): NewConnectApplicationSecretResponse => ({ + object: model.object, + id: model.id, + secret_hint: model.secretHint, + last_used_at: model.lastUsedAt, + created_at: model.createdAt, + updated_at: model.updatedAt, + secret: model.secret, +}); diff --git a/src/workos-connect/serializers/redirect-uri.serializer.ts b/src/workos-connect/serializers/redirect-uri.serializer.ts new file mode 100644 index 000000000..99fa1b53b --- /dev/null +++ b/src/workos-connect/serializers/redirect-uri.serializer.ts @@ -0,0 +1,28 @@ +// This file is auto-generated by oagen. Do not edit. + +import type { + RedirectUri, + RedirectUriResponse, +} from '../interfaces/redirect-uri.interface'; + +export const deserializeRedirectUri = ( + response: RedirectUriResponse, +): RedirectUri => ({ + object: response.object, + id: response.id, + uri: response.uri, + default: response.default, + createdAt: response.created_at, + updatedAt: response.updated_at, +}); + +export const serializeRedirectUri = ( + model: RedirectUri, +): RedirectUriResponse => ({ + object: model.object, + id: model.id, + uri: model.uri, + default: model.default, + created_at: model.createdAt, + updated_at: model.updatedAt, +}); diff --git a/src/workos-connect/serializers/update-oauth-application.serializer.ts b/src/workos-connect/serializers/update-oauth-application.serializer.ts new file mode 100644 index 000000000..9fa4e0cd2 --- /dev/null +++ b/src/workos-connect/serializers/update-oauth-application.serializer.ts @@ -0,0 +1,34 @@ +// This file is auto-generated by oagen. Do not edit. + +import type { + UpdateOAuthApplication, + UpdateOAuthApplicationResponse, +} from '../interfaces/update-oauth-application.interface'; +import { + deserializeRedirectUri, + serializeRedirectUri, +} from './redirect-uri.serializer'; + +export const deserializeUpdateOAuthApplication = ( + response: UpdateOAuthApplicationResponse, +): UpdateOAuthApplication => ({ + name: response.name, + description: response.description ?? null, + scopes: response.scopes ?? null, + redirectUris: + response.redirect_uris != null + ? response.redirect_uris.map(deserializeRedirectUri) + : null, +}); + +export const serializeUpdateOAuthApplication = ( + model: UpdateOAuthApplication, +): UpdateOAuthApplicationResponse => ({ + name: model.name, + description: model.description ?? null, + scopes: model.scopes ?? null, + redirect_uris: + model.redirectUris != null + ? model.redirectUris.map(serializeRedirectUri) + : null, +}); diff --git a/src/workos-connect/serializers/user-consent-option-choice.serializer.ts b/src/workos-connect/serializers/user-consent-option-choice.serializer.ts new file mode 100644 index 000000000..9933a0a18 --- /dev/null +++ b/src/workos-connect/serializers/user-consent-option-choice.serializer.ts @@ -0,0 +1,20 @@ +// This file is auto-generated by oagen. Do not edit. + +import type { + UserConsentOptionChoice, + UserConsentOptionChoiceResponse, +} from '../interfaces/user-consent-option-choice.interface'; + +export const deserializeUserConsentOptionChoice = ( + response: UserConsentOptionChoiceResponse, +): UserConsentOptionChoice => ({ + value: response.value, + label: response.label, +}); + +export const serializeUserConsentOptionChoice = ( + model: UserConsentOptionChoice, +): UserConsentOptionChoiceResponse => ({ + value: model.value, + label: model.label, +}); diff --git a/src/workos-connect/serializers/user-consent-option.serializer.ts b/src/workos-connect/serializers/user-consent-option.serializer.ts new file mode 100644 index 000000000..61c03031e --- /dev/null +++ b/src/workos-connect/serializers/user-consent-option.serializer.ts @@ -0,0 +1,28 @@ +// This file is auto-generated by oagen. Do not edit. + +import type { + UserConsentOption, + UserConsentOptionResponse, +} from '../interfaces/user-consent-option.interface'; +import { + deserializeUserConsentOptionChoice, + serializeUserConsentOptionChoice, +} from './user-consent-option-choice.serializer'; + +export const deserializeUserConsentOption = ( + response: UserConsentOptionResponse, +): UserConsentOption => ({ + claim: response.claim, + type: response.type, + label: response.label, + choices: response.choices.map(deserializeUserConsentOptionChoice), +}); + +export const serializeUserConsentOption = ( + model: UserConsentOption, +): UserConsentOptionResponse => ({ + claim: model.claim, + type: model.type, + label: model.label, + choices: model.choices.map(serializeUserConsentOptionChoice), +}); diff --git a/src/workos-connect/serializers/user-management-login-request.serializer.ts b/src/workos-connect/serializers/user-management-login-request.serializer.ts new file mode 100644 index 000000000..81c5b7864 --- /dev/null +++ b/src/workos-connect/serializers/user-management-login-request.serializer.ts @@ -0,0 +1,36 @@ +// This file is auto-generated by oagen. Do not edit. + +import type { + UserManagementLoginRequest, + UserManagementLoginRequestResponse, +} from '../interfaces/user-management-login-request.interface'; +import { + deserializeUserObject, + serializeUserObject, +} from './user-object.serializer'; +import { + deserializeUserConsentOption, + serializeUserConsentOption, +} from './user-consent-option.serializer'; + +export const deserializeUserManagementLoginRequest = ( + response: UserManagementLoginRequestResponse, +): UserManagementLoginRequest => ({ + externalAuthId: response.external_auth_id, + user: deserializeUserObject(response.user), + userConsentOptions: + response.user_consent_options != null + ? response.user_consent_options.map(deserializeUserConsentOption) + : undefined, +}); + +export const serializeUserManagementLoginRequest = ( + model: UserManagementLoginRequest, +): UserManagementLoginRequestResponse => ({ + external_auth_id: model.externalAuthId, + user: serializeUserObject(model.user), + user_consent_options: + model.userConsentOptions != null + ? model.userConsentOptions.map(serializeUserConsentOption) + : undefined, +}); diff --git a/src/workos-connect/serializers/user-object.serializer.ts b/src/workos-connect/serializers/user-object.serializer.ts new file mode 100644 index 000000000..94dbd1409 --- /dev/null +++ b/src/workos-connect/serializers/user-object.serializer.ts @@ -0,0 +1,24 @@ +// This file is auto-generated by oagen. Do not edit. + +import type { + UserObject, + UserObjectResponse, +} from '../interfaces/user-object.interface'; + +export const deserializeUserObject = ( + response: UserObjectResponse, +): UserObject => ({ + id: response.id, + email: response.email, + firstName: response.first_name, + lastName: response.last_name, + metadata: response.metadata, +}); + +export const serializeUserObject = (model: UserObject): UserObjectResponse => ({ + id: model.id, + email: model.email, + first_name: model.firstName, + last_name: model.lastName, + metadata: model.metadata, +}); diff --git a/src/workos-connect/work-os-connect.spec.ts b/src/workos-connect/work-os-connect.spec.ts new file mode 100644 index 000000000..0bcd863f2 --- /dev/null +++ b/src/workos-connect/work-os-connect.spec.ts @@ -0,0 +1,51 @@ +// This file is auto-generated by oagen. Do not edit. + +import fetch from 'jest-fetch-mock'; +import { + fetchOnce, + fetchURL, + fetchMethod, + fetchBody, + testUnauthorized, +} from '../common/utils/test-utils'; +import { WorkOS } from '../workos'; + +import externalAuthCompleteResponseFixture from './fixtures/external-auth-complete-response.fixture.json'; + +const workos = new WorkOS('sk_test_Sz3IQjepeSWaI4cMS4ms4sMuU'); + +describe('WorkOSConnect', () => { + beforeEach(() => fetch.resetMocks()); + + describe('completeLogin', () => { + it('sends the correct request and returns result', async () => { + fetchOnce(externalAuthCompleteResponseFixture); + + const result = await workos.workOsConnect.completeLogin({ + externalAuthId: 'external_auth_id_01234', + user: { id: '01234', email: 'test@example.com' }, + }); + + expect(fetchMethod()).toBe('POST'); + expect(new URL(String(fetchURL())).pathname).toBe( + '/authkit/oauth2/complete', + ); + expect(fetchBody()).toEqual( + expect.objectContaining({ + external_auth_id: 'external_auth_id_01234', + user: { id: '01234', email: 'test@example.com' }, + }), + ); + expect(result.redirectUri).toBe( + 'https://your-authkit-domain.workos.com/oauth/authorize/complete?state=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdGF0ZSI6InJhbmRvbV9zdGF0ZV9zdHJpbmciLCJpYXQiOjE3NDI2MDQ4NTN9.abc123def456ghi789', + ); + }); + + testUnauthorized(() => + workos.workOsConnect.completeLogin({ + externalAuthId: 'external_auth_id_01234', + user: { id: '01234', email: 'test@example.com' }, + }), + ); + }); +}); diff --git a/src/workos-connect/work-os-connect.ts b/src/workos-connect/work-os-connect.ts new file mode 100644 index 000000000..751f524e3 --- /dev/null +++ b/src/workos-connect/work-os-connect.ts @@ -0,0 +1,43 @@ +// This file is auto-generated by oagen. Do not edit. + +import type { WorkOS } from '../workos'; +import type { + ExternalAuthCompleteResponse, + ExternalAuthCompleteResponseWire, +} from './interfaces/external-auth-complete-response.interface'; +import type { UserManagementLoginRequest } from './interfaces/user-management-login-request.interface'; +import { deserializeExternalAuthCompleteResponse } from './serializers/external-auth-complete-response.serializer'; +import { serializeUserManagementLoginRequest } from './serializers/user-management-login-request.serializer'; + +export class WorkOSConnect { + constructor(private readonly workos: WorkOS) {} + + /** + * Complete external authentication + * + * Completes an external authentication flow and returns control to AuthKit. This endpoint is used with [Standalone Connect](https://workos.com/docs/authkit/connect/standalone) to bridge your existing authentication system with the Connect OAuth API infrastructure. + * + * After successfully authenticating a user in your application, calling this endpoint will: + * + * - Create or update the user in AuthKit, using the given `id` as its `external_id`. + * - Return a `redirect_uri` your application should redirect to in order for AuthKit to complete the flow + * + * Users are automatically created or updated based on the `id` and `email` provided. If a user with the same `id` exists, their information is updated. Otherwise, a new user is created. + * + * If you provide a new `id` with an `email` that already belongs to an existing user, the request will fail with an error as email addresses are unique to a user. + * @param payload - Object containing externalAuthId, user. + * @returns {ExternalAuthCompleteResponse} + * @throws {BadRequestException} 400 + * @throws {NotFoundException} 404 + * @throws {UnprocessableEntityException} 422 + */ + async completeLogin( + payload: UserManagementLoginRequest, + ): Promise { + const { data } = await this.workos.post( + '/authkit/oauth2/complete', + serializeUserManagementLoginRequest(payload), + ); + return deserializeExternalAuthCompleteResponse(data); + } +} From 60c3f6f626c6bba203c2b8853565893a47776424 Mon Sep 17 00:00:00 2001 From: "Garen J. Torikian" Date: Thu, 26 Mar 2026 14:53:44 -0400 Subject: [PATCH 06/17] Update test-utils to include more helpers --- src/common/utils/test-utils.ts | 58 ++++++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) diff --git a/src/common/utils/test-utils.ts b/src/common/utils/test-utils.ts index 89269a88c..5c54812c8 100644 --- a/src/common/utils/test-utils.ts +++ b/src/common/utils/test-utils.ts @@ -1,3 +1,5 @@ +// This file is auto-generated by oagen. Do not edit. + import fetch, { MockParams } from 'jest-fetch-mock'; export function fetchOnce( @@ -37,3 +39,59 @@ export function fetchBody({ raw = false } = {}) { } return JSON.parse(String(body)); } + +/** + * Shared test helper: asserts that the given async function throws when the + * server responds with 401 Unauthorized. + */ +export function testUnauthorized(fn: () => Promise) { + it('throws on unauthorized', async () => { + fetchOnce({ message: 'Unauthorized' }, { status: 401 }); + await expect(fn()).rejects.toThrow('Could not authorize the request'); + }); +} + +/** + * Shared test helper: asserts that a paginated list call returns the expected + * shape (data array + listMetadata) and hits the correct endpoint. + */ +export function testPaginatedList( + fn: () => Promise, + pathContains: string, +) { + it('returns paginated results', async () => { + // Caller must have called fetchOnce with the list fixture before invoking fn + const { data, listMetadata } = await fn(); + expect(fetchURL()).toContain(pathContains); + expect(fetchSearchParams()).toHaveProperty('order'); + expect(Array.isArray(data)).toBe(true); + expect(listMetadata).toBeDefined(); + }); +} + +/** + * Shared test helper: asserts that a paginated list call returns empty data + * when the server responds with an empty list. + */ +export function testEmptyResults(fn: () => Promise) { + it('handles empty results', async () => { + fetchOnce({ data: [], list_metadata: { before: null, after: null } }); + const { data } = await fn(); + expect(data).toEqual([]); + }); +} + +/** + * Shared test helper: asserts that pagination params are forwarded correctly. + */ +export function testPaginationParams( + fn: (opts: any) => Promise, + fixture: any, +) { + it('forwards pagination params', async () => { + fetchOnce(fixture); + await fn({ limit: 10, after: 'cursor_abc' }); + expect(fetchSearchParams()['limit']).toBe('10'); + expect(fetchSearchParams()['after']).toBe('cursor_abc'); + }); +} From 44e142ec1d96b7ad93baf67ebe68431f1d0d97cb Mon Sep 17 00:00:00 2001 From: "Garen J. Torikian" Date: Thu, 26 Mar 2026 15:59:48 -0400 Subject: [PATCH 07/17] feat(node): wire new services into WorkOS client Adds imports and readonly properties for AdminPortal, Permissions, Radar, WebhooksEndpoints, WorkOSConnect, Applications, and ApplicationClientSecrets. Adds createPaginatedList helper used by generated paginated list methods. Co-Authored-By: Claude Opus 4.6 (1M context) --- src/common/utils/fetch-and-deserialize.ts | 31 +++++++++++++++++++++++ src/workos.ts | 22 ++++++++++++++++ 2 files changed, 53 insertions(+) diff --git a/src/common/utils/fetch-and-deserialize.ts b/src/common/utils/fetch-and-deserialize.ts index 61c646c73..694ccc39e 100644 --- a/src/common/utils/fetch-and-deserialize.ts +++ b/src/common/utils/fetch-and-deserialize.ts @@ -1,3 +1,5 @@ +// This file is auto-generated by oagen. Do not edit. + import { WorkOS } from '../../workos'; import { GetOptions, @@ -5,6 +7,7 @@ import { ListResponse, PaginationOptions, } from '../interfaces'; +import { AutoPaginatable } from './pagination'; import { deserializeList } from '../serializers'; const setDefaultOptions = (options?: PaginationOptions): PaginationOptions => { @@ -28,3 +31,31 @@ export const fetchAndDeserialize = async ( return deserializeList(data, deserializeFn); }; + +export async function createPaginatedList< + TResponse, + TModel, + TOptions extends PaginationOptions, +>( + workos: WorkOS, + endpoint: string, + deserializeFn: (r: TResponse) => TModel, + options?: TOptions, +): Promise> { + return new AutoPaginatable( + await fetchAndDeserialize( + workos, + endpoint, + deserializeFn, + options, + ), + (params) => + fetchAndDeserialize( + workos, + endpoint, + deserializeFn, + params, + ), + options, + ); +} diff --git a/src/workos.ts b/src/workos.ts index 4d5b45156..d24d82b78 100644 --- a/src/workos.ts +++ b/src/workos.ts @@ -1,3 +1,5 @@ +// This file is auto-generated by oagen. Do not edit. + import { ApiKeyRequiredException, GenericServerException, @@ -47,6 +49,13 @@ import { ParseError } from './common/exceptions/parse-error'; import { getEnv } from './common/utils/env'; import { getRuntimeInfo } from './common/utils/runtime-info'; import { version as VERSION } from '../package.json' with { type: 'json' }; +import { AdminPortal } from './admin-portal/admin-portal'; +import { Permissions } from './permissions/permissions'; +import { Radar } from './radar/radar'; +import { WebhooksEndpoints } from './webhooks/webhooks-endpoints'; +import { WorkOSConnect } from './workos-connect/work-os-connect'; +import { Applications } from './workos-connect/applications'; +import { ApplicationClientSecrets } from './workos-connect/application-client-secrets'; const DEFAULT_HOSTNAME = 'api.workos.com'; @@ -54,6 +63,7 @@ const HEADER_AUTHORIZATION = 'Authorization'; const HEADER_IDEMPOTENCY_KEY = 'Idempotency-Key'; const HEADER_WARRANT_TOKEN = 'Warrant-Token'; +/** WorkOS REST API */ export class WorkOS { readonly baseURL: string; readonly client: HttpClient; @@ -508,4 +518,16 @@ export class WorkOS { } } } + + readonly adminPortal = new AdminPortal(this); + readonly permissions = new Permissions(this); + readonly radar = new Radar(this); + readonly webhooksEndpoints = new WebhooksEndpoints(this); + readonly workOsConnect = new WorkOSConnect(this); + readonly applications = new Applications(this); + readonly applicationClientSecrets = new ApplicationClientSecrets(this); + /** Production */ + static readonly SERVER_PRODUCTION = 'https://api.workos.com'; + /** Staging */ + static readonly SERVER_STAGING = 'https://api.workos-test.com'; } From 2b5ca3c69dd4437719972f8ca8e2d1539898cb03 Mon Sep 17 00:00:00 2001 From: "Garen J. Torikian" Date: Thu, 26 Mar 2026 16:04:38 -0400 Subject: [PATCH 08/17] feat(node): add generated model dependencies for new services Adds the minimal set of auto-generated interfaces and serializers needed by the new service classes: - 12 common enum/type interfaces (referenced by radar, webhooks, admin-portal models) - 2 authorization model files (referenced by permissions serializer) Co-Authored-By: Claude Opus 4.6 (1M context) --- .../update-organization-role.interface.ts | 13 ++++ .../update-organization-role.serializer.ts | 20 ++++++ .../authentication-factor-totp-2.fixture.json | 4 ++ .../authentication-factor-totp-2.interface.ts | 13 ++++ ...reate-webhook-endpoint-events.interface.ts | 72 +++++++++++++++++++ .../generate-link-intent.interface.ts | 10 +++ ...ndalone-assess-request-action.interface.ts | 11 +++ ...ne-assess-request-auth-method.interface.ts | 11 +++ ...alone-response-blocklist-type.interface.ts | 10 +++ ...r-standalone-response-control.interface.ts | 13 ++++ ...r-standalone-response-verdict.interface.ts | 3 + ...pdate-webhook-endpoint-events.interface.ts | 72 +++++++++++++++++++ ...pdate-webhook-endpoint-status.interface.ts | 3 + .../webhook-endpoint-json-status.interface.ts | 3 + 14 files changed, 258 insertions(+) create mode 100644 src/authorization/interfaces/update-organization-role.interface.ts create mode 100644 src/authorization/serializers/update-organization-role.serializer.ts create mode 100644 src/common/fixtures/authentication-factor-totp-2.fixture.json create mode 100644 src/common/interfaces/authentication-factor-totp-2.interface.ts create mode 100644 src/common/interfaces/create-webhook-endpoint-events.interface.ts create mode 100644 src/common/interfaces/generate-link-intent.interface.ts create mode 100644 src/common/interfaces/radar-standalone-assess-request-action.interface.ts create mode 100644 src/common/interfaces/radar-standalone-assess-request-auth-method.interface.ts create mode 100644 src/common/interfaces/radar-standalone-response-blocklist-type.interface.ts create mode 100644 src/common/interfaces/radar-standalone-response-control.interface.ts create mode 100644 src/common/interfaces/radar-standalone-response-verdict.interface.ts create mode 100644 src/common/interfaces/update-webhook-endpoint-events.interface.ts create mode 100644 src/common/interfaces/update-webhook-endpoint-status.interface.ts create mode 100644 src/common/interfaces/webhook-endpoint-json-status.interface.ts diff --git a/src/authorization/interfaces/update-organization-role.interface.ts b/src/authorization/interfaces/update-organization-role.interface.ts new file mode 100644 index 000000000..2630368ae --- /dev/null +++ b/src/authorization/interfaces/update-organization-role.interface.ts @@ -0,0 +1,13 @@ +// This file is auto-generated by oagen. Do not edit. + +export interface UpdateOrganizationRole { + /** A descriptive name for the role. */ + name?: string; + /** An optional description of the role's purpose. */ + description?: string | null; +} + +export interface UpdateOrganizationRoleResponse { + name?: string; + description?: string | null; +} diff --git a/src/authorization/serializers/update-organization-role.serializer.ts b/src/authorization/serializers/update-organization-role.serializer.ts new file mode 100644 index 000000000..0c4734357 --- /dev/null +++ b/src/authorization/serializers/update-organization-role.serializer.ts @@ -0,0 +1,20 @@ +// This file is auto-generated by oagen. Do not edit. + +import type { + UpdateOrganizationRole, + UpdateOrganizationRoleResponse, +} from '../interfaces/update-organization-role.interface'; + +export const deserializeUpdateOrganizationRole = ( + response: UpdateOrganizationRoleResponse, +): UpdateOrganizationRole => ({ + name: response.name, + description: response.description ?? null, +}); + +export const serializeUpdateOrganizationRole = ( + model: UpdateOrganizationRole, +): UpdateOrganizationRoleResponse => ({ + name: model.name, + description: model.description ?? null, +}); diff --git a/src/common/fixtures/authentication-factor-totp-2.fixture.json b/src/common/fixtures/authentication-factor-totp-2.fixture.json new file mode 100644 index 000000000..14ea6dc07 --- /dev/null +++ b/src/common/fixtures/authentication-factor-totp-2.fixture.json @@ -0,0 +1,4 @@ +{ + "issuer": "WorkOS", + "user": "user@example.com" +} diff --git a/src/common/interfaces/authentication-factor-totp-2.interface.ts b/src/common/interfaces/authentication-factor-totp-2.interface.ts new file mode 100644 index 000000000..1b9adfc74 --- /dev/null +++ b/src/common/interfaces/authentication-factor-totp-2.interface.ts @@ -0,0 +1,13 @@ +// This file is auto-generated by oagen. Do not edit. + +export interface AuthenticationFactorTotp2 { + /** Your application or company name displayed in the user's authenticator app. Defaults to your WorkOS team name. */ + issuer: string; + /** The user's account name displayed in their authenticator app. Defaults to the user's email. */ + user: string; +} + +export interface AuthenticationFactorTotp2Response { + issuer: string; + user: string; +} diff --git a/src/common/interfaces/create-webhook-endpoint-events.interface.ts b/src/common/interfaces/create-webhook-endpoint-events.interface.ts new file mode 100644 index 000000000..f6403c55c --- /dev/null +++ b/src/common/interfaces/create-webhook-endpoint-events.interface.ts @@ -0,0 +1,72 @@ +// This file is auto-generated by oagen. Do not edit. + +export type CreateWebhookEndpointEvents = + | 'authentication.email_verification_succeeded' + | 'authentication.magic_auth_failed' + | 'authentication.magic_auth_succeeded' + | 'authentication.mfa_succeeded' + | 'authentication.oauth_failed' + | 'authentication.oauth_succeeded' + | 'authentication.password_failed' + | 'authentication.password_succeeded' + | 'authentication.passkey_failed' + | 'authentication.passkey_succeeded' + | 'authentication.sso_failed' + | 'authentication.sso_started' + | 'authentication.sso_succeeded' + | 'authentication.sso_timed_out' + | 'authentication.radar_risk_detected' + | 'api_key.created' + | 'api_key.revoked' + | 'connection.activated' + | 'connection.deactivated' + | 'connection.saml_certificate_renewal_required' + | 'connection.saml_certificate_renewed' + | 'connection.deleted' + | 'dsync.activated' + | 'dsync.deleted' + | 'dsync.group.created' + | 'dsync.group.deleted' + | 'dsync.group.updated' + | 'dsync.group.user_added' + | 'dsync.group.user_removed' + | 'dsync.user.created' + | 'dsync.user.deleted' + | 'dsync.user.updated' + | 'email_verification.created' + | 'flag.created' + | 'flag.deleted' + | 'flag.updated' + | 'flag.rule_updated' + | 'invitation.accepted' + | 'invitation.created' + | 'invitation.resent' + | 'invitation.revoked' + | 'magic_auth.created' + | 'organization.created' + | 'organization.deleted' + | 'organization.updated' + | 'organization_domain.created' + | 'organization_domain.deleted' + | 'organization_domain.updated' + | 'organization_domain.verified' + | 'organization_domain.verification_failed' + | 'password_reset.created' + | 'password_reset.succeeded' + | 'user.created' + | 'user.updated' + | 'user.deleted' + | 'organization_membership.created' + | 'organization_membership.deleted' + | 'organization_membership.updated' + | 'role.created' + | 'role.deleted' + | 'role.updated' + | 'organization_role.created' + | 'organization_role.deleted' + | 'organization_role.updated' + | 'permission.created' + | 'permission.deleted' + | 'permission.updated' + | 'session.created' + | 'session.revoked'; diff --git a/src/common/interfaces/generate-link-intent.interface.ts b/src/common/interfaces/generate-link-intent.interface.ts new file mode 100644 index 000000000..696d9d318 --- /dev/null +++ b/src/common/interfaces/generate-link-intent.interface.ts @@ -0,0 +1,10 @@ +// This file is auto-generated by oagen. Do not edit. + +export type GenerateLinkIntent = + | 'sso' + | 'dsync' + | 'audit_logs' + | 'log_streams' + | 'domain_verification' + | 'certificate_renewal' + | 'bring_your_own_key'; diff --git a/src/common/interfaces/radar-standalone-assess-request-action.interface.ts b/src/common/interfaces/radar-standalone-assess-request-action.interface.ts new file mode 100644 index 000000000..f28b709c0 --- /dev/null +++ b/src/common/interfaces/radar-standalone-assess-request-action.interface.ts @@ -0,0 +1,11 @@ +// This file is auto-generated by oagen. Do not edit. + +export type RadarStandaloneAssessRequestAction = + | 'login' + | 'signup' + | 'sign-up' + | 'sign-in' + | 'sign_up' + | 'sign_in' + | 'sign in' + | 'sign up'; diff --git a/src/common/interfaces/radar-standalone-assess-request-auth-method.interface.ts b/src/common/interfaces/radar-standalone-assess-request-auth-method.interface.ts new file mode 100644 index 000000000..3dcfbd26e --- /dev/null +++ b/src/common/interfaces/radar-standalone-assess-request-auth-method.interface.ts @@ -0,0 +1,11 @@ +// This file is auto-generated by oagen. Do not edit. + +export type RadarStandaloneAssessRequestAuthMethod = + | 'Password' + | 'Passkey' + | 'Authenticator' + | 'SMS_OTP' + | 'Email_OTP' + | 'Social' + | 'SSO' + | 'Other'; diff --git a/src/common/interfaces/radar-standalone-response-blocklist-type.interface.ts b/src/common/interfaces/radar-standalone-response-blocklist-type.interface.ts new file mode 100644 index 000000000..d467469f3 --- /dev/null +++ b/src/common/interfaces/radar-standalone-response-blocklist-type.interface.ts @@ -0,0 +1,10 @@ +// This file is auto-generated by oagen. Do not edit. + +export type RadarStandaloneResponseBlocklistType = + | 'ip_address' + | 'domain' + | 'email' + | 'device' + | 'user_agent' + | 'device_fingerprint' + | 'country'; diff --git a/src/common/interfaces/radar-standalone-response-control.interface.ts b/src/common/interfaces/radar-standalone-response-control.interface.ts new file mode 100644 index 000000000..7ca68f0c9 --- /dev/null +++ b/src/common/interfaces/radar-standalone-response-control.interface.ts @@ -0,0 +1,13 @@ +// This file is auto-generated by oagen. Do not edit. + +export type RadarStandaloneResponseControl = + | 'bot_detection' + | 'brute_force_attack' + | 'credential_stuffing' + | 'domain_sign_up_rate_limit' + | 'ip_sign_up_rate_limit' + | 'impossible_travel' + | 'repeat_sign_up' + | 'stale_account' + | 'unrecognized_device' + | 'restriction'; diff --git a/src/common/interfaces/radar-standalone-response-verdict.interface.ts b/src/common/interfaces/radar-standalone-response-verdict.interface.ts new file mode 100644 index 000000000..08f083690 --- /dev/null +++ b/src/common/interfaces/radar-standalone-response-verdict.interface.ts @@ -0,0 +1,3 @@ +// This file is auto-generated by oagen. Do not edit. + +export type RadarStandaloneResponseVerdict = 'allow' | 'block' | 'challenge'; diff --git a/src/common/interfaces/update-webhook-endpoint-events.interface.ts b/src/common/interfaces/update-webhook-endpoint-events.interface.ts new file mode 100644 index 000000000..e5b863473 --- /dev/null +++ b/src/common/interfaces/update-webhook-endpoint-events.interface.ts @@ -0,0 +1,72 @@ +// This file is auto-generated by oagen. Do not edit. + +export type UpdateWebhookEndpointEvents = + | 'authentication.email_verification_succeeded' + | 'authentication.magic_auth_failed' + | 'authentication.magic_auth_succeeded' + | 'authentication.mfa_succeeded' + | 'authentication.oauth_failed' + | 'authentication.oauth_succeeded' + | 'authentication.password_failed' + | 'authentication.password_succeeded' + | 'authentication.passkey_failed' + | 'authentication.passkey_succeeded' + | 'authentication.sso_failed' + | 'authentication.sso_started' + | 'authentication.sso_succeeded' + | 'authentication.sso_timed_out' + | 'authentication.radar_risk_detected' + | 'api_key.created' + | 'api_key.revoked' + | 'connection.activated' + | 'connection.deactivated' + | 'connection.saml_certificate_renewal_required' + | 'connection.saml_certificate_renewed' + | 'connection.deleted' + | 'dsync.activated' + | 'dsync.deleted' + | 'dsync.group.created' + | 'dsync.group.deleted' + | 'dsync.group.updated' + | 'dsync.group.user_added' + | 'dsync.group.user_removed' + | 'dsync.user.created' + | 'dsync.user.deleted' + | 'dsync.user.updated' + | 'email_verification.created' + | 'flag.created' + | 'flag.deleted' + | 'flag.updated' + | 'flag.rule_updated' + | 'invitation.accepted' + | 'invitation.created' + | 'invitation.resent' + | 'invitation.revoked' + | 'magic_auth.created' + | 'organization.created' + | 'organization.deleted' + | 'organization.updated' + | 'organization_domain.created' + | 'organization_domain.deleted' + | 'organization_domain.updated' + | 'organization_domain.verified' + | 'organization_domain.verification_failed' + | 'password_reset.created' + | 'password_reset.succeeded' + | 'user.created' + | 'user.updated' + | 'user.deleted' + | 'organization_membership.created' + | 'organization_membership.deleted' + | 'organization_membership.updated' + | 'role.created' + | 'role.deleted' + | 'role.updated' + | 'organization_role.created' + | 'organization_role.deleted' + | 'organization_role.updated' + | 'permission.created' + | 'permission.deleted' + | 'permission.updated' + | 'session.created' + | 'session.revoked'; diff --git a/src/common/interfaces/update-webhook-endpoint-status.interface.ts b/src/common/interfaces/update-webhook-endpoint-status.interface.ts new file mode 100644 index 000000000..773e9c083 --- /dev/null +++ b/src/common/interfaces/update-webhook-endpoint-status.interface.ts @@ -0,0 +1,3 @@ +// This file is auto-generated by oagen. Do not edit. + +export type UpdateWebhookEndpointStatus = 'enabled' | 'disabled'; diff --git a/src/common/interfaces/webhook-endpoint-json-status.interface.ts b/src/common/interfaces/webhook-endpoint-json-status.interface.ts new file mode 100644 index 000000000..a3baae761 --- /dev/null +++ b/src/common/interfaces/webhook-endpoint-json-status.interface.ts @@ -0,0 +1,3 @@ +// This file is auto-generated by oagen. Do not edit. + +export type WebhookEndpointJsonStatus = 'enabled' | 'disabled'; From 2226944cdf6d3f5d4872e66e1515ab8c135139dc Mon Sep 17 00:00:00 2001 From: "Garen J. Torikian" Date: Mon, 13 Apr 2026 17:22:41 -0400 Subject: [PATCH 09/17] handle discriminated enums better --- src/workos-connect/applications.spec.ts | 24 +++++++++++++++++++++--- src/workos-connect/applications.ts | 14 +++++++++----- 2 files changed, 30 insertions(+), 8 deletions(-) diff --git a/src/workos-connect/applications.spec.ts b/src/workos-connect/applications.spec.ts index ab07f194d..9ca0b6964 100644 --- a/src/workos-connect/applications.spec.ts +++ b/src/workos-connect/applications.spec.ts @@ -63,7 +63,11 @@ describe('Applications', () => { it('sends the correct request and returns result', async () => { fetchOnce(connectApplicationFixture); - const result = await workos.applications.create({} as any); + const result = await workos.applications.create({ + applicationType: 'oauth', + name: 'My Application', + isFirstParty: true, + }); expect(fetchMethod()).toBe('POST'); expect(new URL(String(fetchURL())).pathname).toBe( @@ -73,11 +77,25 @@ describe('Applications', () => { expectConnectApplication(result); }); - testUnauthorized(() => workos.applications.create({} as any)); + testUnauthorized(() => workos.applications.create({ + applicationType: 'oauth', + name: 'My Application', + isFirstParty: true, + })); it('throws UnprocessableEntityException on 422', async () => { fetchOnce('', { status: 422 }); - await expect(workos.applications.create({} as any)).rejects.toThrow(); + await expect(workos.applications.create({ + applicationType: 'oauth', + name: 'My Application', + isFirstParty: true, + })).rejects.toThrow(); + }); + + it('throws on unknown applicationType', async () => { + await expect( + workos.applications.create({ applicationType: 'bogus' } as any), + ).rejects.toThrow(/Unknown applicationType: bogus/); }); }); diff --git a/src/workos-connect/applications.ts b/src/workos-connect/applications.ts index aa6d6f537..086372f72 100644 --- a/src/workos-connect/applications.ts +++ b/src/workos-connect/applications.ts @@ -62,13 +62,17 @@ export class Applications { const { data } = await this.workos.post( '/connect/applications', (() => { - switch ((payload as any).applicationType) { + switch (payload.applicationType) { case 'oauth': - return serializeCreateOAuthApplication(payload as any); + return serializeCreateOAuthApplication(payload); case 'm2m': - return serializeCreateM2MApplication(payload as any); - default: - return payload; + return serializeCreateM2MApplication(payload); + default: { + const _unknown: never = payload; + throw new Error( + `Unknown applicationType: ${(_unknown as { applicationType?: unknown }).applicationType}`, + ); + } } })(), ); From e7fb5b2e80218c5a793c27943f550d1c093ef6fd Mon Sep 17 00:00:00 2001 From: "Garen J. Torikian" Date: Mon, 13 Apr 2026 18:17:41 -0400 Subject: [PATCH 10/17] correctly load non-paginated arrays --- .../application-client-secrets.spec.ts | 14 ++++++++------ .../application-client-secrets.ts | 8 ++++---- ...lication-credentials-list-item.fixture.json | 18 ++++++++++-------- 3 files changed, 22 insertions(+), 18 deletions(-) diff --git a/src/workos-connect/application-client-secrets.spec.ts b/src/workos-connect/application-client-secrets.spec.ts index 8ddfec808..703020f8f 100644 --- a/src/workos-connect/application-client-secrets.spec.ts +++ b/src/workos-connect/application-client-secrets.spec.ts @@ -28,12 +28,14 @@ describe('ApplicationClientSecrets', () => { expect(new URL(String(fetchURL())).pathname).toBe( '/connect/applications/test_id/client_secrets', ); - expect(result.object).toBe('connect_application_secret'); - expect(result.id).toBe('secret_01J9Q2Z3X4Y5W6V7U8T9S0R1Q'); - expect(result.secretHint).toBe('abc123'); - expect(result.lastUsedAt).toBe(null); - expect(result.createdAt).toBe('2026-01-15T12:00:00.000Z'); - expect(result.updatedAt).toBe('2026-01-15T12:00:00.000Z'); + expect(Array.isArray(result)).toBe(true); + expect(result).toHaveLength(1); + expect(result[0].object).toBe('connect_application_secret'); + expect(result[0].id).toBe('secret_01J9Q2Z3X4Y5W6V7U8T9S0R1Q'); + expect(result[0].secretHint).toBe('abc123'); + expect(result[0].lastUsedAt).toBe(null); + expect(result[0].createdAt).toBe('2026-01-15T12:00:00.000Z'); + expect(result[0].updatedAt).toBe('2026-01-15T12:00:00.000Z'); }); testUnauthorized(() => workos.applicationClientSecrets.list('test_id')); diff --git a/src/workos-connect/application-client-secrets.ts b/src/workos-connect/application-client-secrets.ts index afe58e2ae..1c6bd94b1 100644 --- a/src/workos-connect/application-client-secrets.ts +++ b/src/workos-connect/application-client-secrets.ts @@ -23,15 +23,15 @@ export class ApplicationClientSecrets { * List all client secrets associated with a Connect Application. * @param id - The application ID or client ID of the Connect Application. * @example "conn_app_01HXYZ123456789ABCDEFGHIJ" - * @returns {ApplicationCredentialsListItem} + * @returns {Promise} * @throws {NotFoundException} 404 */ - async list(id: string): Promise { + async list(id: string): Promise { const { data } = - await this.workos.get( + await this.workos.get( `/connect/applications/${id}/client_secrets`, ); - return deserializeApplicationCredentialsListItem(data); + return data.map(deserializeApplicationCredentialsListItem); } /** diff --git a/src/workos-connect/fixtures/application-credentials-list-item.fixture.json b/src/workos-connect/fixtures/application-credentials-list-item.fixture.json index 4c03cc2e5..e42e400a8 100644 --- a/src/workos-connect/fixtures/application-credentials-list-item.fixture.json +++ b/src/workos-connect/fixtures/application-credentials-list-item.fixture.json @@ -1,8 +1,10 @@ -{ - "object": "connect_application_secret", - "id": "secret_01J9Q2Z3X4Y5W6V7U8T9S0R1Q", - "secret_hint": "abc123", - "last_used_at": null, - "created_at": "2026-01-15T12:00:00.000Z", - "updated_at": "2026-01-15T12:00:00.000Z" -} +[ + { + "object": "connect_application_secret", + "id": "secret_01J9Q2Z3X4Y5W6V7U8T9S0R1Q", + "secret_hint": "abc123", + "last_used_at": null, + "created_at": "2026-01-15T12:00:00.000Z", + "updated_at": "2026-01-15T12:00:00.000Z" + } +] From f38daaa01d892e32a30a272b69702afde7202039 Mon Sep 17 00:00:00 2001 From: "Garen J. Torikian" Date: Mon, 13 Apr 2026 18:59:02 -0400 Subject: [PATCH 11/17] reshape Connect SDK under workos.connect namespace Collapse the split layout (Applications, ApplicationClientSecrets, WorkOSConnect at top level) into a single Connect class under workos.connect, matching the emitter's canonical mount-rule output. - workos.connect.createApplication(payload) with discriminated union dispatch - workos.connect.listApplicationClientSecrets(id) returns Model[] for the unpaginated array response (was silently deserializing the array as a single object before) - workos.connect.completeLogin(payload) keeps the semantic name via the new POST /authkit/oauth2/complete operation hint in oagen.config.ts - drops the ugly workOsConnect casing in favor of connect Addresses reviewer comments on #1535: - r3074856207: sub-resources now namespaced under workos.connect - r3074861326: workOsConnect renamed to connect - r3074705257: default branch on discriminated union dispatch throws - r3074705330: list()-returning-single-item replaced with Model[] return - r3074783536: as-any casts removed; typed narrowing + never exhaustiveness Co-Authored-By: Claude Opus 4.6 (1M context) --- src/connect/connect.spec.ts | 208 ++++++++++++++++ src/connect/connect.ts | 234 ++++++++++++++++++ ...ication-credentials-list-item.fixture.json | 8 + .../fixtures/connect-application.fixture.json | 6 +- .../create-application-secret.fixture.json | 0 .../create-m2m-application.fixture.json | 6 +- .../create-oauth-application.fixture.json | 6 +- ...ternal-auth-complete-response.fixture.json | 0 .../list-connect-application.fixture.json | 6 +- ...ew-connect-application-secret.fixture.json | 0 .../fixtures/redirect-uri-input.fixture.json | 4 + .../update-oauth-application.fixture.json | 6 +- .../user-consent-option-choice.fixture.json | 0 .../fixtures/user-consent-option.fixture.json | 0 ...user-management-login-request.fixture.json | 0 .../fixtures/user-object.fixture.json | 0 ...ication-credentials-list-item.interface.ts | 0 .../applications-order.interface.ts | 3 + .../connect-application.interface.ts | 0 .../create-application-secret.interface.ts | 0 .../create-m2m-application.interface.ts | 0 .../create-oauth-application.interface.ts | 10 +- ...ternal-auth-complete-response.interface.ts | 0 .../interfaces/index.ts | 3 +- ...ew-connect-application-secret.interface.ts | 0 .../redirect-uri-input.interface.ts | 13 + .../update-oauth-application.interface.ts | 10 +- .../user-consent-option-choice.interface.ts | 0 .../user-consent-option.interface.ts | 0 ...user-management-login-request.interface.ts | 0 .../interfaces/user-object.interface.ts | 0 src/connect/serializers.spec.ts | 146 +++++++++++ ...cation-credentials-list-item.serializer.ts | 0 .../connect-application.serializer.ts | 0 .../create-application-secret.serializer.ts | 0 .../create-m2m-application.serializer.ts | 0 .../create-oauth-application.serializer.ts | 10 +- ...ernal-auth-complete-response.serializer.ts | 0 ...w-connect-application-secret.serializer.ts | 0 .../redirect-uri-input.serializer.ts | 20 ++ .../update-oauth-application.serializer.ts | 10 +- .../user-consent-option-choice.serializer.ts | 0 .../user-consent-option.serializer.ts | 0 ...ser-management-login-request.serializer.ts | 0 .../serializers/user-object.serializer.ts | 0 .../application-client-secrets.spec.ts | 93 ------- .../application-client-secrets.ts | 72 ------ src/workos-connect/applications.spec.ts | 157 ------------ src/workos-connect/applications.ts | 132 ---------- ...ication-credentials-list-item.fixture.json | 10 - .../fixtures/redirect-uri.fixture.json | 8 - .../interfaces/redirect-uri.interface.ts | 25 -- .../serializers/redirect-uri.serializer.ts | 28 --- src/workos-connect/work-os-connect.spec.ts | 51 ---- src/workos-connect/work-os-connect.ts | 43 ---- src/workos.ts | 8 +- 56 files changed, 683 insertions(+), 653 deletions(-) create mode 100644 src/connect/connect.spec.ts create mode 100644 src/connect/connect.ts create mode 100644 src/connect/fixtures/application-credentials-list-item.fixture.json rename src/{workos-connect => connect}/fixtures/connect-application.fixture.json (83%) rename src/{workos-connect => connect}/fixtures/create-application-secret.fixture.json (100%) rename src/{workos-connect => connect}/fixtures/create-m2m-application.fixture.json (74%) rename src/{workos-connect => connect}/fixtures/create-oauth-application.fixture.json (84%) rename src/{workos-connect => connect}/fixtures/external-auth-complete-response.fixture.json (100%) rename src/{workos-connect => connect}/fixtures/list-connect-application.fixture.json (84%) rename src/{workos-connect => connect}/fixtures/new-connect-application-secret.fixture.json (100%) create mode 100644 src/connect/fixtures/redirect-uri-input.fixture.json rename src/{workos-connect => connect}/fixtures/update-oauth-application.fixture.json (76%) rename src/{workos-connect => connect}/fixtures/user-consent-option-choice.fixture.json (100%) rename src/{workos-connect => connect}/fixtures/user-consent-option.fixture.json (100%) rename src/{workos-connect => connect}/fixtures/user-management-login-request.fixture.json (100%) rename src/{workos-connect => connect}/fixtures/user-object.fixture.json (100%) rename src/{workos-connect => connect}/interfaces/application-credentials-list-item.interface.ts (100%) create mode 100644 src/connect/interfaces/applications-order.interface.ts rename src/{workos-connect => connect}/interfaces/connect-application.interface.ts (100%) rename src/{workos-connect => connect}/interfaces/create-application-secret.interface.ts (100%) rename src/{workos-connect => connect}/interfaces/create-m2m-application.interface.ts (100%) rename src/{workos-connect => connect}/interfaces/create-oauth-application.interface.ts (85%) rename src/{workos-connect => connect}/interfaces/external-auth-complete-response.interface.ts (100%) rename src/{workos-connect => connect}/interfaces/index.ts (88%) rename src/{workos-connect => connect}/interfaces/new-connect-application-secret.interface.ts (100%) create mode 100644 src/connect/interfaces/redirect-uri-input.interface.ts rename src/{workos-connect => connect}/interfaces/update-oauth-application.interface.ts (74%) rename src/{workos-connect => connect}/interfaces/user-consent-option-choice.interface.ts (100%) rename src/{workos-connect => connect}/interfaces/user-consent-option.interface.ts (100%) rename src/{workos-connect => connect}/interfaces/user-management-login-request.interface.ts (100%) rename src/{workos-connect => connect}/interfaces/user-object.interface.ts (100%) create mode 100644 src/connect/serializers.spec.ts rename src/{workos-connect => connect}/serializers/application-credentials-list-item.serializer.ts (100%) rename src/{workos-connect => connect}/serializers/connect-application.serializer.ts (100%) rename src/{workos-connect => connect}/serializers/create-application-secret.serializer.ts (100%) rename src/{workos-connect => connect}/serializers/create-m2m-application.serializer.ts (100%) rename src/{workos-connect => connect}/serializers/create-oauth-application.serializer.ts (80%) rename src/{workos-connect => connect}/serializers/external-auth-complete-response.serializer.ts (100%) rename src/{workos-connect => connect}/serializers/new-connect-application-secret.serializer.ts (100%) create mode 100644 src/connect/serializers/redirect-uri-input.serializer.ts rename src/{workos-connect => connect}/serializers/update-oauth-application.serializer.ts (73%) rename src/{workos-connect => connect}/serializers/user-consent-option-choice.serializer.ts (100%) rename src/{workos-connect => connect}/serializers/user-consent-option.serializer.ts (100%) rename src/{workos-connect => connect}/serializers/user-management-login-request.serializer.ts (100%) rename src/{workos-connect => connect}/serializers/user-object.serializer.ts (100%) delete mode 100644 src/workos-connect/application-client-secrets.spec.ts delete mode 100644 src/workos-connect/application-client-secrets.ts delete mode 100644 src/workos-connect/applications.spec.ts delete mode 100644 src/workos-connect/applications.ts delete mode 100644 src/workos-connect/fixtures/application-credentials-list-item.fixture.json delete mode 100644 src/workos-connect/fixtures/redirect-uri.fixture.json delete mode 100644 src/workos-connect/interfaces/redirect-uri.interface.ts delete mode 100644 src/workos-connect/serializers/redirect-uri.serializer.ts delete mode 100644 src/workos-connect/work-os-connect.spec.ts delete mode 100644 src/workos-connect/work-os-connect.ts diff --git a/src/connect/connect.spec.ts b/src/connect/connect.spec.ts new file mode 100644 index 000000000..704fa24cd --- /dev/null +++ b/src/connect/connect.spec.ts @@ -0,0 +1,208 @@ +// This file is auto-generated by oagen. Do not edit. + +import fetch from 'jest-fetch-mock'; +import { + fetchOnce, + fetchURL, + fetchMethod, + fetchSearchParams, + fetchBody, + testEmptyResults, + testPaginationParams, + testUnauthorized, +} from '../common/utils/test-utils'; +import { WorkOS } from '../workos'; + +import externalAuthCompleteResponseFixture from './fixtures/external-auth-complete-response.fixture.json'; +import listConnectApplicationFixture from './fixtures/list-connect-application.fixture.json'; +import connectApplicationFixture from './fixtures/connect-application.fixture.json'; +import applicationCredentialsListItemFixture from './fixtures/application-credentials-list-item.fixture.json'; +import newConnectApplicationSecretFixture from './fixtures/new-connect-application-secret.fixture.json'; + +const workos = new WorkOS('sk_test_Sz3IQjepeSWaI4cMS4ms4sMuU'); + +function expectConnectApplication(result: any) { + expect(result.object).toBe('connect_application'); + expect(result.id).toBe('conn_app_01HXYZ123456789ABCDEFGHIJ'); + expect(result.clientId).toBe('client_01HXYZ123456789ABCDEFGHIJ'); + expect(result.description).toBe('An application for managing user access'); + expect(result.name).toBe('My Application'); + expect(result.scopes).toEqual(["openid","profile","email"]); + expect(result.createdAt).toBe('2026-01-15T12:00:00.000Z'); + expect(result.updatedAt).toBe('2026-01-15T12:00:00.000Z'); +} + +describe('Connect', () => { + beforeEach(() => fetch.resetMocks()); + + describe('completeLogin', () => { + it('sends the correct request and returns result', async () => { + fetchOnce(externalAuthCompleteResponseFixture); + + const result = await workos.connect.completeLogin({ externalAuthId: 'external_auth_id_01234', user: { id: '01234', email: 'test@example.com' } }); + + expect(fetchMethod()).toBe('POST'); + expect(new URL(String(fetchURL())).pathname).toBe('/authkit/oauth2/complete'); + expect(fetchBody()).toEqual(expect.objectContaining({ external_auth_id: 'external_auth_id_01234', user: { id: '01234', email: 'test@example.com' } })); + expect(result.redirectUri).toBe('https://your-authkit-domain.workos.com/oauth/authorize/complete?state=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdGF0ZSI6InJhbmRvbV9zdGF0ZV9zdHJpbmciLCJpYXQiOjE3NDI2MDQ4NTN9.abc123def456ghi789'); + }); + + testUnauthorized(() => workos.connect.completeLogin({ externalAuthId: 'external_auth_id_01234', user: { id: '01234', email: 'test@example.com' } })); + }); + + describe('listApplications', () => { + it('returns paginated results', async () => { + fetchOnce(listConnectApplicationFixture); + + const { data, listMetadata } = await workos.connect.listApplications(); + + expect(fetchMethod()).toBe('GET'); + expect(new URL(String(fetchURL())).pathname).toBe('/connect/applications'); + expect(fetchSearchParams()).toHaveProperty('order'); + expect(Array.isArray(data)).toBe(true); + expect(listMetadata).toBeDefined(); + expect(data.length).toBeGreaterThan(0); + expectConnectApplication(data[0]); + }); + + testEmptyResults(() => workos.connect.listApplications()); + + testPaginationParams( + (opts) => workos.connect.listApplications(opts), + listConnectApplicationFixture, + ); + + testUnauthorized(() => workos.connect.listApplications()); + }); + + describe('createApplication', () => { + it('sends the correct request and returns result', async () => { + fetchOnce(connectApplicationFixture); + + const result = await workos.connect.createApplication({ name: 'Test', applicationType: 'oauth', isFirstParty: true }); + + expect(fetchMethod()).toBe('POST'); + expect(new URL(String(fetchURL())).pathname).toBe('/connect/applications'); + expect(fetchBody()).toEqual(expect.objectContaining({ name: 'Test', application_type: 'oauth', is_first_party: true })); + expectConnectApplication(result); + }); + + testUnauthorized(() => workos.connect.createApplication({ name: 'Test', applicationType: 'oauth', isFirstParty: true })); + + it('throws UnprocessableEntityException on 422', async () => { + fetchOnce('', { status: 422 }); + await expect(workos.connect.createApplication({ name: 'Test', applicationType: 'oauth', isFirstParty: true })).rejects.toThrow(); + }); + }); + + describe('getApplication', () => { + it('returns the expected result', async () => { + fetchOnce(connectApplicationFixture); + + const result = await workos.connect.getApplication('test_id'); + + expect(fetchMethod()).toBe('GET'); + expect(new URL(String(fetchURL())).pathname).toBe('/connect/applications/test_id'); + expectConnectApplication(result); + }); + + testUnauthorized(() => workos.connect.getApplication('test_id')); + + it('throws NotFoundException on 404', async () => { + fetchOnce('', { status: 404 }); + await expect(workos.connect.getApplication('test_id')).rejects.toThrow(); + }); + }); + + describe('updateApplication', () => { + it('sends the correct request and returns result', async () => { + fetchOnce(connectApplicationFixture); + + const result = await workos.connect.updateApplication('test_id', {}); + + expect(fetchMethod()).toBe('PUT'); + expect(new URL(String(fetchURL())).pathname).toBe('/connect/applications/test_id'); + expect(fetchBody()).toBeDefined(); + expectConnectApplication(result); + }); + + testUnauthorized(() => workos.connect.updateApplication('test_id', {})); + + it('throws UnprocessableEntityException on 422', async () => { + fetchOnce('', { status: 422 }); + await expect(workos.connect.updateApplication('test_id', {})).rejects.toThrow(); + }); + }); + + describe('deleteApplication', () => { + it('sends a DELETE request', async () => { + fetchOnce({}, { status: 204 }); + + await workos.connect.deleteApplication('test_id'); + + expect(fetchMethod()).toBe('DELETE'); + expect(new URL(String(fetchURL())).pathname).toBe('/connect/applications/test_id'); + }); + }); + + describe('listApplicationClientSecrets', () => { + it('returns the expected result', async () => { + // The API returns an array; wrap the single-item fixture so the test + // exercises the array-response deserialization path without needing a + // second fixture. + fetchOnce([applicationCredentialsListItemFixture]); + + const result = await workos.connect.listApplicationClientSecrets('test_id'); + + expect(fetchMethod()).toBe('GET'); + expect(new URL(String(fetchURL())).pathname).toBe('/connect/applications/test_id/client_secrets'); + expect(Array.isArray(result)).toBe(true); + expect(result).toHaveLength(1); + expect(result[0].object).toBe('connect_application_secret'); + expect(result[0].id).toBe('secret_01J9Q2Z3X4Y5W6V7U8T9S0R1Q'); + expect(result[0].secretHint).toBe('abc123'); + expect(result[0].lastUsedAt).toBe(null); + expect(result[0].createdAt).toBe('2026-01-15T12:00:00.000Z'); + expect(result[0].updatedAt).toBe('2026-01-15T12:00:00.000Z'); + }); + + testUnauthorized(() => workos.connect.listApplicationClientSecrets('test_id')); + }); + + describe('createApplicationClientSecret', () => { + it('sends the correct request and returns result', async () => { + fetchOnce(newConnectApplicationSecretFixture); + + const result = await workos.connect.createApplicationClientSecret('test_id', {}); + + expect(fetchMethod()).toBe('POST'); + expect(new URL(String(fetchURL())).pathname).toBe('/connect/applications/test_id/client_secrets'); + expect(fetchBody()).toBeDefined(); + expect(result.object).toBe('connect_application_secret'); + expect(result.id).toBe('secret_01J9Q2Z3X4Y5W6V7U8T9S0R1Q'); + expect(result.secretHint).toBe('abc123'); + expect(result.lastUsedAt).toBe(null); + expect(result.createdAt).toBe('2026-01-15T12:00:00.000Z'); + expect(result.updatedAt).toBe('2026-01-15T12:00:00.000Z'); + expect(result.secret).toBe('abc123def456ghi789jkl012mno345pqr678stu901vwx234yz'); + }); + + testUnauthorized(() => workos.connect.createApplicationClientSecret('test_id', {})); + + it('throws UnprocessableEntityException on 422', async () => { + fetchOnce('', { status: 422 }); + await expect(workos.connect.createApplicationClientSecret('test_id', {})).rejects.toThrow(); + }); + }); + + describe('deleteClientSecret', () => { + it('sends a DELETE request', async () => { + fetchOnce({}, { status: 204 }); + + await workos.connect.deleteClientSecret('test_id'); + + expect(fetchMethod()).toBe('DELETE'); + expect(new URL(String(fetchURL())).pathname).toBe('/connect/client_secrets/test_id'); + }); + }); +}); diff --git a/src/connect/connect.ts b/src/connect/connect.ts new file mode 100644 index 000000000..3f4f202e3 --- /dev/null +++ b/src/connect/connect.ts @@ -0,0 +1,234 @@ +// This file is auto-generated by oagen. Do not edit. + +import type { WorkOS } from '../workos'; +import type { PaginationOptions } from '../common/interfaces/pagination-options.interface'; +import type { AutoPaginatable } from '../common/utils/pagination'; +import { createPaginatedList } from '../common/utils/fetch-and-deserialize'; +import type { + ExternalAuthCompleteResponse, + ExternalAuthCompleteResponseWire, +} from './interfaces/external-auth-complete-response.interface'; +import type { + ConnectApplication, + ConnectApplicationResponse, +} from './interfaces/connect-application.interface'; +import type { + ApplicationCredentialsListItem, + ApplicationCredentialsListItemResponse, +} from './interfaces/application-credentials-list-item.interface'; +import type { + NewConnectApplicationSecret, + NewConnectApplicationSecretResponse, +} from './interfaces/new-connect-application-secret.interface'; +import type { UserManagementLoginRequest } from './interfaces/user-management-login-request.interface'; +import type { CreateOAuthApplication } from './interfaces/create-oauth-application.interface'; +import type { CreateM2MApplication } from './interfaces/create-m2m-application.interface'; +import type { UpdateOAuthApplication } from './interfaces/update-oauth-application.interface'; +import type { CreateApplicationSecret } from './interfaces/create-application-secret.interface'; +import { deserializeExternalAuthCompleteResponse } from './serializers/external-auth-complete-response.serializer'; +import { deserializeConnectApplication } from './serializers/connect-application.serializer'; +import { deserializeApplicationCredentialsListItem } from './serializers/application-credentials-list-item.serializer'; +import { deserializeNewConnectApplicationSecret } from './serializers/new-connect-application-secret.serializer'; +import { serializeUserManagementLoginRequest } from './serializers/user-management-login-request.serializer'; +import { serializeCreateOAuthApplication } from './serializers/create-oauth-application.serializer'; +import { serializeCreateM2MApplication } from './serializers/create-m2m-application.serializer'; +import { serializeUpdateOAuthApplication } from './serializers/update-oauth-application.serializer'; +import { serializeCreateApplicationSecret } from './serializers/create-application-secret.serializer'; + +export interface ListApplicationsOptions extends PaginationOptions { + /** Filter Connect Applications by organization ID. */ + organizationId?: string; +} + +export class Connect { + constructor(private readonly workos: WorkOS) {} + + /** + * Complete external authentication + * + * Completes an external authentication flow and returns control to AuthKit. This endpoint is used with [Standalone Connect](https://workos.com/docs/authkit/connect/standalone) to bridge your existing authentication system with the Connect OAuth API infrastructure. + * + * After successfully authenticating a user in your application, calling this endpoint will: + * + * - Create or update the user in AuthKit, using the given `id` as its `external_id`. + * - Return a `redirect_uri` your application should redirect to in order for AuthKit to complete the flow + * + * Users are automatically created or updated based on the `id` and `email` provided. If a user with the same `id` exists, their information is updated. Otherwise, a new user is created. + * + * If you provide a new `id` with an `email` that already belongs to an existing user, the request will fail with an error as email addresses are unique to a user. + * @param payload - Object containing externalAuthId, user. + * @returns {Promise} + * @throws {BadRequestException} 400 + * @throws {NotFoundException} 404 + * @throws {UnprocessableEntityException} 422 + */ + async completeLogin( + payload: UserManagementLoginRequest, + ): Promise { + const { data } = await this.workos.post( + '/authkit/oauth2/complete', + serializeUserManagementLoginRequest(payload), + ); + return deserializeExternalAuthCompleteResponse(data); + } + + /** + * List Connect Applications + * + * List all Connect Applications in the current environment with optional filtering. + * @param options - Pagination and filter options. + * @returns {Promise>} + * @throws {UnprocessableEntityException} 422 + */ + async listApplications( + options?: ListApplicationsOptions, + ): Promise> { + return createPaginatedList< + ConnectApplicationResponse, + ConnectApplication, + ListApplicationsOptions + >( + this.workos, + '/connect/applications', + deserializeConnectApplication, + options, + ); + } + + /** + * Create a Connect Application + * + * Create a new Connect Application. Supports both OAuth and Machine-to-Machine (M2M) application types. + * @param payload - The request body. + * @returns {Promise} + * @throws {NotFoundException} 404 + * @throws {UnprocessableEntityException} 422 + */ + async createApplication( + payload: CreateOAuthApplication | CreateM2MApplication, + ): Promise { + const { data } = await this.workos.post( + '/connect/applications', + (() => { + switch (payload.applicationType) { + case 'oauth': + return serializeCreateOAuthApplication(payload); + case 'm2m': + return serializeCreateM2MApplication(payload); + default: { + const _unknown: never = payload; + throw new Error( + `Unknown applicationType: ${(_unknown as { applicationType?: unknown }).applicationType}`, + ); + } + } + })(), + ); + return deserializeConnectApplication(data); + } + + /** + * Get a Connect Application + * + * Retrieve details for a specific Connect Application by ID or client ID. + * @param id - The application ID or client ID of the Connect Application. + * @example "conn_app_01HXYZ123456789ABCDEFGHIJ" + * @returns {Promise} + * @throws {NotFoundException} 404 + */ + async getApplication(id: string): Promise { + const { data } = await this.workos.get( + `/connect/applications/${id}`, + ); + return deserializeConnectApplication(data); + } + + /** + * Update a Connect Application + * + * Update an existing Connect Application. For OAuth applications, you can update redirect URIs. For all applications, you can update the name, description, and scopes. + * @param id - The application ID or client ID of the Connect Application. + * @example "conn_app_01HXYZ123456789ABCDEFGHIJ" + * @param payload - The request body. + * @returns {Promise} + * @throws {NotFoundException} 404 + * @throws {UnprocessableEntityException} 422 + */ + async updateApplication( + id: string, + payload: UpdateOAuthApplication, + ): Promise { + const { data } = await this.workos.put( + `/connect/applications/${id}`, + serializeUpdateOAuthApplication(payload), + ); + return deserializeConnectApplication(data); + } + + /** + * Delete a Connect Application + * + * Delete an existing Connect Application. + * @param id - The application ID or client ID of the Connect Application. + * @example "conn_app_01HXYZ123456789ABCDEFGHIJ" + * @returns {Promise} + * @throws {NotFoundException} 404 + */ + async deleteApplication(id: string): Promise { + await this.workos.delete(`/connect/applications/${id}`); + } + + /** + * List Client Secrets for a Connect Application + * + * List all client secrets associated with a Connect Application. + * @param id - The application ID or client ID of the Connect Application. + * @example "conn_app_01HXYZ123456789ABCDEFGHIJ" + * @returns {Promise} + * @throws {NotFoundException} 404 + */ + async listApplicationClientSecrets( + id: string, + ): Promise { + const { data } = await this.workos.get< + ApplicationCredentialsListItemResponse[] + >(`/connect/applications/${id}/client_secrets`); + return data.map(deserializeApplicationCredentialsListItem); + } + + /** + * Create a new client secret for a Connect Application + * + * Create new secrets for a Connect Application. + * @param id - The application ID or client ID of the Connect Application. + * @example "conn_app_01HXYZ123456789ABCDEFGHIJ" + * @param payload - The request body. + * @returns {Promise} + * @throws {NotFoundException} 404 + * @throws {UnprocessableEntityException} 422 + */ + async createApplicationClientSecret( + id: string, + payload: CreateApplicationSecret, + ): Promise { + const { data } = + await this.workos.post( + `/connect/applications/${id}/client_secrets`, + serializeCreateApplicationSecret(payload), + ); + return deserializeNewConnectApplicationSecret(data); + } + + /** + * Delete a Client Secret + * + * Delete (revoke) an existing client secret. + * @param id - The unique ID of the client secret. + * @example "secret_01J9Q2Z3X4Y5W6V7U8T9S0R1Q" + * @returns {Promise} + * @throws {NotFoundException} 404 + */ + async deleteClientSecret(id: string): Promise { + await this.workos.delete(`/connect/client_secrets/${id}`); + } +} diff --git a/src/connect/fixtures/application-credentials-list-item.fixture.json b/src/connect/fixtures/application-credentials-list-item.fixture.json new file mode 100644 index 000000000..4c03cc2e5 --- /dev/null +++ b/src/connect/fixtures/application-credentials-list-item.fixture.json @@ -0,0 +1,8 @@ +{ + "object": "connect_application_secret", + "id": "secret_01J9Q2Z3X4Y5W6V7U8T9S0R1Q", + "secret_hint": "abc123", + "last_used_at": null, + "created_at": "2026-01-15T12:00:00.000Z", + "updated_at": "2026-01-15T12:00:00.000Z" +} diff --git a/src/workos-connect/fixtures/connect-application.fixture.json b/src/connect/fixtures/connect-application.fixture.json similarity index 83% rename from src/workos-connect/fixtures/connect-application.fixture.json rename to src/connect/fixtures/connect-application.fixture.json index 4cd701a43..94258b881 100644 --- a/src/workos-connect/fixtures/connect-application.fixture.json +++ b/src/connect/fixtures/connect-application.fixture.json @@ -4,7 +4,11 @@ "client_id": "client_01HXYZ123456789ABCDEFGHIJ", "description": "An application for managing user access", "name": "My Application", - "scopes": ["openid", "profile", "email"], + "scopes": [ + "openid", + "profile", + "email" + ], "created_at": "2026-01-15T12:00:00.000Z", "updated_at": "2026-01-15T12:00:00.000Z" } diff --git a/src/workos-connect/fixtures/create-application-secret.fixture.json b/src/connect/fixtures/create-application-secret.fixture.json similarity index 100% rename from src/workos-connect/fixtures/create-application-secret.fixture.json rename to src/connect/fixtures/create-application-secret.fixture.json diff --git a/src/workos-connect/fixtures/create-m2m-application.fixture.json b/src/connect/fixtures/create-m2m-application.fixture.json similarity index 74% rename from src/workos-connect/fixtures/create-m2m-application.fixture.json rename to src/connect/fixtures/create-m2m-application.fixture.json index 130fc51a9..ecfaf00d5 100644 --- a/src/workos-connect/fixtures/create-m2m-application.fixture.json +++ b/src/connect/fixtures/create-m2m-application.fixture.json @@ -2,6 +2,10 @@ "name": "My Application", "application_type": "m2m", "description": "An application for managing user access", - "scopes": ["openid", "profile", "email"], + "scopes": [ + "openid", + "profile", + "email" + ], "organization_id": "org_01EHZNVPK3SFK441A1RGBFSHRT" } diff --git a/src/workos-connect/fixtures/create-oauth-application.fixture.json b/src/connect/fixtures/create-oauth-application.fixture.json similarity index 84% rename from src/workos-connect/fixtures/create-oauth-application.fixture.json rename to src/connect/fixtures/create-oauth-application.fixture.json index 28f4bb301..343693c19 100644 --- a/src/workos-connect/fixtures/create-oauth-application.fixture.json +++ b/src/connect/fixtures/create-oauth-application.fixture.json @@ -2,7 +2,11 @@ "name": "My Application", "application_type": "oauth", "description": "An application for managing user access", - "scopes": ["openid", "profile", "email"], + "scopes": [ + "openid", + "profile", + "email" + ], "redirect_uris": [ { "uri": "https://example.com/callback", diff --git a/src/workos-connect/fixtures/external-auth-complete-response.fixture.json b/src/connect/fixtures/external-auth-complete-response.fixture.json similarity index 100% rename from src/workos-connect/fixtures/external-auth-complete-response.fixture.json rename to src/connect/fixtures/external-auth-complete-response.fixture.json diff --git a/src/workos-connect/fixtures/list-connect-application.fixture.json b/src/connect/fixtures/list-connect-application.fixture.json similarity index 84% rename from src/workos-connect/fixtures/list-connect-application.fixture.json rename to src/connect/fixtures/list-connect-application.fixture.json index 53bec71d3..52900bd8a 100644 --- a/src/workos-connect/fixtures/list-connect-application.fixture.json +++ b/src/connect/fixtures/list-connect-application.fixture.json @@ -6,7 +6,11 @@ "client_id": "client_01HXYZ123456789ABCDEFGHIJ", "description": "An application for managing user access", "name": "My Application", - "scopes": ["openid", "profile", "email"], + "scopes": [ + "openid", + "profile", + "email" + ], "created_at": "2026-01-15T12:00:00.000Z", "updated_at": "2026-01-15T12:00:00.000Z" } diff --git a/src/workos-connect/fixtures/new-connect-application-secret.fixture.json b/src/connect/fixtures/new-connect-application-secret.fixture.json similarity index 100% rename from src/workos-connect/fixtures/new-connect-application-secret.fixture.json rename to src/connect/fixtures/new-connect-application-secret.fixture.json diff --git a/src/connect/fixtures/redirect-uri-input.fixture.json b/src/connect/fixtures/redirect-uri-input.fixture.json new file mode 100644 index 000000000..083dd6a8f --- /dev/null +++ b/src/connect/fixtures/redirect-uri-input.fixture.json @@ -0,0 +1,4 @@ +{ + "uri": "https://example.com/callback", + "default": true +} diff --git a/src/workos-connect/fixtures/update-oauth-application.fixture.json b/src/connect/fixtures/update-oauth-application.fixture.json similarity index 76% rename from src/workos-connect/fixtures/update-oauth-application.fixture.json rename to src/connect/fixtures/update-oauth-application.fixture.json index 160cacad0..24005a391 100644 --- a/src/workos-connect/fixtures/update-oauth-application.fixture.json +++ b/src/connect/fixtures/update-oauth-application.fixture.json @@ -1,7 +1,11 @@ { "name": "My Application", "description": "An application for managing user access", - "scopes": ["openid", "profile", "email"], + "scopes": [ + "openid", + "profile", + "email" + ], "redirect_uris": [ { "uri": "https://example.com/callback", diff --git a/src/workos-connect/fixtures/user-consent-option-choice.fixture.json b/src/connect/fixtures/user-consent-option-choice.fixture.json similarity index 100% rename from src/workos-connect/fixtures/user-consent-option-choice.fixture.json rename to src/connect/fixtures/user-consent-option-choice.fixture.json diff --git a/src/workos-connect/fixtures/user-consent-option.fixture.json b/src/connect/fixtures/user-consent-option.fixture.json similarity index 100% rename from src/workos-connect/fixtures/user-consent-option.fixture.json rename to src/connect/fixtures/user-consent-option.fixture.json diff --git a/src/workos-connect/fixtures/user-management-login-request.fixture.json b/src/connect/fixtures/user-management-login-request.fixture.json similarity index 100% rename from src/workos-connect/fixtures/user-management-login-request.fixture.json rename to src/connect/fixtures/user-management-login-request.fixture.json diff --git a/src/workos-connect/fixtures/user-object.fixture.json b/src/connect/fixtures/user-object.fixture.json similarity index 100% rename from src/workos-connect/fixtures/user-object.fixture.json rename to src/connect/fixtures/user-object.fixture.json diff --git a/src/workos-connect/interfaces/application-credentials-list-item.interface.ts b/src/connect/interfaces/application-credentials-list-item.interface.ts similarity index 100% rename from src/workos-connect/interfaces/application-credentials-list-item.interface.ts rename to src/connect/interfaces/application-credentials-list-item.interface.ts diff --git a/src/connect/interfaces/applications-order.interface.ts b/src/connect/interfaces/applications-order.interface.ts new file mode 100644 index 000000000..af8e38d5f --- /dev/null +++ b/src/connect/interfaces/applications-order.interface.ts @@ -0,0 +1,3 @@ +// This file is auto-generated by oagen. Do not edit. + +export type ApplicationsOrder = 'normal' | 'desc' | 'asc'; diff --git a/src/workos-connect/interfaces/connect-application.interface.ts b/src/connect/interfaces/connect-application.interface.ts similarity index 100% rename from src/workos-connect/interfaces/connect-application.interface.ts rename to src/connect/interfaces/connect-application.interface.ts diff --git a/src/workos-connect/interfaces/create-application-secret.interface.ts b/src/connect/interfaces/create-application-secret.interface.ts similarity index 100% rename from src/workos-connect/interfaces/create-application-secret.interface.ts rename to src/connect/interfaces/create-application-secret.interface.ts diff --git a/src/workos-connect/interfaces/create-m2m-application.interface.ts b/src/connect/interfaces/create-m2m-application.interface.ts similarity index 100% rename from src/workos-connect/interfaces/create-m2m-application.interface.ts rename to src/connect/interfaces/create-m2m-application.interface.ts diff --git a/src/workos-connect/interfaces/create-oauth-application.interface.ts b/src/connect/interfaces/create-oauth-application.interface.ts similarity index 85% rename from src/workos-connect/interfaces/create-oauth-application.interface.ts rename to src/connect/interfaces/create-oauth-application.interface.ts index b50d788ea..a058feb91 100644 --- a/src/workos-connect/interfaces/create-oauth-application.interface.ts +++ b/src/connect/interfaces/create-oauth-application.interface.ts @@ -1,9 +1,9 @@ // This file is auto-generated by oagen. Do not edit. import type { - RedirectUri, - RedirectUriResponse, -} from './redirect-uri.interface'; + RedirectUriInput, + RedirectUriInputResponse, +} from './redirect-uri-input.interface'; export interface CreateOAuthApplication { /** The name of the application. */ @@ -15,7 +15,7 @@ export interface CreateOAuthApplication { /** The OAuth scopes granted to the application. */ scopes?: string[] | null; /** Redirect URIs for the application. */ - redirectUris?: RedirectUri[] | null; + redirectUris?: RedirectUriInput[] | null; /** Whether the application uses PKCE (Proof Key for Code Exchange). */ usesPkce?: boolean | null; /** Whether this is a first-party application. Third-party applications require an organization_id. */ @@ -29,7 +29,7 @@ export interface CreateOAuthApplicationResponse { application_type: 'oauth'; description?: string | null; scopes?: string[] | null; - redirect_uris?: RedirectUriResponse[] | null; + redirect_uris?: RedirectUriInputResponse[] | null; uses_pkce?: boolean | null; is_first_party: boolean; organization_id?: string | null; diff --git a/src/workos-connect/interfaces/external-auth-complete-response.interface.ts b/src/connect/interfaces/external-auth-complete-response.interface.ts similarity index 100% rename from src/workos-connect/interfaces/external-auth-complete-response.interface.ts rename to src/connect/interfaces/external-auth-complete-response.interface.ts diff --git a/src/workos-connect/interfaces/index.ts b/src/connect/interfaces/index.ts similarity index 88% rename from src/workos-connect/interfaces/index.ts rename to src/connect/interfaces/index.ts index d70f57db7..5593eba76 100644 --- a/src/workos-connect/interfaces/index.ts +++ b/src/connect/interfaces/index.ts @@ -1,13 +1,14 @@ // This file is auto-generated by oagen. Do not edit. export * from './application-credentials-list-item.interface'; +export * from './applications-order.interface'; export * from './connect-application.interface'; export * from './create-application-secret.interface'; export * from './create-m2m-application.interface'; export * from './create-oauth-application.interface'; export * from './external-auth-complete-response.interface'; export * from './new-connect-application-secret.interface'; -export * from './redirect-uri.interface'; +export * from './redirect-uri-input.interface'; export * from './update-oauth-application.interface'; export * from './user-consent-option-choice.interface'; export * from './user-consent-option.interface'; diff --git a/src/workos-connect/interfaces/new-connect-application-secret.interface.ts b/src/connect/interfaces/new-connect-application-secret.interface.ts similarity index 100% rename from src/workos-connect/interfaces/new-connect-application-secret.interface.ts rename to src/connect/interfaces/new-connect-application-secret.interface.ts diff --git a/src/connect/interfaces/redirect-uri-input.interface.ts b/src/connect/interfaces/redirect-uri-input.interface.ts new file mode 100644 index 000000000..e6e57595b --- /dev/null +++ b/src/connect/interfaces/redirect-uri-input.interface.ts @@ -0,0 +1,13 @@ +// This file is auto-generated by oagen. Do not edit. + +export interface RedirectUriInput { + /** The redirect URI. */ + uri: string; + /** Whether this is the default redirect URI. */ + default?: boolean | null; +} + +export interface RedirectUriInputResponse { + uri: string; + default?: boolean | null; +} diff --git a/src/workos-connect/interfaces/update-oauth-application.interface.ts b/src/connect/interfaces/update-oauth-application.interface.ts similarity index 74% rename from src/workos-connect/interfaces/update-oauth-application.interface.ts rename to src/connect/interfaces/update-oauth-application.interface.ts index 9fd5c8090..041828cab 100644 --- a/src/workos-connect/interfaces/update-oauth-application.interface.ts +++ b/src/connect/interfaces/update-oauth-application.interface.ts @@ -1,9 +1,9 @@ // This file is auto-generated by oagen. Do not edit. import type { - RedirectUri, - RedirectUriResponse, -} from './redirect-uri.interface'; + RedirectUriInput, + RedirectUriInputResponse, +} from './redirect-uri-input.interface'; export interface UpdateOAuthApplication { /** The name of the application. */ @@ -13,12 +13,12 @@ export interface UpdateOAuthApplication { /** The OAuth scopes granted to the application. */ scopes?: string[] | null; /** Updated redirect URIs for the application. OAuth applications only. */ - redirectUris?: RedirectUri[] | null; + redirectUris?: RedirectUriInput[] | null; } export interface UpdateOAuthApplicationResponse { name?: string; description?: string | null; scopes?: string[] | null; - redirect_uris?: RedirectUriResponse[] | null; + redirect_uris?: RedirectUriInputResponse[] | null; } diff --git a/src/workos-connect/interfaces/user-consent-option-choice.interface.ts b/src/connect/interfaces/user-consent-option-choice.interface.ts similarity index 100% rename from src/workos-connect/interfaces/user-consent-option-choice.interface.ts rename to src/connect/interfaces/user-consent-option-choice.interface.ts diff --git a/src/workos-connect/interfaces/user-consent-option.interface.ts b/src/connect/interfaces/user-consent-option.interface.ts similarity index 100% rename from src/workos-connect/interfaces/user-consent-option.interface.ts rename to src/connect/interfaces/user-consent-option.interface.ts diff --git a/src/workos-connect/interfaces/user-management-login-request.interface.ts b/src/connect/interfaces/user-management-login-request.interface.ts similarity index 100% rename from src/workos-connect/interfaces/user-management-login-request.interface.ts rename to src/connect/interfaces/user-management-login-request.interface.ts diff --git a/src/workos-connect/interfaces/user-object.interface.ts b/src/connect/interfaces/user-object.interface.ts similarity index 100% rename from src/workos-connect/interfaces/user-object.interface.ts rename to src/connect/interfaces/user-object.interface.ts diff --git a/src/connect/serializers.spec.ts b/src/connect/serializers.spec.ts new file mode 100644 index 000000000..e10017395 --- /dev/null +++ b/src/connect/serializers.spec.ts @@ -0,0 +1,146 @@ +// This file is auto-generated by oagen. Do not edit. + +import { deserializeUserObject, serializeUserObject } from './serializers/user-object.serializer'; +import { deserializeUserConsentOption, serializeUserConsentOption } from './serializers/user-consent-option.serializer'; +import { deserializeUserManagementLoginRequest, serializeUserManagementLoginRequest } from './serializers/user-management-login-request.serializer'; +import { deserializeRedirectUriInput, serializeRedirectUriInput } from './serializers/redirect-uri-input.serializer'; +import { deserializeCreateOAuthApplication, serializeCreateOAuthApplication } from './serializers/create-oauth-application.serializer'; +import { deserializeCreateM2MApplication, serializeCreateM2MApplication } from './serializers/create-m2m-application.serializer'; +import { deserializeUpdateOAuthApplication, serializeUpdateOAuthApplication } from './serializers/update-oauth-application.serializer'; +import { deserializeExternalAuthCompleteResponse, serializeExternalAuthCompleteResponse } from './serializers/external-auth-complete-response.serializer'; +import { deserializeConnectApplication, serializeConnectApplication } from './serializers/connect-application.serializer'; +import { deserializeNewConnectApplicationSecret, serializeNewConnectApplicationSecret } from './serializers/new-connect-application-secret.serializer'; +import { deserializeUserConsentOptionChoice, serializeUserConsentOptionChoice } from './serializers/user-consent-option-choice.serializer'; +import { deserializeApplicationCredentialsListItem, serializeApplicationCredentialsListItem } from './serializers/application-credentials-list-item.serializer'; +import type { UserObjectResponse } from './interfaces/user-object.interface'; +import type { UserConsentOptionResponse } from './interfaces/user-consent-option.interface'; +import type { UserManagementLoginRequestResponse } from './interfaces/user-management-login-request.interface'; +import type { RedirectUriInputResponse } from './interfaces/redirect-uri-input.interface'; +import type { CreateOAuthApplicationResponse } from './interfaces/create-oauth-application.interface'; +import type { CreateM2MApplicationResponse } from './interfaces/create-m2m-application.interface'; +import type { UpdateOAuthApplicationResponse } from './interfaces/update-oauth-application.interface'; +import type { ExternalAuthCompleteResponseWire } from './interfaces/external-auth-complete-response.interface'; +import type { ConnectApplicationResponse } from './interfaces/connect-application.interface'; +import type { NewConnectApplicationSecretResponse } from './interfaces/new-connect-application-secret.interface'; +import type { UserConsentOptionChoiceResponse } from './interfaces/user-consent-option-choice.interface'; +import type { ApplicationCredentialsListItemResponse } from './interfaces/application-credentials-list-item.interface'; +import userObjectFixture from './fixtures/user-object.fixture.json'; +import userConsentOptionFixture from './fixtures/user-consent-option.fixture.json'; +import userManagementLoginRequestFixture from './fixtures/user-management-login-request.fixture.json'; +import redirectUriInputFixture from './fixtures/redirect-uri-input.fixture.json'; +import createOAuthApplicationFixture from './fixtures/create-oauth-application.fixture.json'; +import createM2MApplicationFixture from './fixtures/create-m2m-application.fixture.json'; +import updateOAuthApplicationFixture from './fixtures/update-oauth-application.fixture.json'; +import externalAuthCompleteResponseFixture from './fixtures/external-auth-complete-response.fixture.json'; +import connectApplicationFixture from './fixtures/connect-application.fixture.json'; +import newConnectApplicationSecretFixture from './fixtures/new-connect-application-secret.fixture.json'; +import userConsentOptionChoiceFixture from './fixtures/user-consent-option-choice.fixture.json'; +import applicationCredentialsListItemFixture from './fixtures/application-credentials-list-item.fixture.json'; + +describe('UserObjectSerializer', () => { + it('round-trips through serialize/deserialize', () => { + const fixture = userObjectFixture as UserObjectResponse; + const deserialized = deserializeUserObject(fixture); + const reserialized = serializeUserObject(deserialized); + expect(reserialized).toEqual(expect.objectContaining(fixture)); + }); +}); + +describe('UserConsentOptionSerializer', () => { + it('round-trips through serialize/deserialize', () => { + const fixture = userConsentOptionFixture as UserConsentOptionResponse; + const deserialized = deserializeUserConsentOption(fixture); + const reserialized = serializeUserConsentOption(deserialized); + expect(reserialized).toEqual(expect.objectContaining(fixture)); + }); +}); + +describe('UserManagementLoginRequestSerializer', () => { + it('round-trips through serialize/deserialize', () => { + const fixture = userManagementLoginRequestFixture as UserManagementLoginRequestResponse; + const deserialized = deserializeUserManagementLoginRequest(fixture); + const reserialized = serializeUserManagementLoginRequest(deserialized); + expect(reserialized).toEqual(expect.objectContaining(fixture)); + }); +}); + +describe('RedirectUriInputSerializer', () => { + it('round-trips through serialize/deserialize', () => { + const fixture = redirectUriInputFixture as RedirectUriInputResponse; + const deserialized = deserializeRedirectUriInput(fixture); + const reserialized = serializeRedirectUriInput(deserialized); + expect(reserialized).toEqual(expect.objectContaining(fixture)); + }); +}); + +describe('CreateOAuthApplicationSerializer', () => { + it('round-trips through serialize/deserialize', () => { + const fixture = createOAuthApplicationFixture as CreateOAuthApplicationResponse; + const deserialized = deserializeCreateOAuthApplication(fixture); + const reserialized = serializeCreateOAuthApplication(deserialized); + expect(reserialized).toEqual(expect.objectContaining(fixture)); + }); +}); + +describe('CreateM2MApplicationSerializer', () => { + it('round-trips through serialize/deserialize', () => { + const fixture = createM2MApplicationFixture as CreateM2MApplicationResponse; + const deserialized = deserializeCreateM2MApplication(fixture); + const reserialized = serializeCreateM2MApplication(deserialized); + expect(reserialized).toEqual(expect.objectContaining(fixture)); + }); +}); + +describe('UpdateOAuthApplicationSerializer', () => { + it('round-trips through serialize/deserialize', () => { + const fixture = updateOAuthApplicationFixture as UpdateOAuthApplicationResponse; + const deserialized = deserializeUpdateOAuthApplication(fixture); + const reserialized = serializeUpdateOAuthApplication(deserialized); + expect(reserialized).toEqual(expect.objectContaining(fixture)); + }); +}); + +describe('ExternalAuthCompleteResponseSerializer', () => { + it('round-trips through serialize/deserialize', () => { + const fixture = externalAuthCompleteResponseFixture as ExternalAuthCompleteResponseWire; + const deserialized = deserializeExternalAuthCompleteResponse(fixture); + const reserialized = serializeExternalAuthCompleteResponse(deserialized); + expect(reserialized).toEqual(expect.objectContaining(fixture)); + }); +}); + +describe('ConnectApplicationSerializer', () => { + it('round-trips through serialize/deserialize', () => { + const fixture = connectApplicationFixture as ConnectApplicationResponse; + const deserialized = deserializeConnectApplication(fixture); + const reserialized = serializeConnectApplication(deserialized); + expect(reserialized).toEqual(expect.objectContaining(fixture)); + }); +}); + +describe('NewConnectApplicationSecretSerializer', () => { + it('round-trips through serialize/deserialize', () => { + const fixture = newConnectApplicationSecretFixture as NewConnectApplicationSecretResponse; + const deserialized = deserializeNewConnectApplicationSecret(fixture); + const reserialized = serializeNewConnectApplicationSecret(deserialized); + expect(reserialized).toEqual(expect.objectContaining(fixture)); + }); +}); + +describe('UserConsentOptionChoiceSerializer', () => { + it('round-trips through serialize/deserialize', () => { + const fixture = userConsentOptionChoiceFixture as UserConsentOptionChoiceResponse; + const deserialized = deserializeUserConsentOptionChoice(fixture); + const reserialized = serializeUserConsentOptionChoice(deserialized); + expect(reserialized).toEqual(expect.objectContaining(fixture)); + }); +}); + +describe('ApplicationCredentialsListItemSerializer', () => { + it('round-trips through serialize/deserialize', () => { + const fixture = applicationCredentialsListItemFixture as ApplicationCredentialsListItemResponse; + const deserialized = deserializeApplicationCredentialsListItem(fixture); + const reserialized = serializeApplicationCredentialsListItem(deserialized); + expect(reserialized).toEqual(expect.objectContaining(fixture)); + }); +}); diff --git a/src/workos-connect/serializers/application-credentials-list-item.serializer.ts b/src/connect/serializers/application-credentials-list-item.serializer.ts similarity index 100% rename from src/workos-connect/serializers/application-credentials-list-item.serializer.ts rename to src/connect/serializers/application-credentials-list-item.serializer.ts diff --git a/src/workos-connect/serializers/connect-application.serializer.ts b/src/connect/serializers/connect-application.serializer.ts similarity index 100% rename from src/workos-connect/serializers/connect-application.serializer.ts rename to src/connect/serializers/connect-application.serializer.ts diff --git a/src/workos-connect/serializers/create-application-secret.serializer.ts b/src/connect/serializers/create-application-secret.serializer.ts similarity index 100% rename from src/workos-connect/serializers/create-application-secret.serializer.ts rename to src/connect/serializers/create-application-secret.serializer.ts diff --git a/src/workos-connect/serializers/create-m2m-application.serializer.ts b/src/connect/serializers/create-m2m-application.serializer.ts similarity index 100% rename from src/workos-connect/serializers/create-m2m-application.serializer.ts rename to src/connect/serializers/create-m2m-application.serializer.ts diff --git a/src/workos-connect/serializers/create-oauth-application.serializer.ts b/src/connect/serializers/create-oauth-application.serializer.ts similarity index 80% rename from src/workos-connect/serializers/create-oauth-application.serializer.ts rename to src/connect/serializers/create-oauth-application.serializer.ts index 4896feb6a..8accc8a30 100644 --- a/src/workos-connect/serializers/create-oauth-application.serializer.ts +++ b/src/connect/serializers/create-oauth-application.serializer.ts @@ -4,10 +4,8 @@ import type { CreateOAuthApplication, CreateOAuthApplicationResponse, } from '../interfaces/create-oauth-application.interface'; -import { - deserializeRedirectUri, - serializeRedirectUri, -} from './redirect-uri.serializer'; +import { deserializeRedirectUriInput } from './redirect-uri-input.serializer'; +import { serializeRedirectUriInput } from './redirect-uri-input.serializer'; export const deserializeCreateOAuthApplication = ( response: CreateOAuthApplicationResponse, @@ -18,7 +16,7 @@ export const deserializeCreateOAuthApplication = ( scopes: response.scopes ?? null, redirectUris: response.redirect_uris != null - ? response.redirect_uris.map(deserializeRedirectUri) + ? response.redirect_uris.map(deserializeRedirectUriInput) : null, usesPkce: response.uses_pkce ?? null, isFirstParty: response.is_first_party, @@ -34,7 +32,7 @@ export const serializeCreateOAuthApplication = ( scopes: model.scopes ?? null, redirect_uris: model.redirectUris != null - ? model.redirectUris.map(serializeRedirectUri) + ? model.redirectUris.map(serializeRedirectUriInput) : null, uses_pkce: model.usesPkce ?? null, is_first_party: model.isFirstParty, diff --git a/src/workos-connect/serializers/external-auth-complete-response.serializer.ts b/src/connect/serializers/external-auth-complete-response.serializer.ts similarity index 100% rename from src/workos-connect/serializers/external-auth-complete-response.serializer.ts rename to src/connect/serializers/external-auth-complete-response.serializer.ts diff --git a/src/workos-connect/serializers/new-connect-application-secret.serializer.ts b/src/connect/serializers/new-connect-application-secret.serializer.ts similarity index 100% rename from src/workos-connect/serializers/new-connect-application-secret.serializer.ts rename to src/connect/serializers/new-connect-application-secret.serializer.ts diff --git a/src/connect/serializers/redirect-uri-input.serializer.ts b/src/connect/serializers/redirect-uri-input.serializer.ts new file mode 100644 index 000000000..1135cb65b --- /dev/null +++ b/src/connect/serializers/redirect-uri-input.serializer.ts @@ -0,0 +1,20 @@ +// This file is auto-generated by oagen. Do not edit. + +import type { + RedirectUriInput, + RedirectUriInputResponse, +} from '../interfaces/redirect-uri-input.interface'; + +export const deserializeRedirectUriInput = ( + response: RedirectUriInputResponse, +): RedirectUriInput => ({ + uri: response.uri, + default: response.default ?? null, +}); + +export const serializeRedirectUriInput = ( + model: RedirectUriInput, +): RedirectUriInputResponse => ({ + uri: model.uri, + default: model.default ?? null, +}); diff --git a/src/workos-connect/serializers/update-oauth-application.serializer.ts b/src/connect/serializers/update-oauth-application.serializer.ts similarity index 73% rename from src/workos-connect/serializers/update-oauth-application.serializer.ts rename to src/connect/serializers/update-oauth-application.serializer.ts index 9fa4e0cd2..792ff0756 100644 --- a/src/workos-connect/serializers/update-oauth-application.serializer.ts +++ b/src/connect/serializers/update-oauth-application.serializer.ts @@ -4,10 +4,8 @@ import type { UpdateOAuthApplication, UpdateOAuthApplicationResponse, } from '../interfaces/update-oauth-application.interface'; -import { - deserializeRedirectUri, - serializeRedirectUri, -} from './redirect-uri.serializer'; +import { deserializeRedirectUriInput } from './redirect-uri-input.serializer'; +import { serializeRedirectUriInput } from './redirect-uri-input.serializer'; export const deserializeUpdateOAuthApplication = ( response: UpdateOAuthApplicationResponse, @@ -17,7 +15,7 @@ export const deserializeUpdateOAuthApplication = ( scopes: response.scopes ?? null, redirectUris: response.redirect_uris != null - ? response.redirect_uris.map(deserializeRedirectUri) + ? response.redirect_uris.map(deserializeRedirectUriInput) : null, }); @@ -29,6 +27,6 @@ export const serializeUpdateOAuthApplication = ( scopes: model.scopes ?? null, redirect_uris: model.redirectUris != null - ? model.redirectUris.map(serializeRedirectUri) + ? model.redirectUris.map(serializeRedirectUriInput) : null, }); diff --git a/src/workos-connect/serializers/user-consent-option-choice.serializer.ts b/src/connect/serializers/user-consent-option-choice.serializer.ts similarity index 100% rename from src/workos-connect/serializers/user-consent-option-choice.serializer.ts rename to src/connect/serializers/user-consent-option-choice.serializer.ts diff --git a/src/workos-connect/serializers/user-consent-option.serializer.ts b/src/connect/serializers/user-consent-option.serializer.ts similarity index 100% rename from src/workos-connect/serializers/user-consent-option.serializer.ts rename to src/connect/serializers/user-consent-option.serializer.ts diff --git a/src/workos-connect/serializers/user-management-login-request.serializer.ts b/src/connect/serializers/user-management-login-request.serializer.ts similarity index 100% rename from src/workos-connect/serializers/user-management-login-request.serializer.ts rename to src/connect/serializers/user-management-login-request.serializer.ts diff --git a/src/workos-connect/serializers/user-object.serializer.ts b/src/connect/serializers/user-object.serializer.ts similarity index 100% rename from src/workos-connect/serializers/user-object.serializer.ts rename to src/connect/serializers/user-object.serializer.ts diff --git a/src/workos-connect/application-client-secrets.spec.ts b/src/workos-connect/application-client-secrets.spec.ts deleted file mode 100644 index 703020f8f..000000000 --- a/src/workos-connect/application-client-secrets.spec.ts +++ /dev/null @@ -1,93 +0,0 @@ -// This file is auto-generated by oagen. Do not edit. - -import fetch from 'jest-fetch-mock'; -import { - fetchOnce, - fetchURL, - fetchMethod, - fetchBody, - testUnauthorized, -} from '../common/utils/test-utils'; -import { WorkOS } from '../workos'; - -import applicationCredentialsListItemFixture from './fixtures/application-credentials-list-item.fixture.json'; -import newConnectApplicationSecretFixture from './fixtures/new-connect-application-secret.fixture.json'; - -const workos = new WorkOS('sk_test_Sz3IQjepeSWaI4cMS4ms4sMuU'); - -describe('ApplicationClientSecrets', () => { - beforeEach(() => fetch.resetMocks()); - - describe('list', () => { - it('returns the expected result', async () => { - fetchOnce(applicationCredentialsListItemFixture); - - const result = await workos.applicationClientSecrets.list('test_id'); - - expect(fetchMethod()).toBe('GET'); - expect(new URL(String(fetchURL())).pathname).toBe( - '/connect/applications/test_id/client_secrets', - ); - expect(Array.isArray(result)).toBe(true); - expect(result).toHaveLength(1); - expect(result[0].object).toBe('connect_application_secret'); - expect(result[0].id).toBe('secret_01J9Q2Z3X4Y5W6V7U8T9S0R1Q'); - expect(result[0].secretHint).toBe('abc123'); - expect(result[0].lastUsedAt).toBe(null); - expect(result[0].createdAt).toBe('2026-01-15T12:00:00.000Z'); - expect(result[0].updatedAt).toBe('2026-01-15T12:00:00.000Z'); - }); - - testUnauthorized(() => workos.applicationClientSecrets.list('test_id')); - }); - - describe('create', () => { - it('sends the correct request and returns result', async () => { - fetchOnce(newConnectApplicationSecretFixture); - - const result = await workos.applicationClientSecrets.create( - 'test_id', - {}, - ); - - expect(fetchMethod()).toBe('POST'); - expect(new URL(String(fetchURL())).pathname).toBe( - '/connect/applications/test_id/client_secrets', - ); - expect(fetchBody()).toBeDefined(); - expect(result.object).toBe('connect_application_secret'); - expect(result.id).toBe('secret_01J9Q2Z3X4Y5W6V7U8T9S0R1Q'); - expect(result.secretHint).toBe('abc123'); - expect(result.lastUsedAt).toBe(null); - expect(result.createdAt).toBe('2026-01-15T12:00:00.000Z'); - expect(result.updatedAt).toBe('2026-01-15T12:00:00.000Z'); - expect(result.secret).toBe( - 'abc123def456ghi789jkl012mno345pqr678stu901vwx234yz', - ); - }); - - testUnauthorized(() => - workos.applicationClientSecrets.create('test_id', {}), - ); - - it('throws UnprocessableEntityException on 422', async () => { - fetchOnce('', { status: 422 }); - await expect( - workos.applicationClientSecrets.create('test_id', {}), - ).rejects.toThrow(); - }); - }); - - describe('delete', () => { - it('sends a DELETE request', async () => { - fetchOnce({}, { status: 204 }); - - await workos.applicationClientSecrets.delete('test_id'); - - expect(fetchMethod()).toBe('DELETE'); - expect(new URL(String(fetchURL())).pathname).toBe( - '/connect/client_secrets/test_id', - ); - }); - }); -}); diff --git a/src/workos-connect/application-client-secrets.ts b/src/workos-connect/application-client-secrets.ts deleted file mode 100644 index 1c6bd94b1..000000000 --- a/src/workos-connect/application-client-secrets.ts +++ /dev/null @@ -1,72 +0,0 @@ -// This file is auto-generated by oagen. Do not edit. - -import type { WorkOS } from '../workos'; -import type { - ApplicationCredentialsListItem, - ApplicationCredentialsListItemResponse, -} from './interfaces/application-credentials-list-item.interface'; -import type { - NewConnectApplicationSecret, - NewConnectApplicationSecretResponse, -} from './interfaces/new-connect-application-secret.interface'; -import type { CreateApplicationSecret } from './interfaces/create-application-secret.interface'; -import { deserializeApplicationCredentialsListItem } from './serializers/application-credentials-list-item.serializer'; -import { deserializeNewConnectApplicationSecret } from './serializers/new-connect-application-secret.serializer'; -import { serializeCreateApplicationSecret } from './serializers/create-application-secret.serializer'; - -export class ApplicationClientSecrets { - constructor(private readonly workos: WorkOS) {} - - /** - * List Client Secrets for a Connect Application - * - * List all client secrets associated with a Connect Application. - * @param id - The application ID or client ID of the Connect Application. - * @example "conn_app_01HXYZ123456789ABCDEFGHIJ" - * @returns {Promise} - * @throws {NotFoundException} 404 - */ - async list(id: string): Promise { - const { data } = - await this.workos.get( - `/connect/applications/${id}/client_secrets`, - ); - return data.map(deserializeApplicationCredentialsListItem); - } - - /** - * Create a new client secret for a Connect Application - * - * Create new secrets for a Connect Application. - * @param id - The application ID or client ID of the Connect Application. - * @example "conn_app_01HXYZ123456789ABCDEFGHIJ" - * @param payload - The request body. - * @returns {NewConnectApplicationSecret} - * @throws {NotFoundException} 404 - * @throws {UnprocessableEntityException} 422 - */ - async create( - id: string, - payload: CreateApplicationSecret, - ): Promise { - const { data } = - await this.workos.post( - `/connect/applications/${id}/client_secrets`, - serializeCreateApplicationSecret(payload), - ); - return deserializeNewConnectApplicationSecret(data); - } - - /** - * Delete a Client Secret - * - * Delete (revoke) an existing client secret. - * @param id - The unique ID of the client secret. - * @example "secret_01J9Q2Z3X4Y5W6V7U8T9S0R1Q" - * @returns {void} - * @throws {NotFoundException} 404 - */ - async delete(id: string): Promise { - await this.workos.delete(`/connect/client_secrets/${id}`); - } -} diff --git a/src/workos-connect/applications.spec.ts b/src/workos-connect/applications.spec.ts deleted file mode 100644 index 9ca0b6964..000000000 --- a/src/workos-connect/applications.spec.ts +++ /dev/null @@ -1,157 +0,0 @@ -// This file is auto-generated by oagen. Do not edit. - -import fetch from 'jest-fetch-mock'; -import { - fetchOnce, - fetchURL, - fetchMethod, - fetchSearchParams, - fetchBody, - testEmptyResults, - testPaginationParams, - testUnauthorized, -} from '../common/utils/test-utils'; -import { WorkOS } from '../workos'; - -import listConnectApplicationFixture from './fixtures/list-connect-application.fixture.json'; -import connectApplicationFixture from './fixtures/connect-application.fixture.json'; - -const workos = new WorkOS('sk_test_Sz3IQjepeSWaI4cMS4ms4sMuU'); - -function expectConnectApplication(result: any) { - expect(result.object).toBe('connect_application'); - expect(result.id).toBe('conn_app_01HXYZ123456789ABCDEFGHIJ'); - expect(result.clientId).toBe('client_01HXYZ123456789ABCDEFGHIJ'); - expect(result.description).toBe('An application for managing user access'); - expect(result.name).toBe('My Application'); - expect(result.scopes).toEqual(['openid', 'profile', 'email']); - expect(result.createdAt).toBe('2026-01-15T12:00:00.000Z'); - expect(result.updatedAt).toBe('2026-01-15T12:00:00.000Z'); -} - -describe('Applications', () => { - beforeEach(() => fetch.resetMocks()); - - describe('list', () => { - it('returns paginated results', async () => { - fetchOnce(listConnectApplicationFixture); - - const { data, listMetadata } = await workos.applications.list(); - - expect(fetchMethod()).toBe('GET'); - expect(new URL(String(fetchURL())).pathname).toBe( - '/connect/applications', - ); - expect(fetchSearchParams()).toHaveProperty('order'); - expect(Array.isArray(data)).toBe(true); - expect(listMetadata).toBeDefined(); - expect(data.length).toBeGreaterThan(0); - expectConnectApplication(data[0]); - }); - - testEmptyResults(() => workos.applications.list()); - - testPaginationParams( - (opts) => workos.applications.list(opts), - listConnectApplicationFixture, - ); - - testUnauthorized(() => workos.applications.list()); - }); - - describe('create', () => { - it('sends the correct request and returns result', async () => { - fetchOnce(connectApplicationFixture); - - const result = await workos.applications.create({ - applicationType: 'oauth', - name: 'My Application', - isFirstParty: true, - }); - - expect(fetchMethod()).toBe('POST'); - expect(new URL(String(fetchURL())).pathname).toBe( - '/connect/applications', - ); - expect(fetchBody()).toBeDefined(); - expectConnectApplication(result); - }); - - testUnauthorized(() => workos.applications.create({ - applicationType: 'oauth', - name: 'My Application', - isFirstParty: true, - })); - - it('throws UnprocessableEntityException on 422', async () => { - fetchOnce('', { status: 422 }); - await expect(workos.applications.create({ - applicationType: 'oauth', - name: 'My Application', - isFirstParty: true, - })).rejects.toThrow(); - }); - - it('throws on unknown applicationType', async () => { - await expect( - workos.applications.create({ applicationType: 'bogus' } as any), - ).rejects.toThrow(/Unknown applicationType: bogus/); - }); - }); - - describe('find', () => { - it('returns the expected result', async () => { - fetchOnce(connectApplicationFixture); - - const result = await workos.applications.find('test_id'); - - expect(fetchMethod()).toBe('GET'); - expect(new URL(String(fetchURL())).pathname).toBe( - '/connect/applications/test_id', - ); - expectConnectApplication(result); - }); - - testUnauthorized(() => workos.applications.find('test_id')); - - it('throws NotFoundException on 404', async () => { - fetchOnce('', { status: 404 }); - await expect(workos.applications.find('test_id')).rejects.toThrow(); - }); - }); - - describe('update', () => { - it('sends the correct request and returns result', async () => { - fetchOnce(connectApplicationFixture); - - const result = await workos.applications.update('test_id', {}); - - expect(fetchMethod()).toBe('PUT'); - expect(new URL(String(fetchURL())).pathname).toBe( - '/connect/applications/test_id', - ); - expect(fetchBody()).toBeDefined(); - expectConnectApplication(result); - }); - - testUnauthorized(() => workos.applications.update('test_id', {})); - - it('throws UnprocessableEntityException on 422', async () => { - fetchOnce('', { status: 422 }); - await expect(workos.applications.update('test_id', {})).rejects.toThrow(); - }); - }); - - describe('delete', () => { - it('sends a DELETE request', async () => { - fetchOnce({}, { status: 204 }); - - await workos.applications.delete('test_id'); - - expect(fetchMethod()).toBe('DELETE'); - expect(new URL(String(fetchURL())).pathname).toBe( - '/connect/applications/test_id', - ); - }); - }); -}); diff --git a/src/workos-connect/applications.ts b/src/workos-connect/applications.ts deleted file mode 100644 index 086372f72..000000000 --- a/src/workos-connect/applications.ts +++ /dev/null @@ -1,132 +0,0 @@ -// This file is auto-generated by oagen. Do not edit. - -import type { WorkOS } from '../workos'; -import type { PaginationOptions } from '../common/interfaces/pagination-options.interface'; -import type { AutoPaginatable } from '../common/utils/pagination'; -import { createPaginatedList } from '../common/utils/fetch-and-deserialize'; -import type { - ConnectApplication, - ConnectApplicationResponse, -} from './interfaces/connect-application.interface'; -import type { CreateOAuthApplication } from './interfaces/create-oauth-application.interface'; -import type { CreateM2MApplication } from './interfaces/create-m2m-application.interface'; -import type { UpdateOAuthApplication } from './interfaces/update-oauth-application.interface'; -import { deserializeConnectApplication } from './serializers/connect-application.serializer'; -import { serializeCreateOAuthApplication } from './serializers/create-oauth-application.serializer'; -import { serializeCreateM2MApplication } from './serializers/create-m2m-application.serializer'; -import { serializeUpdateOAuthApplication } from './serializers/update-oauth-application.serializer'; - -export interface ApplicationsListOptions extends PaginationOptions { - /** Filter Connect Applications by organization ID. */ - organizationId?: string; -} - -export class Applications { - constructor(private readonly workos: WorkOS) {} - - /** - * List Connect Applications - * - * List all Connect Applications in the current environment with optional filtering. - * @param options - Pagination and filter options. - * @returns {AutoPaginatable} - * @throws {UnprocessableEntityException} 422 - */ - async list( - options?: ApplicationsListOptions, - ): Promise> { - return createPaginatedList< - ConnectApplicationResponse, - ConnectApplication, - ApplicationsListOptions - >( - this.workos, - '/connect/applications', - deserializeConnectApplication, - options, - ); - } - - /** - * Create a Connect Application - * - * Create a new Connect Application. Supports both OAuth and Machine-to-Machine (M2M) application types. - * @param payload - The request body. - * @returns {ConnectApplication} - * @throws {NotFoundException} 404 - * @throws {UnprocessableEntityException} 422 - */ - async create( - payload: CreateOAuthApplication | CreateM2MApplication, - ): Promise { - const { data } = await this.workos.post( - '/connect/applications', - (() => { - switch (payload.applicationType) { - case 'oauth': - return serializeCreateOAuthApplication(payload); - case 'm2m': - return serializeCreateM2MApplication(payload); - default: { - const _unknown: never = payload; - throw new Error( - `Unknown applicationType: ${(_unknown as { applicationType?: unknown }).applicationType}`, - ); - } - } - })(), - ); - return deserializeConnectApplication(data); - } - - /** - * Get a Connect Application - * - * Retrieve details for a specific Connect Application by ID or client ID. - * @param id - The application ID or client ID of the Connect Application. - * @example "conn_app_01HXYZ123456789ABCDEFGHIJ" - * @returns {ConnectApplication} - * @throws {NotFoundException} 404 - */ - async find(id: string): Promise { - const { data } = await this.workos.get( - `/connect/applications/${id}`, - ); - return deserializeConnectApplication(data); - } - - /** - * Update a Connect Application - * - * Update an existing Connect Application. For OAuth applications, you can update redirect URIs. For all applications, you can update the name, description, and scopes. - * @param id - The application ID or client ID of the Connect Application. - * @example "conn_app_01HXYZ123456789ABCDEFGHIJ" - * @param payload - The request body. - * @returns {ConnectApplication} - * @throws {NotFoundException} 404 - * @throws {UnprocessableEntityException} 422 - */ - async update( - id: string, - payload: UpdateOAuthApplication, - ): Promise { - const { data } = await this.workos.put( - `/connect/applications/${id}`, - serializeUpdateOAuthApplication(payload), - ); - return deserializeConnectApplication(data); - } - - /** - * Delete a Connect Application - * - * Delete an existing Connect Application. - * @param id - The application ID or client ID of the Connect Application. - * @example "conn_app_01HXYZ123456789ABCDEFGHIJ" - * @returns {void} - * @throws {NotFoundException} 404 - */ - async delete(id: string): Promise { - await this.workos.delete(`/connect/applications/${id}`); - } -} diff --git a/src/workos-connect/fixtures/application-credentials-list-item.fixture.json b/src/workos-connect/fixtures/application-credentials-list-item.fixture.json deleted file mode 100644 index e42e400a8..000000000 --- a/src/workos-connect/fixtures/application-credentials-list-item.fixture.json +++ /dev/null @@ -1,10 +0,0 @@ -[ - { - "object": "connect_application_secret", - "id": "secret_01J9Q2Z3X4Y5W6V7U8T9S0R1Q", - "secret_hint": "abc123", - "last_used_at": null, - "created_at": "2026-01-15T12:00:00.000Z", - "updated_at": "2026-01-15T12:00:00.000Z" - } -] diff --git a/src/workos-connect/fixtures/redirect-uri.fixture.json b/src/workos-connect/fixtures/redirect-uri.fixture.json deleted file mode 100644 index dcd6ad8d6..000000000 --- a/src/workos-connect/fixtures/redirect-uri.fixture.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "object": "redirect_uri", - "id": "ruri_01EHZNVPK3SFK441A1RGBFSHRT", - "uri": "https://example.com/callback", - "default": true, - "created_at": "2026-01-15T12:00:00.000Z", - "updated_at": "2026-01-15T12:00:00.000Z" -} diff --git a/src/workos-connect/interfaces/redirect-uri.interface.ts b/src/workos-connect/interfaces/redirect-uri.interface.ts deleted file mode 100644 index d32ed8e25..000000000 --- a/src/workos-connect/interfaces/redirect-uri.interface.ts +++ /dev/null @@ -1,25 +0,0 @@ -// This file is auto-generated by oagen. Do not edit. - -export interface RedirectUri { - /** The object type. */ - object: 'redirect_uri'; - /** The ID of the redirect URI. */ - id: string; - /** The redirect URI. */ - uri: string; - /** Whether this is the default redirect URI. */ - default: boolean; - /** The timestamp when the redirect URI was created. */ - createdAt: string; - /** The timestamp when the redirect URI was last updated. */ - updatedAt: string; -} - -export interface RedirectUriResponse { - object: 'redirect_uri'; - id: string; - uri: string; - default: boolean; - created_at: string; - updated_at: string; -} diff --git a/src/workos-connect/serializers/redirect-uri.serializer.ts b/src/workos-connect/serializers/redirect-uri.serializer.ts deleted file mode 100644 index 99fa1b53b..000000000 --- a/src/workos-connect/serializers/redirect-uri.serializer.ts +++ /dev/null @@ -1,28 +0,0 @@ -// This file is auto-generated by oagen. Do not edit. - -import type { - RedirectUri, - RedirectUriResponse, -} from '../interfaces/redirect-uri.interface'; - -export const deserializeRedirectUri = ( - response: RedirectUriResponse, -): RedirectUri => ({ - object: response.object, - id: response.id, - uri: response.uri, - default: response.default, - createdAt: response.created_at, - updatedAt: response.updated_at, -}); - -export const serializeRedirectUri = ( - model: RedirectUri, -): RedirectUriResponse => ({ - object: model.object, - id: model.id, - uri: model.uri, - default: model.default, - created_at: model.createdAt, - updated_at: model.updatedAt, -}); diff --git a/src/workos-connect/work-os-connect.spec.ts b/src/workos-connect/work-os-connect.spec.ts deleted file mode 100644 index 0bcd863f2..000000000 --- a/src/workos-connect/work-os-connect.spec.ts +++ /dev/null @@ -1,51 +0,0 @@ -// This file is auto-generated by oagen. Do not edit. - -import fetch from 'jest-fetch-mock'; -import { - fetchOnce, - fetchURL, - fetchMethod, - fetchBody, - testUnauthorized, -} from '../common/utils/test-utils'; -import { WorkOS } from '../workos'; - -import externalAuthCompleteResponseFixture from './fixtures/external-auth-complete-response.fixture.json'; - -const workos = new WorkOS('sk_test_Sz3IQjepeSWaI4cMS4ms4sMuU'); - -describe('WorkOSConnect', () => { - beforeEach(() => fetch.resetMocks()); - - describe('completeLogin', () => { - it('sends the correct request and returns result', async () => { - fetchOnce(externalAuthCompleteResponseFixture); - - const result = await workos.workOsConnect.completeLogin({ - externalAuthId: 'external_auth_id_01234', - user: { id: '01234', email: 'test@example.com' }, - }); - - expect(fetchMethod()).toBe('POST'); - expect(new URL(String(fetchURL())).pathname).toBe( - '/authkit/oauth2/complete', - ); - expect(fetchBody()).toEqual( - expect.objectContaining({ - external_auth_id: 'external_auth_id_01234', - user: { id: '01234', email: 'test@example.com' }, - }), - ); - expect(result.redirectUri).toBe( - 'https://your-authkit-domain.workos.com/oauth/authorize/complete?state=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdGF0ZSI6InJhbmRvbV9zdGF0ZV9zdHJpbmciLCJpYXQiOjE3NDI2MDQ4NTN9.abc123def456ghi789', - ); - }); - - testUnauthorized(() => - workos.workOsConnect.completeLogin({ - externalAuthId: 'external_auth_id_01234', - user: { id: '01234', email: 'test@example.com' }, - }), - ); - }); -}); diff --git a/src/workos-connect/work-os-connect.ts b/src/workos-connect/work-os-connect.ts deleted file mode 100644 index 751f524e3..000000000 --- a/src/workos-connect/work-os-connect.ts +++ /dev/null @@ -1,43 +0,0 @@ -// This file is auto-generated by oagen. Do not edit. - -import type { WorkOS } from '../workos'; -import type { - ExternalAuthCompleteResponse, - ExternalAuthCompleteResponseWire, -} from './interfaces/external-auth-complete-response.interface'; -import type { UserManagementLoginRequest } from './interfaces/user-management-login-request.interface'; -import { deserializeExternalAuthCompleteResponse } from './serializers/external-auth-complete-response.serializer'; -import { serializeUserManagementLoginRequest } from './serializers/user-management-login-request.serializer'; - -export class WorkOSConnect { - constructor(private readonly workos: WorkOS) {} - - /** - * Complete external authentication - * - * Completes an external authentication flow and returns control to AuthKit. This endpoint is used with [Standalone Connect](https://workos.com/docs/authkit/connect/standalone) to bridge your existing authentication system with the Connect OAuth API infrastructure. - * - * After successfully authenticating a user in your application, calling this endpoint will: - * - * - Create or update the user in AuthKit, using the given `id` as its `external_id`. - * - Return a `redirect_uri` your application should redirect to in order for AuthKit to complete the flow - * - * Users are automatically created or updated based on the `id` and `email` provided. If a user with the same `id` exists, their information is updated. Otherwise, a new user is created. - * - * If you provide a new `id` with an `email` that already belongs to an existing user, the request will fail with an error as email addresses are unique to a user. - * @param payload - Object containing externalAuthId, user. - * @returns {ExternalAuthCompleteResponse} - * @throws {BadRequestException} 400 - * @throws {NotFoundException} 404 - * @throws {UnprocessableEntityException} 422 - */ - async completeLogin( - payload: UserManagementLoginRequest, - ): Promise { - const { data } = await this.workos.post( - '/authkit/oauth2/complete', - serializeUserManagementLoginRequest(payload), - ); - return deserializeExternalAuthCompleteResponse(data); - } -} diff --git a/src/workos.ts b/src/workos.ts index d24d82b78..8df7f8803 100644 --- a/src/workos.ts +++ b/src/workos.ts @@ -53,9 +53,7 @@ import { AdminPortal } from './admin-portal/admin-portal'; import { Permissions } from './permissions/permissions'; import { Radar } from './radar/radar'; import { WebhooksEndpoints } from './webhooks/webhooks-endpoints'; -import { WorkOSConnect } from './workos-connect/work-os-connect'; -import { Applications } from './workos-connect/applications'; -import { ApplicationClientSecrets } from './workos-connect/application-client-secrets'; +import { Connect } from './connect/connect'; const DEFAULT_HOSTNAME = 'api.workos.com'; @@ -523,9 +521,7 @@ export class WorkOS { readonly permissions = new Permissions(this); readonly radar = new Radar(this); readonly webhooksEndpoints = new WebhooksEndpoints(this); - readonly workOsConnect = new WorkOSConnect(this); - readonly applications = new Applications(this); - readonly applicationClientSecrets = new ApplicationClientSecrets(this); + readonly connect = new Connect(this); /** Production */ static readonly SERVER_PRODUCTION = 'https://api.workos.com'; /** Staging */ From a823cc3de0bb3dfbe9333c151b38cef9dfb5e478 Mon Sep 17 00:00:00 2001 From: "Garen J. Torikian" Date: Mon, 13 Apr 2026 19:02:18 -0400 Subject: [PATCH 12/17] adjust properties to the proper location --- src/workos.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/workos.ts b/src/workos.ts index 8df7f8803..0defb07ef 100644 --- a/src/workos.ts +++ b/src/workos.ts @@ -73,9 +73,11 @@ export class WorkOS { private readonly hasApiKey: boolean; readonly actions: Actions; + readonly adminPortal = new AdminPortal(this); readonly apiKeys = new ApiKeys(this); readonly auditLogs = new AuditLogs(this); readonly authorization = new Authorization(this); + readonly connect = new Connect(this); readonly directorySync = new DirectorySync(this); readonly events = new Events(this); readonly featureFlags = new FeatureFlags(this); @@ -84,12 +86,15 @@ export class WorkOS { readonly organizations = new Organizations(this); readonly organizationDomains = new OrganizationDomains(this); readonly passwordless = new Passwordless(this); + readonly permissions = new Permissions(this); readonly pipes = new Pipes(this); readonly portal = new Portal(this); + readonly radar = new Radar(this); readonly sso = new SSO(this); readonly userManagement: UserManagement; readonly vault = new Vault(this); readonly webhooks: Webhooks; + readonly webhooksEndpoints = new WebhooksEndpoints(this); readonly widgets = new Widgets(this); /** @@ -517,11 +522,6 @@ export class WorkOS { } } - readonly adminPortal = new AdminPortal(this); - readonly permissions = new Permissions(this); - readonly radar = new Radar(this); - readonly webhooksEndpoints = new WebhooksEndpoints(this); - readonly connect = new Connect(this); /** Production */ static readonly SERVER_PRODUCTION = 'https://api.workos.com'; /** Staging */ From 76e03ad11f4a7afb94deb801e1ad35974796f668 Mon Sep 17 00:00:00 2001 From: "Garen J. Torikian" Date: Mon, 13 Apr 2026 19:26:21 -0400 Subject: [PATCH 13/17] preserve filter options across paginated pages MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit AutoPaginatable invokes the paging callback with just the cursor (`{ limit, after }`) for each page. The original code passed that cursor alone to fetchAndDeserialize, which dropped user-supplied filters like `organizationId` after page 1 — page 2 would silently return every record in the environment instead of just the filtered subset. Spread `options` first so filters persist, then spread `params` so the cursor overrides `after`/`limit` as pages advance. Co-Authored-By: Claude Opus 4.6 (1M context) --- src/common/utils/fetch-and-deserialize.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/common/utils/fetch-and-deserialize.ts b/src/common/utils/fetch-and-deserialize.ts index 694ccc39e..bc0cc85e2 100644 --- a/src/common/utils/fetch-and-deserialize.ts +++ b/src/common/utils/fetch-and-deserialize.ts @@ -49,12 +49,17 @@ export async function createPaginatedList< deserializeFn, options, ), + // AutoPaginatable invokes this callback with just the pagination cursor + // (`{ limit, after }`) for each page. Spread the original `options` + // first so user-supplied filters (e.g. `organizationId`) persist across + // pages, then spread `params` so the cursor overrides `after`/`limit` + // as pages advance. (params) => fetchAndDeserialize( workos, endpoint, deserializeFn, - params, + { ...options, ...params }, ), options, ); From 00d3936054061dde13897f6124fdda6e72fae52e Mon Sep 17 00:00:00 2001 From: "Garen J. Torikian" Date: Mon, 13 Apr 2026 19:37:00 -0400 Subject: [PATCH 14/17] translate paginated list options to wire (snake_case) keys MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Paginated list methods with extension filter fields (e.g. `organizationId` on `ListApplicationsOptions`) were forwarding the user-facing camelCase keys straight through to the query string. The API expects snake_case, so the filter was silently dropped server-side — `workos.connect.listApplications({ organizationId: 'x' })` was returning every Connect Application in the environment instead of just the org's. Extend `createPaginatedList` with an optional serializer callback that converts options to the wire shape once, then reuses the wire form on both the initial fetch and every paging callback. The Connect list method passes a hand-written `serializeListApplicationsOptions` helper; the emitter will generate the equivalent for every future list endpoint with divergent field names. Co-Authored-By: Claude Opus 4.6 (1M context) --- src/common/utils/fetch-and-deserialize.ts | 20 ++++++++++++++------ src/connect/connect.spec.ts | 13 +++++++++++++ src/connect/connect.ts | 15 +++++++++++++++ 3 files changed, 42 insertions(+), 6 deletions(-) diff --git a/src/common/utils/fetch-and-deserialize.ts b/src/common/utils/fetch-and-deserialize.ts index bc0cc85e2..b05e87841 100644 --- a/src/common/utils/fetch-and-deserialize.ts +++ b/src/common/utils/fetch-and-deserialize.ts @@ -41,25 +41,33 @@ export async function createPaginatedList< endpoint: string, deserializeFn: (r: TResponse) => TModel, options?: TOptions, + serializeOptionsFn?: (opts: TOptions) => PaginationOptions, ): Promise> { + // When a serializer is provided, transform the user-facing camelCase + // options into the wire (snake_case) shape before making any HTTP call. + // Without this, extension filter fields like `organizationId` go out as + // camelCase query keys that the API silently ignores. Standard pagination + // fields (limit/before/after/order) share spelling so no serializer is + // needed for options that extend only PaginationOptions. + const wireOptions = + options && serializeOptionsFn ? serializeOptionsFn(options) : options; return new AutoPaginatable( await fetchAndDeserialize( workos, endpoint, deserializeFn, - options, + wireOptions, ), // AutoPaginatable invokes this callback with just the pagination cursor - // (`{ limit, after }`) for each page. Spread the original `options` - // first so user-supplied filters (e.g. `organizationId`) persist across - // pages, then spread `params` so the cursor overrides `after`/`limit` - // as pages advance. + // (`{ limit, after }`) for each page. Spread the wire-form options so + // filters persist across pages, then spread `params` so the cursor + // overrides `after`/`limit` as pages advance. (params) => fetchAndDeserialize( workos, endpoint, deserializeFn, - { ...options, ...params }, + { ...wireOptions, ...params }, ), options, ); diff --git a/src/connect/connect.spec.ts b/src/connect/connect.spec.ts index 704fa24cd..07b5039db 100644 --- a/src/connect/connect.spec.ts +++ b/src/connect/connect.spec.ts @@ -73,6 +73,19 @@ describe('Connect', () => { ); testUnauthorized(() => workos.connect.listApplications()); + + it('translates camelCase filter options to snake_case on the wire', async () => { + fetchOnce(listConnectApplicationFixture); + + await workos.connect.listApplications({ organizationId: 'org_01HXYZ' }); + + // The API expects snake_case query keys; without the serializer, + // `organizationId` would go out unchanged and the filter would be + // silently dropped by the server. + const params = fetchSearchParams(); + expect(params).toHaveProperty('organization_id', 'org_01HXYZ'); + expect(params).not.toHaveProperty('organizationId'); + }); }); describe('createApplication', () => { diff --git a/src/connect/connect.ts b/src/connect/connect.ts index 3f4f202e3..20a5c3bfa 100644 --- a/src/connect/connect.ts +++ b/src/connect/connect.ts @@ -40,6 +40,20 @@ export interface ListApplicationsOptions extends PaginationOptions { organizationId?: string; } +const serializeListApplicationsOptions = ( + options: ListApplicationsOptions, +): PaginationOptions => { + const wire: Record = { + limit: options.limit, + before: options.before, + after: options.after, + order: options.order, + }; + if (options.organizationId !== undefined) + wire.organization_id = options.organizationId; + return wire as PaginationOptions; +}; + export class Connect { constructor(private readonly workos: WorkOS) {} @@ -92,6 +106,7 @@ export class Connect { '/connect/applications', deserializeConnectApplication, options, + serializeListApplicationsOptions, ); } From e3247724a97fb5c75ba30845e665f6412d2ecc86 Mon Sep 17 00:00:00 2001 From: "Garen J. Torikian" Date: Mon, 13 Apr 2026 19:46:35 -0400 Subject: [PATCH 15/17] expose deprecation + connect types, flag listPermissions return shape Three reviewer-flagged fixes: - Portal and FGA had `@deprecated` on the class but TS deprecation-lint only fires on `new X()` call sites, not on `workos.portal` / `workos.fga` access. Added matching `/** @deprecated ... */` directives on the property declarations in workos.ts so IDEs surface the strikethrough at the access site. - Authorization.listPermissions is marked deprecated in favor of the new workos.permissions.list(), but the return types differ (PermissionList vs AutoPaginatable). Expanded the deprecation JSDoc to flag the shape change so callers migrating the method name aren't surprised that explicit PermissionList type annotations don't transfer. - ListApplicationsOptions was declared inline in connect.ts, so the per-service barrel never re-exported it and callers couldn't import the type from the package root. Moved it to its own interface file under src/connect/interfaces/ and added the barrel pointer to src/index.ts. Co-Authored-By: Claude Opus 4.6 (1M context) --- src/authorization/authorization.ts | 8 +++++++- src/connect/connect.ts | 6 +----- src/connect/interfaces/index.ts | 1 + .../interfaces/list-applications-options.interface.ts | 8 ++++++++ src/index.ts | 1 + src/workos.ts | 2 ++ 6 files changed, 20 insertions(+), 6 deletions(-) create mode 100644 src/connect/interfaces/list-applications-options.interface.ts diff --git a/src/authorization/authorization.ts b/src/authorization/authorization.ts index 158c060a6..9b2b16c2c 100644 --- a/src/authorization/authorization.ts +++ b/src/authorization/authorization.ts @@ -241,7 +241,13 @@ export class Authorization { return deserializePermission(data); } - /** @deprecated Use `workos.permissions.list()` instead. */ + /** + * @deprecated Use `workos.permissions.list()` instead. Note that the new + * method returns an `AutoPaginatable` (with auto-pagination + * support) rather than this method's plain `PermissionList`. The shapes + * are largely compatible — `.data` and `.listMetadata` are both available + * — but explicit `PermissionList` type annotations won't transfer. + */ async listPermissions( options?: ListPermissionsOptions, ): Promise { diff --git a/src/connect/connect.ts b/src/connect/connect.ts index 20a5c3bfa..db2bc5cb0 100644 --- a/src/connect/connect.ts +++ b/src/connect/connect.ts @@ -25,6 +25,7 @@ import type { CreateOAuthApplication } from './interfaces/create-oauth-applicati import type { CreateM2MApplication } from './interfaces/create-m2m-application.interface'; import type { UpdateOAuthApplication } from './interfaces/update-oauth-application.interface'; import type { CreateApplicationSecret } from './interfaces/create-application-secret.interface'; +import type { ListApplicationsOptions } from './interfaces/list-applications-options.interface'; import { deserializeExternalAuthCompleteResponse } from './serializers/external-auth-complete-response.serializer'; import { deserializeConnectApplication } from './serializers/connect-application.serializer'; import { deserializeApplicationCredentialsListItem } from './serializers/application-credentials-list-item.serializer'; @@ -35,11 +36,6 @@ import { serializeCreateM2MApplication } from './serializers/create-m2m-applicat import { serializeUpdateOAuthApplication } from './serializers/update-oauth-application.serializer'; import { serializeCreateApplicationSecret } from './serializers/create-application-secret.serializer'; -export interface ListApplicationsOptions extends PaginationOptions { - /** Filter Connect Applications by organization ID. */ - organizationId?: string; -} - const serializeListApplicationsOptions = ( options: ListApplicationsOptions, ): PaginationOptions => { diff --git a/src/connect/interfaces/index.ts b/src/connect/interfaces/index.ts index 5593eba76..915e3484c 100644 --- a/src/connect/interfaces/index.ts +++ b/src/connect/interfaces/index.ts @@ -7,6 +7,7 @@ export * from './create-application-secret.interface'; export * from './create-m2m-application.interface'; export * from './create-oauth-application.interface'; export * from './external-auth-complete-response.interface'; +export * from './list-applications-options.interface'; export * from './new-connect-application-secret.interface'; export * from './redirect-uri-input.interface'; export * from './update-oauth-application.interface'; diff --git a/src/connect/interfaces/list-applications-options.interface.ts b/src/connect/interfaces/list-applications-options.interface.ts new file mode 100644 index 000000000..a181aff25 --- /dev/null +++ b/src/connect/interfaces/list-applications-options.interface.ts @@ -0,0 +1,8 @@ +// This file is auto-generated by oagen. Do not edit. + +import type { PaginationOptions } from '../../common/interfaces/pagination-options.interface'; + +export interface ListApplicationsOptions extends PaginationOptions { + /** Filter Connect Applications by organization ID. */ + organizationId?: string; +} diff --git a/src/index.ts b/src/index.ts index 32787d7b3..215dfd03d 100644 --- a/src/index.ts +++ b/src/index.ts @@ -16,6 +16,7 @@ export * from './authorization/interfaces'; export * from './common/exceptions'; export * from './common/interfaces'; export * from './common/utils/pagination'; +export * from './connect/interfaces'; export * from './directory-sync/interfaces'; export * from './events/interfaces'; export * from './feature-flags/interfaces'; diff --git a/src/workos.ts b/src/workos.ts index 0defb07ef..8f934a3b2 100644 --- a/src/workos.ts +++ b/src/workos.ts @@ -81,6 +81,7 @@ export class WorkOS { readonly directorySync = new DirectorySync(this); readonly events = new Events(this); readonly featureFlags = new FeatureFlags(this); + /** @deprecated Use `workos.authorization` instead. */ readonly fga = new FGA(this); readonly mfa = new Mfa(this); readonly organizations = new Organizations(this); @@ -88,6 +89,7 @@ export class WorkOS { readonly passwordless = new Passwordless(this); readonly permissions = new Permissions(this); readonly pipes = new Pipes(this); + /** @deprecated Use `workos.adminPortal` instead. */ readonly portal = new Portal(this); readonly radar = new Radar(this); readonly sso = new SSO(this); From 7ec1297314048b8225fe836bdfbb84856b9b538d Mon Sep 17 00:00:00 2001 From: "Garen J. Torikian" Date: Fri, 10 Apr 2026 18:11:11 -0400 Subject: [PATCH 16/17] trashed unused permissions file --- .../authorization-permission.fixture.json | 11 -- ...eate-authorization-permission.fixture.json | 6 - ...list-authorization-permission.fixture.json | 19 --- .../fixtures/permission.fixture.json | 11 -- ...date-authorization-permission.fixture.json | 4 - .../authorization-permission.interface.ts | 34 ---- ...eate-authorization-permission.interface.ts | 19 --- src/permissions/interfaces/index.ts | 5 - .../interfaces/permission.interface.ts | 9 - ...date-authorization-permission.interface.ts | 10 -- src/permissions/permissions.spec.ts | 160 ------------------ src/permissions/permissions.ts | 117 ------------- .../authorization-permission.serializer.ts | 34 ---- ...ate-authorization-permission.serializer.ts | 24 --- .../serializers/permission.serializer.ts | 6 - ...ate-authorization-permission.serializer.ts | 6 - src/workos.ts | 2 - 17 files changed, 477 deletions(-) delete mode 100644 src/permissions/fixtures/authorization-permission.fixture.json delete mode 100644 src/permissions/fixtures/create-authorization-permission.fixture.json delete mode 100644 src/permissions/fixtures/list-authorization-permission.fixture.json delete mode 100644 src/permissions/fixtures/permission.fixture.json delete mode 100644 src/permissions/fixtures/update-authorization-permission.fixture.json delete mode 100644 src/permissions/interfaces/authorization-permission.interface.ts delete mode 100644 src/permissions/interfaces/create-authorization-permission.interface.ts delete mode 100644 src/permissions/interfaces/index.ts delete mode 100644 src/permissions/interfaces/permission.interface.ts delete mode 100644 src/permissions/interfaces/update-authorization-permission.interface.ts delete mode 100644 src/permissions/permissions.spec.ts delete mode 100644 src/permissions/permissions.ts delete mode 100644 src/permissions/serializers/authorization-permission.serializer.ts delete mode 100644 src/permissions/serializers/create-authorization-permission.serializer.ts delete mode 100644 src/permissions/serializers/permission.serializer.ts delete mode 100644 src/permissions/serializers/update-authorization-permission.serializer.ts diff --git a/src/permissions/fixtures/authorization-permission.fixture.json b/src/permissions/fixtures/authorization-permission.fixture.json deleted file mode 100644 index 0424eed6e..000000000 --- a/src/permissions/fixtures/authorization-permission.fixture.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "object": "permission", - "id": "perm_01HXYZ123456789ABCDEFGHIJ", - "slug": "documents:read", - "name": "View Documents", - "description": "Allows viewing document contents", - "system": false, - "resource_type_slug": "workspace", - "created_at": "2026-01-15T12:00:00.000Z", - "updated_at": "2026-01-15T12:00:00.000Z" -} diff --git a/src/permissions/fixtures/create-authorization-permission.fixture.json b/src/permissions/fixtures/create-authorization-permission.fixture.json deleted file mode 100644 index 6c9dc2aed..000000000 --- a/src/permissions/fixtures/create-authorization-permission.fixture.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "slug": "documents:read", - "name": "View Documents", - "description": "Allows viewing document contents", - "resource_type_slug": "document" -} diff --git a/src/permissions/fixtures/list-authorization-permission.fixture.json b/src/permissions/fixtures/list-authorization-permission.fixture.json deleted file mode 100644 index 348f9d773..000000000 --- a/src/permissions/fixtures/list-authorization-permission.fixture.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "data": [ - { - "object": "permission", - "id": "perm_01HXYZ123456789ABCDEFGHIJ", - "slug": "documents:read", - "name": "View Documents", - "description": "Allows viewing document contents", - "system": false, - "resource_type_slug": "workspace", - "created_at": "2026-01-15T12:00:00.000Z", - "updated_at": "2026-01-15T12:00:00.000Z" - } - ], - "list_metadata": { - "before": null, - "after": null - } -} diff --git a/src/permissions/fixtures/permission.fixture.json b/src/permissions/fixtures/permission.fixture.json deleted file mode 100644 index e4ac8b822..000000000 --- a/src/permissions/fixtures/permission.fixture.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "object": "permission", - "id": "perm_01HXYZ123456789ABCDEFGHIJ", - "slug": "documents:read", - "name": "View Documents", - "description": "Allows viewing document contents", - "system": false, - "resource_type_slug": "document", - "created_at": "2026-01-15T12:00:00.000Z", - "updated_at": "2026-01-15T12:00:00.000Z" -} diff --git a/src/permissions/fixtures/update-authorization-permission.fixture.json b/src/permissions/fixtures/update-authorization-permission.fixture.json deleted file mode 100644 index 5aed776b7..000000000 --- a/src/permissions/fixtures/update-authorization-permission.fixture.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "name": "View Documents", - "description": "Allows viewing document contents" -} diff --git a/src/permissions/interfaces/authorization-permission.interface.ts b/src/permissions/interfaces/authorization-permission.interface.ts deleted file mode 100644 index ac727d924..000000000 --- a/src/permissions/interfaces/authorization-permission.interface.ts +++ /dev/null @@ -1,34 +0,0 @@ -// This file is auto-generated by oagen. Do not edit. - -export interface AuthorizationPermission { - /** Distinguishes the Permission object. */ - object: 'permission'; - /** Unique identifier of the Permission. */ - id: string; - /** A unique key to reference the permission. Must be lowercase and contain only letters, numbers, hyphens, underscores, colons, periods, and asterisks. */ - slug: string; - /** A descriptive name for the Permission. */ - name: string; - /** An optional description of the Permission. */ - description: string | null; - /** Whether the permission is a system permission. System permissions are managed by WorkOS and cannot be deleted. */ - system: boolean; - /** The slug of the resource type associated with the permission. */ - resourceTypeSlug: string; - /** An ISO 8601 timestamp. */ - createdAt: string; - /** An ISO 8601 timestamp. */ - updatedAt: string; -} - -export interface AuthorizationPermissionResponse { - object: 'permission'; - id: string; - slug: string; - name: string; - description: string | null; - system: boolean; - resource_type_slug: string; - created_at: string; - updated_at: string; -} diff --git a/src/permissions/interfaces/create-authorization-permission.interface.ts b/src/permissions/interfaces/create-authorization-permission.interface.ts deleted file mode 100644 index ddcfb825e..000000000 --- a/src/permissions/interfaces/create-authorization-permission.interface.ts +++ /dev/null @@ -1,19 +0,0 @@ -// This file is auto-generated by oagen. Do not edit. - -export interface CreateAuthorizationPermission { - /** A unique key to reference the permission. Must be lowercase and contain only letters, numbers, hyphens, underscores, colons, periods, and asterisks. */ - slug: string; - /** A descriptive name for the Permission. */ - name: string; - /** An optional description of the Permission. */ - description?: string | null; - /** The slug of the resource type this permission is scoped to. */ - resourceTypeSlug?: string; -} - -export interface CreateAuthorizationPermissionResponse { - slug: string; - name: string; - description?: string | null; - resource_type_slug?: string; -} diff --git a/src/permissions/interfaces/index.ts b/src/permissions/interfaces/index.ts deleted file mode 100644 index 8f156997c..000000000 --- a/src/permissions/interfaces/index.ts +++ /dev/null @@ -1,5 +0,0 @@ -// This file is auto-generated by oagen. Do not edit. - -export * from './authorization-permission.interface'; -export * from './create-authorization-permission.interface'; -export * from './update-authorization-permission.interface'; diff --git a/src/permissions/interfaces/permission.interface.ts b/src/permissions/interfaces/permission.interface.ts deleted file mode 100644 index 864e38042..000000000 --- a/src/permissions/interfaces/permission.interface.ts +++ /dev/null @@ -1,9 +0,0 @@ -// This file is auto-generated by oagen. Do not edit. - -import type { - AuthorizationPermission, - AuthorizationPermissionResponse, -} from './authorization-permission.interface'; - -export type Permission = AuthorizationPermission; -export type PermissionResponse = AuthorizationPermissionResponse; diff --git a/src/permissions/interfaces/update-authorization-permission.interface.ts b/src/permissions/interfaces/update-authorization-permission.interface.ts deleted file mode 100644 index f15bcb0fa..000000000 --- a/src/permissions/interfaces/update-authorization-permission.interface.ts +++ /dev/null @@ -1,10 +0,0 @@ -// This file is auto-generated by oagen. Do not edit. - -import type { - UpdateOrganizationRole, - UpdateOrganizationRoleResponse, -} from '../../authorization/interfaces/update-organization-role.interface'; - -export type UpdateAuthorizationPermission = UpdateOrganizationRole; -export type UpdateAuthorizationPermissionResponse = - UpdateOrganizationRoleResponse; diff --git a/src/permissions/permissions.spec.ts b/src/permissions/permissions.spec.ts deleted file mode 100644 index 6b17fe6c2..000000000 --- a/src/permissions/permissions.spec.ts +++ /dev/null @@ -1,160 +0,0 @@ -// This file is auto-generated by oagen. Do not edit. - -import fetch from 'jest-fetch-mock'; -import { - fetchOnce, - fetchURL, - fetchMethod, - fetchSearchParams, - fetchBody, - testEmptyResults, - testPaginationParams, - testUnauthorized, -} from '../common/utils/test-utils'; -import { WorkOS } from '../workos'; - -import listAuthorizationPermissionFixture from './fixtures/list-authorization-permission.fixture.json'; -import permissionFixture from './fixtures/permission.fixture.json'; -import authorizationPermissionFixture from './fixtures/authorization-permission.fixture.json'; - -const workos = new WorkOS('sk_test_Sz3IQjepeSWaI4cMS4ms4sMuU'); - -function expectAuthorizationPermission(result: any) { - expect(result.object).toBe('permission'); - expect(result.id).toBe('perm_01HXYZ123456789ABCDEFGHIJ'); - expect(result.slug).toBe('documents:read'); - expect(result.name).toBe('View Documents'); - expect(result.description).toBe('Allows viewing document contents'); - expect(result.system).toBe(false); - expect(result.resourceTypeSlug).toBe('workspace'); - expect(result.createdAt).toBe('2026-01-15T12:00:00.000Z'); - expect(result.updatedAt).toBe('2026-01-15T12:00:00.000Z'); -} - -describe('Permissions', () => { - beforeEach(() => fetch.resetMocks()); - - describe('list', () => { - it('returns paginated results', async () => { - fetchOnce(listAuthorizationPermissionFixture); - - const { data, listMetadata } = await workos.permissions.list(); - - expect(fetchMethod()).toBe('GET'); - expect(new URL(String(fetchURL())).pathname).toBe( - '/authorization/permissions', - ); - expect(fetchSearchParams()).toHaveProperty('order'); - expect(Array.isArray(data)).toBe(true); - expect(listMetadata).toBeDefined(); - expect(data.length).toBeGreaterThan(0); - expectAuthorizationPermission(data[0]); - }); - - testEmptyResults(() => workos.permissions.list()); - - testPaginationParams( - (opts) => workos.permissions.list(opts), - listAuthorizationPermissionFixture, - ); - - testUnauthorized(() => workos.permissions.list()); - }); - - describe('create', () => { - it('sends the correct request and returns result', async () => { - fetchOnce(permissionFixture); - - const result = await workos.permissions.create({ - slug: 'test_slug', - name: 'Test', - }); - - expect(fetchMethod()).toBe('POST'); - expect(new URL(String(fetchURL())).pathname).toBe( - '/authorization/permissions', - ); - expect(fetchBody()).toEqual( - expect.objectContaining({ slug: 'test_slug', name: 'Test' }), - ); - expect(result.object).toBe('permission'); - expect(result.id).toBe('perm_01HXYZ123456789ABCDEFGHIJ'); - expect(result.slug).toBe('documents:read'); - expect(result.name).toBe('View Documents'); - expect(result.description).toBe('Allows viewing document contents'); - expect(result.system).toBe(false); - expect(result.resourceTypeSlug).toBe('document'); - expect(result.createdAt).toBe('2026-01-15T12:00:00.000Z'); - expect(result.updatedAt).toBe('2026-01-15T12:00:00.000Z'); - }); - - testUnauthorized(() => - workos.permissions.create({ slug: 'test_slug', name: 'Test' }), - ); - - it('throws UnprocessableEntityException on 422', async () => { - fetchOnce('', { status: 422 }); - await expect( - workos.permissions.create({ slug: 'test_slug', name: 'Test' }), - ).rejects.toThrow(); - }); - }); - - describe('find', () => { - it('returns the expected result', async () => { - fetchOnce(authorizationPermissionFixture); - - const result = await workos.permissions.find('test_slug'); - - expect(fetchMethod()).toBe('GET'); - expect(new URL(String(fetchURL())).pathname).toBe( - '/authorization/permissions/test_slug', - ); - expectAuthorizationPermission(result); - }); - - testUnauthorized(() => workos.permissions.find('test_slug')); - - it('throws NotFoundException on 404', async () => { - fetchOnce('', { status: 404 }); - await expect(workos.permissions.find('test_slug')).rejects.toThrow(); - }); - }); - - describe('update', () => { - it('sends the correct request and returns result', async () => { - fetchOnce(authorizationPermissionFixture); - - const result = await workos.permissions.update('test_slug', {}); - - expect(fetchMethod()).toBe('PATCH'); - expect(new URL(String(fetchURL())).pathname).toBe( - '/authorization/permissions/test_slug', - ); - expect(fetchBody()).toBeDefined(); - expectAuthorizationPermission(result); - }); - - testUnauthorized(() => workos.permissions.update('test_slug', {})); - - it('throws UnprocessableEntityException on 422', async () => { - fetchOnce('', { status: 422 }); - await expect( - workos.permissions.update('test_slug', {}), - ).rejects.toThrow(); - }); - }); - - describe('delete', () => { - it('sends a DELETE request', async () => { - fetchOnce({}, { status: 204 }); - - await workos.permissions.delete('test_slug'); - - expect(fetchMethod()).toBe('DELETE'); - expect(new URL(String(fetchURL())).pathname).toBe( - '/authorization/permissions/test_slug', - ); - }); - }); -}); diff --git a/src/permissions/permissions.ts b/src/permissions/permissions.ts deleted file mode 100644 index b7b8539d4..000000000 --- a/src/permissions/permissions.ts +++ /dev/null @@ -1,117 +0,0 @@ -// This file is auto-generated by oagen. Do not edit. - -import type { WorkOS } from '../workos'; -import type { PaginationOptions } from '../common/interfaces/pagination-options.interface'; -import type { AutoPaginatable } from '../common/utils/pagination'; -import { createPaginatedList } from '../common/utils/fetch-and-deserialize'; -import type { - AuthorizationPermission, - AuthorizationPermissionResponse, -} from './interfaces/authorization-permission.interface'; -import type { - Permission, - PermissionResponse, -} from './interfaces/permission.interface'; -import type { CreateAuthorizationPermission } from './interfaces/create-authorization-permission.interface'; -import type { UpdateAuthorizationPermission } from './interfaces/update-authorization-permission.interface'; -import { deserializeAuthorizationPermission } from './serializers/authorization-permission.serializer'; -import { deserializePermission } from './serializers/permission.serializer'; -import { serializeCreateAuthorizationPermission } from './serializers/create-authorization-permission.serializer'; -import { serializeUpdateAuthorizationPermission } from './serializers/update-authorization-permission.serializer'; - -export class Permissions { - constructor(private readonly workos: WorkOS) {} - - /** - * List permissions - * - * Get a list of all permissions in your WorkOS environment. - * @param options - Pagination and filter options. - * @returns {AutoPaginatable} - * @throws {NotFoundException} 404 - */ - async list( - options?: PaginationOptions, - ): Promise> { - return createPaginatedList< - AuthorizationPermissionResponse, - AuthorizationPermission, - PaginationOptions - >( - this.workos, - '/authorization/permissions', - deserializeAuthorizationPermission, - options, - ); - } - - /** - * Create a permission - * - * Create a new permission in your WorkOS environment. The permission can then be assigned to environment roles and organization roles. - * @param payload - Object containing slug, name. - * @returns {Permission} - * @throws {BadRequestException} 400 - * @throws {NotFoundException} 404 - * @throws {ConflictException} 409 - * @throws {UnprocessableEntityException} 422 - */ - async create(payload: CreateAuthorizationPermission): Promise { - const { data } = await this.workos.post( - '/authorization/permissions', - serializeCreateAuthorizationPermission(payload), - ); - return deserializePermission(data); - } - - /** - * Get a permission - * - * Retrieve a permission by its unique slug. - * @param slug - A unique key to reference the permission. Must be lowercase and contain only letters, numbers, hyphens, underscores, colons, periods, and asterisks. - * @example "documents:read" - * @returns {AuthorizationPermission} - * @throws {NotFoundException} 404 - */ - async find(slug: string): Promise { - const { data } = await this.workos.get( - `/authorization/permissions/${slug}`, - ); - return deserializeAuthorizationPermission(data); - } - - /** - * Update a permission - * - * Update an existing permission. Only the fields provided in the request body will be updated. - * @param slug - A unique key to reference the permission. Must be lowercase and contain only letters, numbers, hyphens, underscores, colons, periods, and asterisks. - * @example "documents:read" - * @param payload - The request body. - * @returns {AuthorizationPermission} - * @throws {NotFoundException} 404 - * @throws {UnprocessableEntityException} 422 - */ - async update( - slug: string, - payload: UpdateAuthorizationPermission, - ): Promise { - const { data } = await this.workos.patch( - `/authorization/permissions/${slug}`, - serializeUpdateAuthorizationPermission(payload), - ); - return deserializeAuthorizationPermission(data); - } - - /** - * Delete a permission - * - * Delete an existing permission. System permissions cannot be deleted. - * @param slug - A unique key to reference the permission. Must be lowercase and contain only letters, numbers, hyphens, underscores, colons, periods, and asterisks. - * @example "documents:read" - * @returns {void} - * @throws {NotFoundException} 404 - */ - async delete(slug: string): Promise { - await this.workos.delete(`/authorization/permissions/${slug}`); - } -} diff --git a/src/permissions/serializers/authorization-permission.serializer.ts b/src/permissions/serializers/authorization-permission.serializer.ts deleted file mode 100644 index 1f228af35..000000000 --- a/src/permissions/serializers/authorization-permission.serializer.ts +++ /dev/null @@ -1,34 +0,0 @@ -// This file is auto-generated by oagen. Do not edit. - -import type { - AuthorizationPermission, - AuthorizationPermissionResponse, -} from '../interfaces/authorization-permission.interface'; - -export const deserializeAuthorizationPermission = ( - response: AuthorizationPermissionResponse, -): AuthorizationPermission => ({ - object: response.object, - id: response.id, - slug: response.slug, - name: response.name, - description: response.description ?? null, - system: response.system, - resourceTypeSlug: response.resource_type_slug, - createdAt: response.created_at, - updatedAt: response.updated_at, -}); - -export const serializeAuthorizationPermission = ( - model: AuthorizationPermission, -): AuthorizationPermissionResponse => ({ - object: model.object, - id: model.id, - slug: model.slug, - name: model.name, - description: model.description, - system: model.system, - resource_type_slug: model.resourceTypeSlug, - created_at: model.createdAt, - updated_at: model.updatedAt, -}); diff --git a/src/permissions/serializers/create-authorization-permission.serializer.ts b/src/permissions/serializers/create-authorization-permission.serializer.ts deleted file mode 100644 index 1d1346562..000000000 --- a/src/permissions/serializers/create-authorization-permission.serializer.ts +++ /dev/null @@ -1,24 +0,0 @@ -// This file is auto-generated by oagen. Do not edit. - -import type { - CreateAuthorizationPermission, - CreateAuthorizationPermissionResponse, -} from '../interfaces/create-authorization-permission.interface'; - -export const deserializeCreateAuthorizationPermission = ( - response: CreateAuthorizationPermissionResponse, -): CreateAuthorizationPermission => ({ - slug: response.slug, - name: response.name, - description: response.description ?? null, - resourceTypeSlug: response.resource_type_slug, -}); - -export const serializeCreateAuthorizationPermission = ( - model: CreateAuthorizationPermission, -): CreateAuthorizationPermissionResponse => ({ - slug: model.slug, - name: model.name, - description: model.description ?? null, - resource_type_slug: model.resourceTypeSlug, -}); diff --git a/src/permissions/serializers/permission.serializer.ts b/src/permissions/serializers/permission.serializer.ts deleted file mode 100644 index 78f0d3f24..000000000 --- a/src/permissions/serializers/permission.serializer.ts +++ /dev/null @@ -1,6 +0,0 @@ -// This file is auto-generated by oagen. Do not edit. - -export { - deserializeAuthorizationPermission as deserializePermission, - serializeAuthorizationPermission as serializePermission, -} from './authorization-permission.serializer'; diff --git a/src/permissions/serializers/update-authorization-permission.serializer.ts b/src/permissions/serializers/update-authorization-permission.serializer.ts deleted file mode 100644 index 078a2f9b8..000000000 --- a/src/permissions/serializers/update-authorization-permission.serializer.ts +++ /dev/null @@ -1,6 +0,0 @@ -// This file is auto-generated by oagen. Do not edit. - -export { - deserializeUpdateOrganizationRole as deserializeUpdateAuthorizationPermission, - serializeUpdateOrganizationRole as serializeUpdateAuthorizationPermission, -} from '../../authorization/serializers/update-organization-role.serializer'; diff --git a/src/workos.ts b/src/workos.ts index 8f934a3b2..25b8e3919 100644 --- a/src/workos.ts +++ b/src/workos.ts @@ -50,7 +50,6 @@ import { getEnv } from './common/utils/env'; import { getRuntimeInfo } from './common/utils/runtime-info'; import { version as VERSION } from '../package.json' with { type: 'json' }; import { AdminPortal } from './admin-portal/admin-portal'; -import { Permissions } from './permissions/permissions'; import { Radar } from './radar/radar'; import { WebhooksEndpoints } from './webhooks/webhooks-endpoints'; import { Connect } from './connect/connect'; @@ -87,7 +86,6 @@ export class WorkOS { readonly organizations = new Organizations(this); readonly organizationDomains = new OrganizationDomains(this); readonly passwordless = new Passwordless(this); - readonly permissions = new Permissions(this); readonly pipes = new Pipes(this); /** @deprecated Use `workos.adminPortal` instead. */ readonly portal = new Portal(this); From 2dacf34d0d773b20185a431a4208aacb6bae91e1 Mon Sep 17 00:00:00 2001 From: "Garen J. Torikian" Date: Tue, 14 Apr 2026 14:42:54 -0400 Subject: [PATCH 17/17] fix prettier formatting on generated and hand-maintained files Co-Authored-By: Claude Opus 4.6 (1M context) --- src/common/utils/fetch-and-deserialize.ts | 10 +- src/connect/connect.spec.ts | 119 +++++++++++++++++----- src/connect/serializers.spec.ts | 81 +++++++++++---- 3 files changed, 160 insertions(+), 50 deletions(-) diff --git a/src/common/utils/fetch-and-deserialize.ts b/src/common/utils/fetch-and-deserialize.ts index b05e87841..0eea1fe11 100644 --- a/src/common/utils/fetch-and-deserialize.ts +++ b/src/common/utils/fetch-and-deserialize.ts @@ -63,12 +63,10 @@ export async function createPaginatedList< // filters persist across pages, then spread `params` so the cursor // overrides `after`/`limit` as pages advance. (params) => - fetchAndDeserialize( - workos, - endpoint, - deserializeFn, - { ...wireOptions, ...params }, - ), + fetchAndDeserialize(workos, endpoint, deserializeFn, { + ...wireOptions, + ...params, + }), options, ); } diff --git a/src/connect/connect.spec.ts b/src/connect/connect.spec.ts index 07b5039db..b6ce57b04 100644 --- a/src/connect/connect.spec.ts +++ b/src/connect/connect.spec.ts @@ -27,7 +27,7 @@ function expectConnectApplication(result: any) { expect(result.clientId).toBe('client_01HXYZ123456789ABCDEFGHIJ'); expect(result.description).toBe('An application for managing user access'); expect(result.name).toBe('My Application'); - expect(result.scopes).toEqual(["openid","profile","email"]); + expect(result.scopes).toEqual(['openid', 'profile', 'email']); expect(result.createdAt).toBe('2026-01-15T12:00:00.000Z'); expect(result.updatedAt).toBe('2026-01-15T12:00:00.000Z'); } @@ -39,15 +39,32 @@ describe('Connect', () => { it('sends the correct request and returns result', async () => { fetchOnce(externalAuthCompleteResponseFixture); - const result = await workos.connect.completeLogin({ externalAuthId: 'external_auth_id_01234', user: { id: '01234', email: 'test@example.com' } }); + const result = await workos.connect.completeLogin({ + externalAuthId: 'external_auth_id_01234', + user: { id: '01234', email: 'test@example.com' }, + }); expect(fetchMethod()).toBe('POST'); - expect(new URL(String(fetchURL())).pathname).toBe('/authkit/oauth2/complete'); - expect(fetchBody()).toEqual(expect.objectContaining({ external_auth_id: 'external_auth_id_01234', user: { id: '01234', email: 'test@example.com' } })); - expect(result.redirectUri).toBe('https://your-authkit-domain.workos.com/oauth/authorize/complete?state=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdGF0ZSI6InJhbmRvbV9zdGF0ZV9zdHJpbmciLCJpYXQiOjE3NDI2MDQ4NTN9.abc123def456ghi789'); + expect(new URL(String(fetchURL())).pathname).toBe( + '/authkit/oauth2/complete', + ); + expect(fetchBody()).toEqual( + expect.objectContaining({ + external_auth_id: 'external_auth_id_01234', + user: { id: '01234', email: 'test@example.com' }, + }), + ); + expect(result.redirectUri).toBe( + 'https://your-authkit-domain.workos.com/oauth/authorize/complete?state=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdGF0ZSI6InJhbmRvbV9zdGF0ZV9zdHJpbmciLCJpYXQiOjE3NDI2MDQ4NTN9.abc123def456ghi789', + ); }); - testUnauthorized(() => workos.connect.completeLogin({ externalAuthId: 'external_auth_id_01234', user: { id: '01234', email: 'test@example.com' } })); + testUnauthorized(() => + workos.connect.completeLogin({ + externalAuthId: 'external_auth_id_01234', + user: { id: '01234', email: 'test@example.com' }, + }), + ); }); describe('listApplications', () => { @@ -57,7 +74,9 @@ describe('Connect', () => { const { data, listMetadata } = await workos.connect.listApplications(); expect(fetchMethod()).toBe('GET'); - expect(new URL(String(fetchURL())).pathname).toBe('/connect/applications'); + expect(new URL(String(fetchURL())).pathname).toBe( + '/connect/applications', + ); expect(fetchSearchParams()).toHaveProperty('order'); expect(Array.isArray(data)).toBe(true); expect(listMetadata).toBeDefined(); @@ -92,19 +111,43 @@ describe('Connect', () => { it('sends the correct request and returns result', async () => { fetchOnce(connectApplicationFixture); - const result = await workos.connect.createApplication({ name: 'Test', applicationType: 'oauth', isFirstParty: true }); + const result = await workos.connect.createApplication({ + name: 'Test', + applicationType: 'oauth', + isFirstParty: true, + }); expect(fetchMethod()).toBe('POST'); - expect(new URL(String(fetchURL())).pathname).toBe('/connect/applications'); - expect(fetchBody()).toEqual(expect.objectContaining({ name: 'Test', application_type: 'oauth', is_first_party: true })); + expect(new URL(String(fetchURL())).pathname).toBe( + '/connect/applications', + ); + expect(fetchBody()).toEqual( + expect.objectContaining({ + name: 'Test', + application_type: 'oauth', + is_first_party: true, + }), + ); expectConnectApplication(result); }); - testUnauthorized(() => workos.connect.createApplication({ name: 'Test', applicationType: 'oauth', isFirstParty: true })); + testUnauthorized(() => + workos.connect.createApplication({ + name: 'Test', + applicationType: 'oauth', + isFirstParty: true, + }), + ); it('throws UnprocessableEntityException on 422', async () => { fetchOnce('', { status: 422 }); - await expect(workos.connect.createApplication({ name: 'Test', applicationType: 'oauth', isFirstParty: true })).rejects.toThrow(); + await expect( + workos.connect.createApplication({ + name: 'Test', + applicationType: 'oauth', + isFirstParty: true, + }), + ).rejects.toThrow(); }); }); @@ -115,7 +158,9 @@ describe('Connect', () => { const result = await workos.connect.getApplication('test_id'); expect(fetchMethod()).toBe('GET'); - expect(new URL(String(fetchURL())).pathname).toBe('/connect/applications/test_id'); + expect(new URL(String(fetchURL())).pathname).toBe( + '/connect/applications/test_id', + ); expectConnectApplication(result); }); @@ -134,7 +179,9 @@ describe('Connect', () => { const result = await workos.connect.updateApplication('test_id', {}); expect(fetchMethod()).toBe('PUT'); - expect(new URL(String(fetchURL())).pathname).toBe('/connect/applications/test_id'); + expect(new URL(String(fetchURL())).pathname).toBe( + '/connect/applications/test_id', + ); expect(fetchBody()).toBeDefined(); expectConnectApplication(result); }); @@ -143,7 +190,9 @@ describe('Connect', () => { it('throws UnprocessableEntityException on 422', async () => { fetchOnce('', { status: 422 }); - await expect(workos.connect.updateApplication('test_id', {})).rejects.toThrow(); + await expect( + workos.connect.updateApplication('test_id', {}), + ).rejects.toThrow(); }); }); @@ -154,7 +203,9 @@ describe('Connect', () => { await workos.connect.deleteApplication('test_id'); expect(fetchMethod()).toBe('DELETE'); - expect(new URL(String(fetchURL())).pathname).toBe('/connect/applications/test_id'); + expect(new URL(String(fetchURL())).pathname).toBe( + '/connect/applications/test_id', + ); }); }); @@ -165,10 +216,13 @@ describe('Connect', () => { // second fixture. fetchOnce([applicationCredentialsListItemFixture]); - const result = await workos.connect.listApplicationClientSecrets('test_id'); + const result = + await workos.connect.listApplicationClientSecrets('test_id'); expect(fetchMethod()).toBe('GET'); - expect(new URL(String(fetchURL())).pathname).toBe('/connect/applications/test_id/client_secrets'); + expect(new URL(String(fetchURL())).pathname).toBe( + '/connect/applications/test_id/client_secrets', + ); expect(Array.isArray(result)).toBe(true); expect(result).toHaveLength(1); expect(result[0].object).toBe('connect_application_secret'); @@ -179,17 +233,24 @@ describe('Connect', () => { expect(result[0].updatedAt).toBe('2026-01-15T12:00:00.000Z'); }); - testUnauthorized(() => workos.connect.listApplicationClientSecrets('test_id')); + testUnauthorized(() => + workos.connect.listApplicationClientSecrets('test_id'), + ); }); describe('createApplicationClientSecret', () => { it('sends the correct request and returns result', async () => { fetchOnce(newConnectApplicationSecretFixture); - const result = await workos.connect.createApplicationClientSecret('test_id', {}); + const result = await workos.connect.createApplicationClientSecret( + 'test_id', + {}, + ); expect(fetchMethod()).toBe('POST'); - expect(new URL(String(fetchURL())).pathname).toBe('/connect/applications/test_id/client_secrets'); + expect(new URL(String(fetchURL())).pathname).toBe( + '/connect/applications/test_id/client_secrets', + ); expect(fetchBody()).toBeDefined(); expect(result.object).toBe('connect_application_secret'); expect(result.id).toBe('secret_01J9Q2Z3X4Y5W6V7U8T9S0R1Q'); @@ -197,14 +258,20 @@ describe('Connect', () => { expect(result.lastUsedAt).toBe(null); expect(result.createdAt).toBe('2026-01-15T12:00:00.000Z'); expect(result.updatedAt).toBe('2026-01-15T12:00:00.000Z'); - expect(result.secret).toBe('abc123def456ghi789jkl012mno345pqr678stu901vwx234yz'); + expect(result.secret).toBe( + 'abc123def456ghi789jkl012mno345pqr678stu901vwx234yz', + ); }); - testUnauthorized(() => workos.connect.createApplicationClientSecret('test_id', {})); + testUnauthorized(() => + workos.connect.createApplicationClientSecret('test_id', {}), + ); it('throws UnprocessableEntityException on 422', async () => { fetchOnce('', { status: 422 }); - await expect(workos.connect.createApplicationClientSecret('test_id', {})).rejects.toThrow(); + await expect( + workos.connect.createApplicationClientSecret('test_id', {}), + ).rejects.toThrow(); }); }); @@ -215,7 +282,9 @@ describe('Connect', () => { await workos.connect.deleteClientSecret('test_id'); expect(fetchMethod()).toBe('DELETE'); - expect(new URL(String(fetchURL())).pathname).toBe('/connect/client_secrets/test_id'); + expect(new URL(String(fetchURL())).pathname).toBe( + '/connect/client_secrets/test_id', + ); }); }); }); diff --git a/src/connect/serializers.spec.ts b/src/connect/serializers.spec.ts index e10017395..eeaa22958 100644 --- a/src/connect/serializers.spec.ts +++ b/src/connect/serializers.spec.ts @@ -1,17 +1,53 @@ // This file is auto-generated by oagen. Do not edit. -import { deserializeUserObject, serializeUserObject } from './serializers/user-object.serializer'; -import { deserializeUserConsentOption, serializeUserConsentOption } from './serializers/user-consent-option.serializer'; -import { deserializeUserManagementLoginRequest, serializeUserManagementLoginRequest } from './serializers/user-management-login-request.serializer'; -import { deserializeRedirectUriInput, serializeRedirectUriInput } from './serializers/redirect-uri-input.serializer'; -import { deserializeCreateOAuthApplication, serializeCreateOAuthApplication } from './serializers/create-oauth-application.serializer'; -import { deserializeCreateM2MApplication, serializeCreateM2MApplication } from './serializers/create-m2m-application.serializer'; -import { deserializeUpdateOAuthApplication, serializeUpdateOAuthApplication } from './serializers/update-oauth-application.serializer'; -import { deserializeExternalAuthCompleteResponse, serializeExternalAuthCompleteResponse } from './serializers/external-auth-complete-response.serializer'; -import { deserializeConnectApplication, serializeConnectApplication } from './serializers/connect-application.serializer'; -import { deserializeNewConnectApplicationSecret, serializeNewConnectApplicationSecret } from './serializers/new-connect-application-secret.serializer'; -import { deserializeUserConsentOptionChoice, serializeUserConsentOptionChoice } from './serializers/user-consent-option-choice.serializer'; -import { deserializeApplicationCredentialsListItem, serializeApplicationCredentialsListItem } from './serializers/application-credentials-list-item.serializer'; +import { + deserializeUserObject, + serializeUserObject, +} from './serializers/user-object.serializer'; +import { + deserializeUserConsentOption, + serializeUserConsentOption, +} from './serializers/user-consent-option.serializer'; +import { + deserializeUserManagementLoginRequest, + serializeUserManagementLoginRequest, +} from './serializers/user-management-login-request.serializer'; +import { + deserializeRedirectUriInput, + serializeRedirectUriInput, +} from './serializers/redirect-uri-input.serializer'; +import { + deserializeCreateOAuthApplication, + serializeCreateOAuthApplication, +} from './serializers/create-oauth-application.serializer'; +import { + deserializeCreateM2MApplication, + serializeCreateM2MApplication, +} from './serializers/create-m2m-application.serializer'; +import { + deserializeUpdateOAuthApplication, + serializeUpdateOAuthApplication, +} from './serializers/update-oauth-application.serializer'; +import { + deserializeExternalAuthCompleteResponse, + serializeExternalAuthCompleteResponse, +} from './serializers/external-auth-complete-response.serializer'; +import { + deserializeConnectApplication, + serializeConnectApplication, +} from './serializers/connect-application.serializer'; +import { + deserializeNewConnectApplicationSecret, + serializeNewConnectApplicationSecret, +} from './serializers/new-connect-application-secret.serializer'; +import { + deserializeUserConsentOptionChoice, + serializeUserConsentOptionChoice, +} from './serializers/user-consent-option-choice.serializer'; +import { + deserializeApplicationCredentialsListItem, + serializeApplicationCredentialsListItem, +} from './serializers/application-credentials-list-item.serializer'; import type { UserObjectResponse } from './interfaces/user-object.interface'; import type { UserConsentOptionResponse } from './interfaces/user-consent-option.interface'; import type { UserManagementLoginRequestResponse } from './interfaces/user-management-login-request.interface'; @@ -57,7 +93,8 @@ describe('UserConsentOptionSerializer', () => { describe('UserManagementLoginRequestSerializer', () => { it('round-trips through serialize/deserialize', () => { - const fixture = userManagementLoginRequestFixture as UserManagementLoginRequestResponse; + const fixture = + userManagementLoginRequestFixture as UserManagementLoginRequestResponse; const deserialized = deserializeUserManagementLoginRequest(fixture); const reserialized = serializeUserManagementLoginRequest(deserialized); expect(reserialized).toEqual(expect.objectContaining(fixture)); @@ -75,7 +112,8 @@ describe('RedirectUriInputSerializer', () => { describe('CreateOAuthApplicationSerializer', () => { it('round-trips through serialize/deserialize', () => { - const fixture = createOAuthApplicationFixture as CreateOAuthApplicationResponse; + const fixture = + createOAuthApplicationFixture as CreateOAuthApplicationResponse; const deserialized = deserializeCreateOAuthApplication(fixture); const reserialized = serializeCreateOAuthApplication(deserialized); expect(reserialized).toEqual(expect.objectContaining(fixture)); @@ -93,7 +131,8 @@ describe('CreateM2MApplicationSerializer', () => { describe('UpdateOAuthApplicationSerializer', () => { it('round-trips through serialize/deserialize', () => { - const fixture = updateOAuthApplicationFixture as UpdateOAuthApplicationResponse; + const fixture = + updateOAuthApplicationFixture as UpdateOAuthApplicationResponse; const deserialized = deserializeUpdateOAuthApplication(fixture); const reserialized = serializeUpdateOAuthApplication(deserialized); expect(reserialized).toEqual(expect.objectContaining(fixture)); @@ -102,7 +141,8 @@ describe('UpdateOAuthApplicationSerializer', () => { describe('ExternalAuthCompleteResponseSerializer', () => { it('round-trips through serialize/deserialize', () => { - const fixture = externalAuthCompleteResponseFixture as ExternalAuthCompleteResponseWire; + const fixture = + externalAuthCompleteResponseFixture as ExternalAuthCompleteResponseWire; const deserialized = deserializeExternalAuthCompleteResponse(fixture); const reserialized = serializeExternalAuthCompleteResponse(deserialized); expect(reserialized).toEqual(expect.objectContaining(fixture)); @@ -120,7 +160,8 @@ describe('ConnectApplicationSerializer', () => { describe('NewConnectApplicationSecretSerializer', () => { it('round-trips through serialize/deserialize', () => { - const fixture = newConnectApplicationSecretFixture as NewConnectApplicationSecretResponse; + const fixture = + newConnectApplicationSecretFixture as NewConnectApplicationSecretResponse; const deserialized = deserializeNewConnectApplicationSecret(fixture); const reserialized = serializeNewConnectApplicationSecret(deserialized); expect(reserialized).toEqual(expect.objectContaining(fixture)); @@ -129,7 +170,8 @@ describe('NewConnectApplicationSecretSerializer', () => { describe('UserConsentOptionChoiceSerializer', () => { it('round-trips through serialize/deserialize', () => { - const fixture = userConsentOptionChoiceFixture as UserConsentOptionChoiceResponse; + const fixture = + userConsentOptionChoiceFixture as UserConsentOptionChoiceResponse; const deserialized = deserializeUserConsentOptionChoice(fixture); const reserialized = serializeUserConsentOptionChoice(deserialized); expect(reserialized).toEqual(expect.objectContaining(fixture)); @@ -138,7 +180,8 @@ describe('UserConsentOptionChoiceSerializer', () => { describe('ApplicationCredentialsListItemSerializer', () => { it('round-trips through serialize/deserialize', () => { - const fixture = applicationCredentialsListItemFixture as ApplicationCredentialsListItemResponse; + const fixture = + applicationCredentialsListItemFixture as ApplicationCredentialsListItemResponse; const deserialized = deserializeApplicationCredentialsListItem(fixture); const reserialized = serializeApplicationCredentialsListItem(deserialized); expect(reserialized).toEqual(expect.objectContaining(fixture));