Warning
This is prerelease software. APIs may change without notice.
A framework-agnostic authentication library for WorkOS with a modular adapter system for server-side rendered applications.
This library serves as the foundation for framework-specific WorkOS authentication packages like @workos-inc/authkit-nextjs
, @workos-inc/authkit-remix
, and @workos-inc/authkit-sveltekit
. By implementing the SessionStorage
interface for your framework, you can provide WorkOS authentication capabilities with minimal framework-specific code.
- Framework-agnostic core: Common authentication logic that works across platforms
- Adapter pattern: Simple interface for framework-specific implementations
- Session management: Secure encrypted cookie-based authentication
- JWT handling: Token validation, parsing, and refresh
- Organization switching: Switch user context between organizations
- Type-safe API: Full TypeScript support with custom claims
- Token claims parsing: Extract and validate JWT claims
# Using npm
npm install @workos/authkit-session
# Using pnpm
pnpm add @workos/authkit-session
# Using yarn
yarn add @workos/authkit-session
- Configure AuthKit with your WorkOS credentials:
import { configure, createAuthKitFactory } from '@workos/authkit-session';
configure({
clientId: 'your-client-id',
apiKey: 'your-workos-api-key',
redirectUri: 'https://yourdomain.com/auth/callback',
cookiePassword: 'must-be-at-least-32-characters-long-secret',
});
- Create a storage adapter for your framework:
import {
createAuthKitFactory,
CookieSessionStorage,
} from '@workos/authkit-session';
import type {
SessionStorage,
ConfigurationProvider,
} from '@workos/authkit-session';
// Option 1: Extend CookieSessionStorage (recommended)
class MyFrameworkStorage extends CookieSessionStorage<MyRequest, MyResponse> {
constructor(config: ConfigurationProvider) {
super(config);
}
async getSession(request: MyRequest): Promise<string | null> {
// Extract cookie from your framework's request object
const cookies = request.headers.cookie || '';
const match = cookies.match(new RegExp(`${this.cookieName}=([^;]+)`));
return match ? decodeURIComponent(match[1]) : null;
}
async saveSession(
response: MyResponse,
sessionData: string,
): Promise<MyResponse> {
// Set cookie on your framework's response object
const cookieValue = `${this.cookieName}=${encodeURIComponent(sessionData)}; ${this.getCookieAttributes()}`;
response.headers['Set-Cookie'] = cookieValue;
return response;
}
async clearSession(response: MyResponse): Promise<MyResponse> {
// Clear cookie by setting expired date
const expiredCookie = `${this.cookieName}=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/`;
response.headers['Set-Cookie'] = expiredCookie;
return response;
}
private getCookieAttributes(): string {
const attrs = [];
if (this.cookieOptions.path) attrs.push(`path=${this.cookieOptions.path}`);
if (this.cookieOptions.domain)
attrs.push(`domain=${this.cookieOptions.domain}`);
if (this.cookieOptions.maxAge)
attrs.push(`max-age=${this.cookieOptions.maxAge}`);
if (this.cookieOptions.httpOnly) attrs.push('httponly');
if (this.cookieOptions.secure) attrs.push('secure');
if (this.cookieOptions.sameSite)
attrs.push(`samesite=${this.cookieOptions.sameSite}`);
return attrs.join('; ');
}
}
// Option 2: Implement SessionStorage interface directly
class CustomStorage implements SessionStorage<MyRequest, MyResponse> {
async getSession(request: MyRequest): Promise<string | null> {
// Your custom session retrieval logic
return getSessionFromRequest(request);
}
async saveSession(
response: MyResponse,
sessionData: string,
): Promise<MyResponse> {
// Your custom session storage logic
return saveSessionToResponse(response, sessionData);
}
async clearSession(response: MyResponse): Promise<MyResponse> {
// Your custom session clearing logic
return clearSessionFromResponse(response);
}
}
// Create your AuthKit instance
const authKit = createAuthKitFactory<MyRequest, MyResponse>({
sessionStorageFactory: config => new MyFrameworkStorage(config),
});
- Use AuthKit in your application:
// Validate a session
const { user, claims, sessionId, impersonator, accessToken, refreshToken } =
await authKit.withAuth(request);
// Note: ensureSignedIn option not yet implemented
// Framework adapters should handle redirects when user is null
// Generate an authorization URL
const authUrl = await authKit.getAuthorizationUrl({
returnPathname: '/dashboard',
redirectUri: 'https://yourdomain.com/auth/callback',
screenHint: 'sign-in', // or 'sign-up'
});
// Generate sign-in/sign-up URLs
const signInUrl = await authKit.getSignInUrl({
redirectUri: 'https://yourdomain.com/auth/callback',
});
const signUpUrl = await authKit.getSignUpUrl({
redirectUri: 'https://yourdomain.com/auth/callback',
});
// Get token claims from current session
const claims = await authKit.getTokenClaims(request);
// Or parse specific access token
const specificClaims = await authKit.getTokenClaims(request, accessToken);
// Switch to different organization
const { response: updatedResponse, authResult: newAuth } =
await authKit.switchToOrganization(request, response, 'org_123');
// Handle OAuth callback
const {
response: updatedResponse,
returnPathname,
authResponse,
} = await authKit.handleCallback(request, response, {
code: 'oauth_code_from_query',
state: 'optional_state_from_query',
});
// Sign out (simple session termination)
const clearedResponse = await authKit.signOut(request, response, {
returnTo: 'https://yourdomain.com',
});
// Terminate session and get logout URL (advanced)
const { response: clearedResponse, logoutUrl } = await authKit.terminateSession(
session,
response,
{ returnTo: 'https://yourdomain.com' },
);
// Manually save a session (for custom auth flows)
await authKit.saveSession(response, authResponse);
// Manually refresh session
const refreshedAuth = await authKit.refreshSession(session, organizationId);
If you're building a framework-specific package (like @workos-inc/authkit-sveltekit
), this library provides everything needed for WorkOS authentication:
- Install and configure
@workos/authkit-session
as a dependency - Create a SessionStorage adapter for your framework's request/response objects
- Export framework-specific helpers that wrap the core AuthKit functionality
- Handle routing for sign-in, callback, and sign-out endpoints
// src/storage.ts - Framework-specific storage adapter
import { CookieSessionStorage } from '@workos/authkit-session';
import type { ConfigurationProvider } from '@workos/authkit-session';
export class SvelteKitStorage extends CookieSessionStorage<RequestEvent, void> {
// Implement framework-specific cookie handling
}
// src/index.ts - Main exports
import { configure, createAuthKitFactory } from '@workos/authkit-session';
import { SvelteKitStorage } from './storage';
export { configure };
export const authKit = createAuthKitFactory({
sessionStorageFactory: config => new SvelteKitStorage(config),
});
// Framework-specific helpers
export async function withAuth(event: RequestEvent) {
return authKit.withAuth(event);
}
export async function handleCallback(event: RequestEvent) {
const url = new URL(event.request.url);
const code = url.searchParams.get('code');
const state = url.searchParams.get('state');
if (!code) throw new Error('Missing authorization code');
const result = await authKit.handleCallback(event, undefined, {
code,
state,
});
// Handle redirect to returnPathname
}
- Request/Response Objects: Adapt your framework's request/response objects to the
SessionStorage
interface - Cookie Handling: Implement secure cookie setting/getting for your framework
- Routing Integration: Provide helpers for authentication routes (
/auth/signin
,/auth/callback
,/auth/signout
) - TypeScript Support: Export proper types for your framework's patterns
- Error Handling: Wrap AuthKit errors in framework-appropriate error types
AuthKit SSR uses encrypted cookies to store session information. It handles:
- Token encryption/decryption (using iron-webcrypto)
- JWT validation and parsing
- Session refresh logic
- Session termination
AuthKit uses an adapter pattern to abstract framework-specific request/response handling. This allows the core authentication logic to remain framework-agnostic while enabling support for any server-side framework.
interface SessionStorage<TRequest, TResponse, TOptions = unknown> {
getSession(request: TRequest): Promise<string | null>;
saveSession(
response: TResponse,
sessionData: string,
options?: TOptions,
): Promise<TResponse>;
clearSession(response: TResponse, options?: TOptions): Promise<TResponse>;
}
For cookie-based session storage, extend the provided CookieSessionStorage
class:
import { CookieSessionStorage } from '@workos/authkit-session';
abstract class CookieSessionStorage<TRequest, TResponse> {
protected cookieName: string; // From config: cookieName
protected cookieOptions: CookieOptions; // Derived from config
// Implement these methods for your framework
abstract getSession(request: TRequest): Promise<string | null>;
abstract saveSession(
response: TResponse,
sessionData: string,
): Promise<TResponse>;
abstract clearSession(response: TResponse): Promise<TResponse>;
}
interface CookieOptions {
path?: string; // Default: '/'
domain?: string; // From config: cookieDomain
maxAge?: number; // From config: cookieMaxAge (400 days)
httpOnly?: boolean; // Default: true
secure?: boolean; // From config: apiHttps
sameSite?: 'lax' | 'strict' | 'none'; // From config: cookieSameSite
}
Express/Node.js:
class ExpressStorage extends CookieSessionStorage<Request, Response> {
async getSession(request: Request): Promise<string | null> {
return request.cookies[this.cookieName] || null;
}
async saveSession(
response: Response,
sessionData: string,
): Promise<Response> {
response.cookie(this.cookieName, sessionData, this.cookieOptions);
return response;
}
async clearSession(response: Response): Promise<Response> {
response.clearCookie(this.cookieName, { path: this.cookieOptions.path });
return response;
}
}
Hono:
class HonoStorage extends CookieSessionStorage<HonoRequest, HonoResponse> {
async getSession(request: HonoRequest): Promise<string | null> {
return getCookie(request, this.cookieName) || null;
}
async saveSession(
response: HonoResponse,
sessionData: string,
): Promise<HonoResponse> {
setCookie(response, this.cookieName, sessionData, this.cookieOptions);
return response;
}
async clearSession(response: HonoResponse): Promise<HonoResponse> {
deleteCookie(response, this.cookieName);
return response;
}
}
When creating an adapter for a new framework:
- Choose your approach: Extend
CookieSessionStorage
for cookie-based storage, or implementSessionStorage
directly for custom storage - Handle framework request/response objects: Extract cookies from requests and set cookies on responses
- Respect configuration: Use
this.cookieName
andthis.cookieOptions
from the base class - Test thoroughly: Ensure session persistence works across requests
const authKit = createAuthKitFactory<MyRequest, MyResponse>({
sessionStorageFactory: config => new MyFrameworkStorage(config),
// Optional: Custom session encryption
sessionEncryptionFactory: config => myCustomEncryption,
// Optional: Custom WorkOS client
clientFactory: config => myCustomWorkOSClient,
});
AuthKit can be configured in multiple ways:
WORKOS_CLIENT_ID=your-client-id
WORKOS_API_KEY=your-api-key
WORKOS_REDIRECT_URI=https://yourdomain.com/auth/callback
WORKOS_COOKIE_PASSWORD=must-be-at-least-32-characters-long
WORKOS_COOKIE_NAME=wos-session
WORKOS_COOKIE_MAX_AGE=34560000
WORKOS_API_HOSTNAME=api.workos.com
WORKOS_API_HTTPS=true
WORKOS_API_PORT=443
import { configure } from '@workos/authkit-session';
configure({
clientId: 'your-client-id',
apiKey: 'your-api-key',
redirectUri: 'https://yourdomain.com/auth/callback',
cookiePassword: 'must-be-at-least-32-characters-long',
cookieName: 'wos-session', // Default: 'wos-session'
cookieMaxAge: 60 * 60 * 24 * 400, // 400 days in seconds
cookieSameSite: 'lax', // 'strict', 'lax', or 'none'
cookieDomain: '.yourdomain.com', // Optional: cookie domain
apiHostname: 'api.workos.com', // Optional: API hostname
apiHttps: true, // Default: true
apiPort: 443, // Optional: API port
});
configure(config)
: Set up AuthKit with your WorkOS configurationgetConfig(key)
: Get a specific configuration valuecreateAuthKitFactory(options)
: Create an instance of AuthKit for your framework
withAuth<TCustomClaims>(request, options?)
: Validate the current session and returnAuthResult<TCustomClaims>
- Note:
ensureSignedIn
option not yet implemented - framework adapters should handle redirects
- Note:
handleCallback(request, response, { code, state? })
: Process OAuth callback and create sessionsignOut(request, response, options?)
: Simple session termination with cookie clearingterminateSession(session, response, options?)
: Advanced session termination with logout URLsaveSession(response, authResponse)
: Manually save session fromAuthenticationResponse
refreshSession(session, organizationId?)
: Manually refresh session tokens
getAuthorizationUrl(options)
: Generate a WorkOS authorization URL withreturnPathname
,redirectUri
, andscreenHint
getSignInUrl(options)
: Generate a sign-in URL withorganizationId
,loginHint
, andredirectUri
getSignUpUrl(options)
: Generate a sign-up URL withorganizationId
,loginHint
, andredirectUri
getTokenClaims<TCustomClaims>(request, accessToken?)
: Parse JWT token claims from session or specific tokenswitchToOrganization(request, response, organizationId)
: Switch user to different organization context
Switch users between organizations without requiring re-authentication:
try {
const { response: updatedResponse, authResult } =
await authKit.switchToOrganization(
request,
response,
'org_new_organization_id',
);
// Use the updated response and new auth context
const { user, sessionId, claims } = authResult;
} catch (error) {
if (error.authUrl) {
// Handle cases requiring re-authentication (SSO, MFA)
redirect(error.authUrl);
}
}
Extract and validate JWT claims from access tokens:
// Parse claims from current session
const claims = await authKit.getTokenClaims<MyCustomClaims>(request);
// Parse claims from specific token
const specificClaims = await authKit.getTokenClaims<MyCustomClaims>(
request,
accessToken,
);
// Custom claims interface
interface MyCustomClaims {
custom_field: string;
user_metadata: Record<string, unknown>;
}
AuthKit provides full TypeScript support with generic type parameters:
interface CustomClaims {
department: string;
permissions: string[];
}
const { user, claims } = await authKit.withAuth<CustomClaims>(request);
// claims is typed as BaseTokenClaims & CustomClaims
The withAuth
method returns an AuthResult
object with the following structure:
interface AuthResult<TCustomClaims = Record<string, unknown>> {
user?: User | null; // WorkOS user object
claims?: BaseTokenClaims & TCustomClaims; // JWT token claims
impersonator?: Impersonator; // Impersonation context if any
accessToken?: string; // JWT access token
refreshToken?: string; // Refresh token
sessionId?: string; // Session identifier from claims
}
interface BaseTokenClaims {
sid: string; // Session ID
org_id?: string; // Organization ID
role?: string; // User role
permissions?: string[]; // User permissions
entitlements?: string[]; // User entitlements
feature_flags?: string[]; // Feature flags
}
The library also exports these components for advanced use cases:
import {
SessionManager, // Core session management class
CookieSessionStorage, // Abstract cookie storage base class
getWorkOS, // WorkOS client factory
} from '@workos/authkit-session';
AuthKit uses iron-webcrypto for secure, encrypted cookies with the following security features:
- Encrypted cookies (AES-256-CBC)
- HMAC validation (SHA-256)
- Customizable cookie settings (HttpOnly, SameSite, etc.)
- Token refresh mechanism
MIT