Release v8.0.0: Universal Runtime Support & API Modernization (#1370)
## Overview
Version 8 is a major release focused on **universal runtime
compatibility**, **PKCE authentication support**, and **API
modernization**. The SDK now works seamlessly across Node.js, Deno, Bun,
and Cloudflare Workers while removing long-deprecated APIs.
---
## BREAKING CHANGES
### Runtime & Build System
**1. Node.js Version Requirement** (BREAKING)
- **Changed:** Minimum Node.js version: `16` → `20`
- **Reason:** Node 18 reached end-of-life in April 2025
- **Files:** `package.json` (`engines.node`)
- **Migration:** Update to Node.js 20 or higher
**2. Package Type Change to ESM-First** (BREAKING)
- **Changed:** `package.json` now includes `"type": "module"`
- **Impact:** The package is now ESM-first with dual CJS/ESM exports
- **Files:** `package.json`
- **Migration:** Most users won't need changes due to conditional
exports, but projects with custom build configurations may need
adjustments
**3. Build System Migration** (Internal)
- **Changed:** Migrated from `tsc` to `tsdown` (Rolldown/Oxc-based) for
building
- **Added:** `tsdown.config.ts` with dual-config approach:
- **ESM build:** Unbundled with external dependencies (optimal for
tree-shaking)
- **CJS build:** Bundles ESM-only dependencies (`iron-webcrypto`,
`uint8array-extras`) for compatibility
- **Impact:** Faster builds, lower memory usage, improved tree-shaking,
better compatibility with various bundlers and package managers
### Removed Internal Classes (BREAKING)
**4. HTTP Client Removal** (BREAKING)
- **Removed:** `NodeHttpClient` class and `createHttpClient()` method
- **Files:** `src/common/net/node-client.ts` (deleted)
- **Impact:** Only affects deep imports into internal modules (never
part of public API)
- **Migration:** Use `WorkOS` instance instead - HTTP is handled
automatically
**5. Crypto Provider Removal** (BREAKING)
- **Removed:** `NodeCryptoProvider`, `IronSessionProvider`,
`EdgeIronSessionProvider`, `WebIronSessionProvider`
- **Changed:** Now using `iron-webcrypto` v2 directly with lightweight
wrapper
- **Files:** `src/common/crypto/node-crypto-provider.ts` (deleted),
`src/common/iron-session/*` (deleted), `src/common/crypto/seal.ts`
(added)
- **Impact:** Only affects deep imports into internal modules
- **Migration:** Use `WorkOS` instance methods - crypto is handled
automatically
### Directory Sync (BREAKING)
**6. DirectoryUser Interface Changes** (BREAKING)
- **Removed fields:**
- `emails` → Use `customAttributes.emails`
- `username` → Use `customAttributes.username`
- `jobTitle` → Use `customAttributes.jobTitle`
- **Removed utility:** `getPrimaryEmail()` function
- **Files:**
`src/directory-sync/interfaces/directory-user.interface.ts`,
`src/directory-sync/utils/get-primary-email.ts` (deleted)
- **Migration:**
```typescript
// v7
user.emails
user.username
user.jobTitle
// v8
user.customAttributes?.emails
user.customAttributes?.username
user.customAttributes?.jobTitle
```
### User Management (BREAKING)
**7. AuthorizationURLOptions Changes** (BREAKING)
- **Removed:** `context` field (no longer supported)
- **Files:**
`src/user-management/interfaces/authorization-url-options.interface.ts`
- **Migration:** Remove the `context` parameter from authorization URL
calls
**8. Removed Deprecated Methods** (BREAKING)
- **Removed:**
- `sendMagicAuthCode()` → Use `userManagement.sendMagicCode()` instead
- `sendPasswordResetEmail()` → Use
`userManagement.sendPasswordResetEmail()` instead
- `refreshAndSealSessionData()` → Use new session helper methods instead
- **Files:** `src/user-management/user-management.ts`, serializers
removed
**9. listOrganizationMemberships Requires userId or organizationId**
(BREAKING)
- **Changed:** `listOrganizationMemberships()` now requires either
`userId` or `organizationId`
- **Files:** `src/user-management/user-management.ts`
- **Migration:**
```typescript
// v7 - Could call without parameters
workos.userManagement.listOrganizationMemberships();
// v8 - Must specify userId or organizationId
workos.userManagement.listOrganizationMemberships({ userId: 'user_123'
});
// OR
workos.userManagement.listOrganizationMemberships({ organizationId:
'org_456' });
```
### SSO (BREAKING)
**10. SSOAuthorizationURLOptions Type Changes** (BREAKING)
- **Changed:** Converted to discriminated union for type safety
- **Removed:** `domain` field (deprecated)
- **Files:** `src/sso/interfaces/authorization-url-options.interface.ts`
- **Impact:** Stricter TypeScript types - must now specify exactly one
of: `connection`, `organization`, or `provider`
- **Migration:**
```typescript
// v7 - Multiple options allowed
{ connection: 'conn_123', organization: 'org_456' } // Both accepted
// v8 - Mutually exclusive (enforced by types)
{ connection: 'conn_123' } // OR
{ organization: 'org_456' } // OR
{ provider: 'GoogleOAuth' }
```
**11. Connection Interface** (BREAKING)
- **Removed:** Several deprecated internal fields
- **Files:** `src/sso/interfaces/connection.interface.ts`
- **Migration:** Use only documented fields
### MFA (BREAKING)
**12. Method Removal** (BREAKING)
- **Removed:** `verifyFactor()` method
- **Files:** `src/mfa/mfa.ts`,
`src/mfa/interfaces/verify-factor-options.ts` (deleted)
- **Migration:** Use `verifyChallenge()` instead (same functionality)
```typescript
// v7
await workos.mfa.verifyFactor({ authenticationFactorId, code });
// v8
await workos.mfa.verifyChallenge({ authenticationFactorId, code });
```
### Organizations (BREAKING)
**13. Organization Options Changes** (BREAKING)
- **Removed from CreateOrganizationOptions and
UpdateOrganizationOptions:**
- `allowProfilesOutsideOrganization`
- `domains` (use `domainData` instead)
- **Files:** `src/organizations/interfaces/*.interface.ts`
- **Migration:** Remove these fields from organization creation/update
calls
**14. Organization Domain Enum** (BREAKING)
- **Removed:** `LegacyVerified` from `OrganizationDomainState` enum
- **Files:**
`src/organizations/interfaces/organization-domain.interface.ts`
- **Migration:** Use `Verified` instead
### Events (BREAKING)
**15. Event Type Removals** (BREAKING)
- **Removed event interfaces:**
- `DsyncDeactivatedEvent` → Use `dsync.deleted` instead
- `OrganizationMembershipAdded` → Not applicable in v8
- `OrganizationMembershipRemoved` → Not applicable in v8
- **Files:** `src/common/interfaces/event.interface.ts`
- **Migration:**
```typescript
// v7
if (event.event === 'dsync.deactivated') { }
// v8
if (event.event === 'dsync.deleted') { }
```
### Vault (BREAKING)
**16. Removed Deprecated Method Aliases** (BREAKING)
- **Removed methods:**
- `createSecret()` → Use `createObject()` instead
- `listSecrets()` → Use `listObjects()` instead
- `listSecretVersions()` → Use `listObjectVersions()` instead
- `readSecret()` → Use `readObject()` instead
- `describeSecret()` → Use `describeObject()` instead
- `updateSecret()` → Use `updateObject()` instead
- `deleteSecret()` → Use `deleteObject()` instead
- **Files:** `src/vault/vault.ts`
- **Migration:** Replace all `*Secret` methods with `*Object`
equivalents
### Webhooks (NON-BREAKING)
**17. constructEvent Payload Typing** (NON-BREAKING)
- **Changed:** `constructEvent` payload parameter typed as
`Record<string, unknown>` for better type safety
- **Files:** `src/webhooks/webhooks.ts`
- **Impact:** Stricter typing, but backwards compatible
---
## NEW FEATURES
**18. PKCE Authentication Support** (NON-BREAKING)
- **Added:** Full PKCE (Proof Key for Code Exchange) support for public
and confidential clients
- **API key now optional:** Initialize with just `clientId` for
PKCE-only mode
- **New methods:**
- `userManagement.getAuthorizationUrlWithPKCE()` - generates PKCE
internally, returns `{ url, state, codeVerifier }`
- `userManagement.authenticateWithCodeAndVerifier()` - explicit PKCE
token exchange
- `workos.pkce.generate()` - manual PKCE generation
- **Enhanced:** `authenticateWithCode()` auto-detects client mode based
on credentials
- **Example:**
```typescript
import { WorkOS } from '@workos-inc/node';
// Public client (no API key)
const workos = new WorkOS({ clientId: 'client_123' });
// Generate authorization URL with PKCE
const { url, state, codeVerifier } = await
workos.userManagement.getAuthorizationUrlWithPKCE({
redirectUri: 'myapp://callback',
provider: 'authkit',
});
// Exchange code for tokens
const { accessToken, refreshToken, user } = await
workos.userManagement.authenticateWithCode({
code: authCode,
codeVerifier,
});
```
**19. createWorkOS Factory Function** (NON-BREAKING)
- **Added:** `createWorkOS()` factory with compile-time type safety for
public vs confidential clients
- **Files:** `src/workos.ts`
- **Benefit:** TypeScript will error at compile time if you try to use
methods unavailable to public clients
- **Example:**
```typescript
import { createWorkOS } from '@workos-inc/node';
// Public client - only PKCE methods available
const publicClient = createWorkOS({ clientId: 'client_123' });
publicClient.userManagement.getAuthorizationUrlWithPKCE({ ... }); // ✅
Works
publicClient.userManagement.listUsers(); // ❌ TypeScript error
// Confidential client - full access
const serverClient = createWorkOS({ apiKey: 'sk_...', clientId:
'client_123' });
serverClient.userManagement.listUsers(); // ✅ Works
```
**20. Universal Runtime Support** (NON-BREAKING)
- **Improved:** Better support for Deno, Bun, and Cloudflare Workers
- **Added:** Conditional exports in package.json for runtime-specific
entry points
- **Files:** `package.json` (exports), `src/index.worker.ts`
- **Example:**
```typescript
// Cloudflare Workers
import { WorkOS } from '@workos-inc/node/worker';
// Deno
import { WorkOS } from 'npm:@workos-inc/node';
// Bun (same as Node.js)
import { WorkOS } from '@workos-inc/node';
```
**21. Environment Variable Helper** (NON-BREAKING)
- **Added:** Internal `getEnv()` helper for better cross-runtime
environment variable access
- **Files:** `src/common/utils/env.ts`
- **Benefit:** Works consistently across Node.js, Deno, Cloudflare
Workers, etc.
**22. Pagination Improvements** (NON-BREAKING)
- **Changed:** `AutoPaginatable` now properly defaults
`PaginationOptions` generic parameter
- **Files:** `src/common/utils/pagination.ts`
- **Benefit:** Better TypeScript inference and type safety
**23. Runtime Analytics** (NON-BREAKING)
- **Added:** Runtime information included in User-Agent string for
better debugging
- **Files:** `src/workos.ts`
- **Impact:** Helps identify runtime-specific issues
---
## INTERNAL IMPROVEMENTS
**24. Linting Migration** (Internal)
- **Changed:** Migrated from TSLint to ESLint with flat config
- **Added:** `eslint.config.mjs`
- **Removed:** `tslint.json`
- **Impact:** Better code quality checks, modern linting setup
**25. Runtime Testing** (Internal)
- **Added:** New GitHub Actions workflow for testing across multiple
runtimes
- **Added:** Ecosystem check script (`scripts/ecosystem-check.ts`)
- **Files:** `.github/workflows/runtime-tests.yml`
- **Benefit:** Ensures SDK works correctly in all supported runtimes
**26. Dependency Updates** (Internal)
- **Updated:** All dependencies to latest compatible versions
- **Updated:** Development tooling (Jest 30, TypeScript 5.9, etc.)
- **Removed:** External `leb` and `qs` packages - replaced with internal
vanilla implementations
- **Files:** `package.json`, `package-lock.json`
**27. Test Infrastructure** (Internal)
- **Updated:** Jest configuration migrated to CommonJS
- **Added:** Jest ESM transform
- **Files:** `jest.config.cjs`, `jest-transform-esm.cjs`
- **Updated:** Worker test environment with latest Miniflare
**28. Dynamic jose Import** (Internal)
- **Added:** Dynamic import wrapper for `jose` to support Node.js
20.15-20.18 compatibility
- **Files:** `src/common/utils/jose.ts`
- **Benefit:** Wider Node.js version compatibility
**29. Runtime-Agnostic UUIDs** (Internal)
- **Changed:** Now uses `globalThis.crypto.randomUUID` instead of
Node-specific crypto
- **Files:** Various
- **Benefit:** Better cross-runtime compatibility
---
## REMOVED (Previously Announced)
**30. `/client` Entry Point Removed** (BREAKING)
- **Removed:** The `@workos-inc/node/client` entry point
- **Migration:** Use the main SDK without an API key instead:
```typescript
// Old approach (v8 beta)
import { userManagement } from '@workos-inc/node/client';
const url = userManagement.getAuthorizationUrl({ ... });
// New approach (v8 final)
import { WorkOS } from '@workos-inc/node';
const workos = new WorkOS({ clientId: 'client_...' });
const url = workos.userManagement.getAuthorizationUrl({ ... });
```
---
## Files Changed Summary
- **Major additions**: PKCE support, createWorkOS factory,
runtime-specific exports, environment helper
- **Major deletions**: Internal HTTP/crypto providers, deprecated
methods, legacy event types, `/client` entry point
- **Configuration**: New build system (tsdown), new linting (ESLint),
updated Jest
---
## Migration Guide
See [`docs/V8_MIGRATION_GUIDE.md`](./docs/V8_MIGRATION_GUIDE.md) for
detailed migration instructions.
---
## Testing
- All existing tests updated and passing
- Runtime compatibility tests added
- Worker environment tests updated
- Ecosystem check script validates multi-runtime support
---------
Co-authored-by: Kendall Strautman Swarthout <36613477+kendallstrautman@users.noreply.github.com>
Co-authored-by: Matt Dzwonczyk <9063128+mattgd@users.noreply.github.com>
Co-authored-by: William <william@workos.com>
Co-authored-by: Julius Marminge <jmarminge@gmail.com> N
Nick Nisi committed
6d370ba0044f986f55725ff7bd61ace8fcf92665
Parent: 3bfab22
Committed by GitHub <noreply@github.com>
on 1/12/2026, 4:47:20 PM