|
3 | 3 | > [!WARNING] |
4 | 4 | > This is prerelease software. APIs may change without notice. |
5 | 5 |
|
6 | | -**A toolkit library for building WorkOS AuthKit integrations.** |
| 6 | +**Framework-agnostic authentication library for WorkOS.** |
7 | 7 |
|
8 | | -This library extracts the complex business logic (JWT verification, session encryption, token refresh orchestration) that WorkOS SDK authors need when building framework-specific authentication packages like `@workos-inc/authkit-nextjs`, `@workos-inc/authkit-tanstack-start`, and `@workos-inc/authkit-sveltekit`. |
| 8 | +Provides the business logic for WorkOS authentication (JWT verification, session encryption, token refresh orchestration) with a pluggable storage adapter pattern. Build framework-specific packages like `@workos-inc/authkit-nextjs` or `@workos-inc/authkit-tanstack-start` by implementing the storage interface. |
9 | 9 |
|
10 | | -## Philosophy |
| 10 | +## What This Library Provides |
11 | 11 |
|
12 | | -**We provide the hard stuff:** |
| 12 | +**Authentication business logic:** |
| 13 | +- JWT verification against JWKS (with caching) |
| 14 | +- Session encryption/decryption (iron-webcrypto, AES-256-CBC) |
| 15 | +- Token refresh orchestration (automatic expiry detection, org context preservation) |
| 16 | +- WorkOS API operations (signOut, refreshSession, authorization URLs) |
13 | 17 |
|
14 | | -- Token verification with JWKS and caching |
15 | | -- Session encryption/decryption (iron-webcrypto) |
16 | | -- Token refresh orchestration (when to refresh, how to preserve org context) |
17 | | -- WorkOS API integration helpers (signOut, refreshSession, authorization URLs) |
18 | | -- Cookie building with secure defaults |
| 18 | +**Framework integration helpers:** |
| 19 | +- `CookieSessionStorage` base class with secure defaults |
| 20 | +- Storage adapter pattern via `SessionStorage<TRequest, TResponse>` interface |
19 | 21 | - Configuration management (environment variables, validation) |
20 | 22 |
|
21 | | -**You own the integration:** |
22 | | - |
23 | | -- Implement `updateSession/withAuth` patterns that fit your framework |
24 | | -- Handle request context (headers, locals, WeakMap) your way |
25 | | -- Add framework-specific features (callbacks, debug logging, eager auth) |
26 | | -- Control middleware and route handler patterns |
27 | | - |
28 | | -**Result:** Share 80% of the complexity, keep 100% control over framework integration. |
| 23 | +**What frameworks implement:** |
| 24 | +- Storage adapter for their request/response objects |
| 25 | +- Middleware pattern for auth validation and token refresh |
| 26 | +- Request context handling (headers, locals, WeakMap - whatever fits) |
| 27 | +- Framework-specific features (callbacks, debug logging, route helpers) |
29 | 28 |
|
30 | 29 | ## Features |
31 | 30 |
|
32 | | -- **Business Logic Extraction**: JWT, crypto, and refresh logic in one place |
33 | | -- **Framework-Agnostic Primitives**: Works in any JavaScript environment (Node.js, edge, Deno) |
34 | | -- **Toolkit, Not Framework**: Use what you need, implement patterns your way |
35 | | -- **Type-Safe**: Full TypeScript support with custom claims and generics |
| 31 | +- **Framework-Agnostic**: Works with any server-side JavaScript framework |
| 32 | +- **Pluggable Storage**: Implement storage adapter for your framework's request/response objects |
| 33 | +- **Type-Safe**: Full TypeScript support with discriminated unions and custom claims |
36 | 34 | - **Production-Ready**: 80%+ test coverage, comprehensive error handling |
37 | | -- **Zero Framework Opinions**: No prescribed middleware/route patterns |
| 35 | +- **Secure by Default**: AES-256-CBC encryption, HttpOnly cookies, JWKS verification |
38 | 36 |
|
39 | 37 | ## Architecture |
40 | 38 |
|
41 | | -This library provides a **three-layer toolkit** for building authentication: |
42 | | - |
43 | | -### Layer 1: Core Business Logic (`AuthKitCore`) |
44 | | - |
45 | | -Pure functions for the hard stuff - no framework dependencies: |
| 39 | +The library uses a clean internal architecture with three layers: |
46 | 40 |
|
47 | | -- **Token Operations** |
48 | | - - `verifyToken()` - JWT verification against JWKS (with caching) |
49 | | - - `isTokenExpiring()` - Check if token needs refresh soon |
50 | | - - `parseTokenClaims()` - Extract and decode JWT claims |
| 41 | +### AuthService (Public API) |
51 | 42 |
|
52 | | -- **Session Encryption** |
53 | | - - `encryptSession()` - AES-256-CBC encryption for cookies |
54 | | - - `decryptSession()` - Decrypt and validate session data |
| 43 | +The main interface frameworks interact with. Provides methods for: |
55 | 44 |
|
56 | | -- **Refresh Orchestration** |
57 | | - - `refreshTokens()` - Call WorkOS API to refresh tokens |
58 | | - - `validateAndRefresh()` - **Core orchestration**: validate token → refresh if expiring |
| 45 | +- **Session Management**: `withAuth()` - Validate and auto-refresh sessions |
| 46 | +- **OAuth Flow**: `handleCallback()` - Process authentication callbacks |
| 47 | +- **User Actions**: `signOut()`, `switchOrganization()`, `refreshSession()` |
| 48 | +- **URL Generation**: `getSignInUrl()`, `getSignUpUrl()`, `getAuthorizationUrl()` |
59 | 49 |
|
60 | 50 | ```typescript |
61 | | -import { AuthKitCore } from '@workos/authkit-session'; |
| 51 | +import { createAuthService } from '@workos/authkit-session'; |
62 | 52 |
|
63 | | -const core = new AuthKitCore(config, client, encryption); |
| 53 | +const authService = createAuthService({ |
| 54 | + sessionStorageFactory: (config) => new MyFrameworkStorage(config), |
| 55 | +}); |
64 | 56 |
|
65 | | -// Validate and potentially refresh in one call |
66 | | -const { valid, refreshed, session, claims } = |
67 | | - await core.validateAndRefresh(session); |
| 57 | +const { auth, refreshedSessionData } = await authService.withAuth(request); |
68 | 58 | ``` |
69 | 59 |
|
70 | | -### Layer 2: WorkOS Operations (`AuthOperations`) |
71 | | - |
72 | | -High-level operations combining core logic with WorkOS API calls: |
73 | | - |
74 | | -- **Sign Out**: `signOut()` - Generate logout URL + cookie clear header |
75 | | -- **Refresh Session**: `refreshSession()` - Refresh tokens with org context preservation |
76 | | -- **Switch Organization**: `switchOrganization()` - Refresh with new org ID |
77 | | -- **URL Generation**: `getAuthorizationUrl()`, `getSignInUrl()`, `getSignUpUrl()` |
78 | | - |
79 | | -```typescript |
80 | | -import { AuthOperations } from '@workos/authkit-session'; |
81 | | - |
82 | | -const operations = new AuthOperations(core, client, config); |
| 60 | +### Internal Implementation |
83 | 61 |
|
84 | | -// Sign out returns everything you need |
85 | | -const { logoutUrl, clearCookieHeader } = await operations.signOut(sessionId, { |
86 | | - returnTo: '/', |
87 | | -}); |
88 | | -``` |
| 62 | +AuthService delegates to internal layers (you don't need to interact with these directly): |
89 | 63 |
|
90 | | -### Layer 3: Helpers |
| 64 | +- **`AuthKitCore`** - JWT verification, session encryption, token refresh logic |
| 65 | +- **`AuthOperations`** - WorkOS API calls with proper error handling |
91 | 66 |
|
92 | | -- **`CookieSessionStorage`** - Base class with secure cookie defaults |
93 | | -- **`ConfigurationProvider`** - Environment variable mapping and validation |
| 67 | +These are exported for advanced use cases, but most frameworks only need AuthService. |
94 | 68 |
|
95 | | -### Orchestration (Your Choice) |
| 69 | +### Storage Adapter Pattern |
96 | 70 |
|
97 | | -- **`AuthService`** - One orchestration pattern (used by `@workos/authkit-tanstack-start`) |
98 | | -- **Build your own** - Use Core + Operations directly with custom orchestration |
| 71 | +Frameworks implement the `SessionStorage<TRequest, TResponse>` interface: |
99 | 72 |
|
100 | | -**Key Design Decision:** We don't force context methods (`getSessionFromContext`) into interfaces. Each framework handles request context differently: |
| 73 | +```typescript |
| 74 | +class MyFrameworkStorage extends CookieSessionStorage<Request, Response> { |
| 75 | + async getSession(request: Request): Promise<string | null> { |
| 76 | + // Extract encrypted session from cookies |
| 77 | + } |
101 | 78 |
|
102 | | -- Next.js: Mutable headers (`request.headers.set()`) |
103 | | -- TanStack Start: WeakMap (immutable requests) |
104 | | -- SvelteKit: `event.locals` |
105 | | -- Remix: Context objects |
| 79 | + protected async applyHeaders( |
| 80 | + response: Response | undefined, |
| 81 | + headers: Record<string, string>, |
| 82 | + ): Promise<{ response: Response }> { |
| 83 | + // Apply Set-Cookie headers to response |
| 84 | + } |
| 85 | +} |
| 86 | +``` |
106 | 87 |
|
107 | | -Let frameworks implement context passing idiomatically. |
| 88 | +The library handles everything else: encryption, JWT validation, refresh logic, WorkOS API calls. |
108 | 89 |
|
109 | 90 | ## Installation |
110 | 91 |
|
@@ -315,9 +296,9 @@ const result = await authService.switchOrganization(session, 'org_123'); |
315 | 296 |
|
316 | 297 | If you're building a framework-specific package (like `@workos/authkit-tanstack-start`), this library provides all the business logic you need. You just add framework-specific glue. |
317 | 298 |
|
318 | | -### Option 1: Use AuthService |
| 299 | +### Standard Approach: Use AuthService |
319 | 300 |
|
320 | | -One orchestration approach is `createAuthService()` with a storage adapter (used by `@workos/authkit-tanstack-start`): |
| 301 | +Create an AuthService instance with your storage adapter: |
321 | 302 |
|
322 | 303 | ```typescript |
323 | 304 | // src/storage.ts - Your framework's storage adapter |
@@ -443,9 +424,9 @@ export const authMiddleware = () => { |
443 | 424 |
|
444 | 425 | **Why this matters:** If you don't apply the `Set-Cookie` header, refreshed tokens stay in memory but never persist to the cookie. This causes infinite refresh loops because the next request has the old (expired) token. |
445 | 426 |
|
446 | | -### Option 2: Use Core + Operations Directly |
| 427 | +### Advanced: Use Core + Operations Directly |
447 | 428 |
|
448 | | -Build your own orchestration using the toolkit primitives: |
| 429 | +For specialized use cases, you can use the internal layers directly: |
449 | 430 |
|
450 | 431 | ```typescript |
451 | 432 | import { |
@@ -727,18 +708,15 @@ configure({ |
727 | 708 | - `getConfig(key)`: Get a specific configuration value |
728 | 709 | - `getConfigurationProvider()`: Get the ConfigurationProvider instance |
729 | 710 |
|
730 | | -#### Toolkit Classes |
731 | | - |
732 | | -**Core primitives:** |
733 | | - |
734 | | -- **`AuthKitCore`** - Pure business logic (JWT, crypto, refresh orchestration) |
735 | | -- **`AuthOperations`** - WorkOS API operations (signOut, refreshSession, authorization URLs) |
736 | | -- **`CookieSessionStorage`** - Base class for cookie-based session storage |
| 711 | +#### Main API |
737 | 712 |
|
738 | | -**Orchestration (optional):** |
| 713 | +- **`createAuthService(options)`** - Create an AuthService instance with your storage adapter |
| 714 | +- **`AuthService`** - Main authentication interface (session validation, OAuth, sign out, URLs) |
| 715 | +- **`CookieSessionStorage`** - Base class for implementing storage adapters |
739 | 716 |
|
740 | | -- **`AuthService`** - One orchestration pattern (used by `@workos/authkit-tanstack-start`) |
741 | | -- **`createAuthService(options)`** - Factory for creating AuthService instances |
| 717 | +**Advanced (internal layers):** |
| 718 | +- **`AuthKitCore`** - Internal: JWT verification, session encryption, refresh logic |
| 719 | +- **`AuthOperations`** - Internal: WorkOS API operations |
742 | 720 |
|
743 | 721 | See the [Architecture](#architecture) section for detailed usage. |
744 | 722 |
|
|
0 commit comments