Skip to content

Built for people who want to own their automations. Join the waitlist for an invite.

Package listing

@kody/workos

src/create-portal-link.ts

101 lines · 3.2 KB · TypeScript
import { workosRequest } from './client.ts'
import { WORKOS_PORTAL_INTENTS } from './setup.ts'
import type { DryRunResult, JsonRecord, WorkosAuthInput, WorkosPortalIntent } from './types.ts'
import { compactRecord, optionalString, requireRecord, requireString, stringField } from './types.ts'

export type CreatePortalLinkInput = WorkosAuthInput & {
	organization: string
	intent: WorkosPortalIntent
	returnUrl?: string
	successUrl?: string
	confirm?: boolean
	dryRun?: boolean
}

export type CreatePortalLinkResult = {
	link: string
	intent: WorkosPortalIntent
	organization: string
}

function requireIntent(value: unknown): WorkosPortalIntent {
	const intent = requireString(value, 'intent')
	if (!(WORKOS_PORTAL_INTENTS as ReadonlyArray<string>).includes(intent)) {
		throw new Error(`intent must be one of: ${WORKOS_PORTAL_INTENTS.join(', ')}.`)
	}
	return intent as WorkosPortalIntent
}

/**
 * Generate a WorkOS Admin Portal link. Requires `confirm: true`, or use
 * `dryRun: true`.
 * @example
 * import createPortalLink from 'kody:@kody/workos/create-portal-link'
 * const preview = await createPortalLink({
 *   organization: 'org_01EXAMPLE',
 *   intent: 'sso',
 *   dryRun: true,
 * })
 */
export async function createPortalLink(
	input: CreatePortalLinkInput,
): Promise<
	CreatePortalLinkResult | DryRunResult<{ method: string; path: string; body: JsonRecord }>
> {
	const organization = requireString(input.organization, 'organization')
	const intent = requireIntent(input.intent)
	const body = compactRecord({
		organization,
		intent,
		return_url: optionalString(input.returnUrl, 'returnUrl'),
		success_url: optionalString(input.successUrl, 'successUrl'),
	})
	if (input.dryRun) {
		return { dryRun: true, wouldCall: { method: 'POST', path: '/portal/generate_link', body } }
	}
	if (input.confirm !== true) {
		throw new Error(
			'Creating a WorkOS Admin Portal link requires confirm: true after explicit user approval, or dryRun: true.',
		)
	}
	const result = await workosRequest<unknown>({
		...input,
		operation: 'portal.write',
		method: 'POST',
		path: '/portal/generate_link',
		body,
	})
	const record = result.data && typeof result.data === 'object' ? (result.data as JsonRecord) : {}
	const link = stringField(record.link)
	if (!link) {
		throw new Error('WorkOS did not return a portal link.')
	}
	return { link, intent, organization }
}

/**
 * Generate a WorkOS Admin Portal link. Requires `confirm: true`, or use
 * `dryRun: true`.
 * @example
 * import createPortalLink from 'kody:@kody/workos/create-portal-link'
 * const preview = await createPortalLink({
 *   organization: 'org_01EXAMPLE',
 *   intent: 'sso',
 *   dryRun: true,
 * })
 */
export default async function createPortalLinkEntrypoint(
	params: Partial<CreatePortalLinkInput> & Record<string, unknown> = {},
) {
	const input = requireRecord(params, 'create-portal-link')
	return createPortalLink({
		organization: requireString(input.organization, 'organization'),
		intent: requireIntent(input.intent),
		returnUrl: optionalString(input.returnUrl, 'returnUrl'),
		successUrl: optionalString(input.successUrl, 'successUrl'),
		account: optionalString(input.account, 'account'),
		secretName: optionalString(input.secretName, 'secretName'),
		confirm: input.confirm === true,
		dryRun: input.dryRun === true,
	})
}