Auth API
The @cloudwerk/auth package provides a comprehensive authentication system with providers, sessions, RBAC, multi-tenancy, and rate limiting.
Installation
Section titled “Installation”pnpm add @cloudwerk/authConfiguration API
Section titled “Configuration API”defineAuthConfig()
Section titled “defineAuthConfig()”Main configuration function for the auth system.
import { defineAuthConfig } from '@cloudwerk/auth/convention'
export default defineAuthConfig({ basePath?: string, session?: SessionConfig, cookies?: CookieConfig, pages?: PagesConfig, secret?: string,})AuthConfig
Section titled “AuthConfig”| Property | Type | Default | Description |
|---|---|---|---|
basePath | string | '/auth' | Base path for auth endpoints |
session | SessionConfig | See below | Session configuration |
cookies | CookieConfig | See below | Cookie configuration |
pages | PagesConfig | See below | Custom page paths |
secret | string | Required for JWT | Secret for signing tokens |
SessionConfig
Section titled “SessionConfig”interface SessionConfig { strategy: 'database' | 'jwt' // Session storage strategy maxAge?: number // Session lifetime in seconds (default: 30 days) updateAge?: number // Refresh interval in seconds (default: 24 hours)}CookieConfig
Section titled “CookieConfig”interface CookieConfig { sessionToken?: { name?: string // Cookie name (default: 'cloudwerk.session-token') options?: CookieOptions // Cookie options }}
interface CookieOptions { secure?: boolean // HTTPS only (default: true in production) httpOnly?: boolean // No JavaScript access (default: true) sameSite?: 'lax' | 'strict' | 'none' // (default: 'lax') path?: string // Cookie path (default: '/') domain?: string // Cookie domain}PagesConfig
Section titled “PagesConfig”interface PagesConfig { signIn?: string // Sign-in page (default: '/auth/signin') signOut?: string // Sign-out page (default: '/auth/signout') error?: string // Error page (default: '/auth/error') verifyRequest?: string // Email verification page newUser?: string // New user redirect}Provider API
Section titled “Provider API”defineProvider()
Section titled “defineProvider()”Wraps a provider configuration for use in Cloudwerk.
import { defineProvider } from '@cloudwerk/auth/convention'
export default defineProvider(providerConfig)OAuth Providers
Section titled “OAuth Providers”github()
Section titled “github()”import { github } from '@cloudwerk/auth/convention'
github({ clientId: string, clientSecret: string, scope?: string, // Default: 'read:user user:email'})google()
Section titled “google()”import { google } from '@cloudwerk/auth/convention'
google({ clientId: string, clientSecret: string, scope?: string, // Default: 'openid email profile'})discord()
Section titled “discord()”import { discord } from '@cloudwerk/auth/convention'
discord({ clientId: string, clientSecret: string, scope?: string, // Default: 'identify email'})apple()
Section titled “apple()”import { apple } from '@cloudwerk/auth/convention'
apple({ clientId: string, clientSecret: string, // Generated from Apple private key scope?: string,})createOAuth2Provider()
Section titled “createOAuth2Provider()”Create a custom OAuth 2.0 provider.
import { createOAuth2Provider } from '@cloudwerk/auth'
createOAuth2Provider({ id: string, // Unique provider ID name: string, // Display name clientId: string, clientSecret: string, authorization: string, // Authorization endpoint URL token: string, // Token endpoint URL userinfo: string, // Userinfo endpoint URL scope?: string, // OAuth scopes profile: (profile: unknown) => UserProfile, // Profile mapping})UserProfile Interface:
interface UserProfile { id: string email?: string name?: string image?: string emailVerified?: Date | null}credentials()
Section titled “credentials()”Email/password authentication.
import { credentials } from '@cloudwerk/auth'
credentials({ credentials: { [fieldName: string]: { label: string, type: 'text' | 'email' | 'password', placeholder?: string, required?: boolean, } }, authorize: (credentials: Record<string, string>, ctx: AuthContext) => Promise<User | null>,})Example:
credentials({ credentials: { email: { label: 'Email', type: 'email', required: true }, password: { label: 'Password', type: 'password', required: true }, }, async authorize(creds, ctx) { const user = await ctx.env.DB .prepare('SELECT * FROM users WHERE email = ?') .bind(creds.email) .first()
if (!user) return null
const valid = await verifyPassword(creds.password, user.password_hash) if (!valid) return null
return { id: user.id, email: user.email, name: user.name } },})email()
Section titled “email()”Magic link / passwordless authentication.
import { email } from '@cloudwerk/auth'
email({ from: string, // Sender email address maxAge?: number, // Link validity in seconds (default: 86400) sendVerificationRequest: (params: EmailParams) => Promise<void>,})
interface EmailParams { identifier: string // Recipient email url: string // Magic link URL token: string // Verification token expires: Date // Expiration time}passkey()
Section titled “passkey()”WebAuthn / passkey authentication.
import { passkey } from '@cloudwerk/auth'
passkey({ rpName: string, // Relying party name rpId: string, // Relying party ID (domain) origin: string, // Expected origin authenticatorAttachment?: 'platform' | 'cross-platform', userVerification?: 'required' | 'preferred' | 'discouraged', residentKey?: 'required' | 'preferred' | 'discouraged',})Callbacks API
Section titled “Callbacks API”defineAuthCallbacks()
Section titled “defineAuthCallbacks()”Define lifecycle callbacks for the auth flow.
import { defineAuthCallbacks } from '@cloudwerk/auth/convention'
export default defineAuthCallbacks({ signIn?: SignInCallback, redirect?: RedirectCallback, session?: SessionCallback, jwt?: JwtCallback,})SignInCallback
Section titled “SignInCallback”Called when a user signs in.
type SignInCallback = (params: { user: User, account: Account | null, profile?: Profile, email?: { verificationRequest?: boolean }, credentials?: Record<string, string>,}) => Awaitable<boolean | string> // Return false to deny, string to redirectRedirectCallback
Section titled “RedirectCallback”Customize redirect URLs.
type RedirectCallback = (params: { url: string, baseUrl: string,}) => Awaitable<string>SessionCallback
Section titled “SessionCallback”Customize session data.
type SessionCallback = (params: { session: Session, user: User, token?: JWT,}) => Awaitable<Session>Example:
defineAuthCallbacks({ async session({ session, user }) { session.user.id = user.id session.user.role = user.role return session },})JwtCallback
Section titled “JwtCallback”Customize JWT token (jwt strategy only).
type JwtCallback = (params: { token: JWT, user?: User, account?: Account, profile?: Profile, trigger?: 'signIn' | 'signUp' | 'update',}) => Awaitable<JWT>RBAC API
Section titled “RBAC API”defineRBAC()
Section titled “defineRBAC()”Define roles and permissions.
import { defineRBAC } from '@cloudwerk/auth/convention'
export default defineRBAC({ roles: Role[], defaultRole?: string, hierarchy?: Record<string, string[]>,})interface Role { id: string // Unique role identifier name: string // Display name permissions: string[] // Permission strings description?: string // Optional description}Permission Syntax
Section titled “Permission Syntax”| Pattern | Description |
|---|---|
resource:action | Standard permission |
resource:* | All actions on resource |
* | Full access (admin) |
resource:action:own | Only own resources |
Example:
defineRBAC({ roles: [ { id: 'admin', name: 'Administrator', permissions: ['*'], }, { id: 'editor', name: 'Editor', permissions: [ 'posts:create', 'posts:read', 'posts:update', 'posts:delete:own', 'media:*', ], }, { id: 'viewer', name: 'Viewer', permissions: ['posts:read', 'media:read'], }, ], defaultRole: 'viewer', hierarchy: { editor: ['viewer'], // Editor inherits viewer permissions },})Context Helpers
Section titled “Context Helpers”getUser()
Section titled “getUser()”Get the current authenticated user (returns null if not authenticated).
import { getUser } from '@cloudwerk/auth'
const user = getUser()// Returns: User | nullgetSession()
Section titled “getSession()”Get the current session.
import { getSession } from '@cloudwerk/auth'
const session = getSession()// Returns: Session | nullisAuthenticated()
Section titled “isAuthenticated()”Check if the current request is authenticated.
import { isAuthenticated } from '@cloudwerk/auth'
if (isAuthenticated()) { // User is logged in}requireAuth()
Section titled “requireAuth()”Require authentication; redirects or throws if not authenticated.
import { requireAuth } from '@cloudwerk/auth'
// Redirects to sign-in page if not authenticatedconst user = requireAuth()
// Throws UnauthenticatedError instead of redirectingconst user = requireAuth({ throwError: true })
// Custom redirect URLconst user = requireAuth({ redirectTo: '/custom-login' })hasRole()
Section titled “hasRole()”Check if the user has a specific role.
import { hasRole } from '@cloudwerk/auth'
if (hasRole('admin')) { // User is admin}
// Check for any of multiple rolesif (hasRole(['admin', 'moderator'])) { // User has admin OR moderator role}hasPermission()
Section titled “hasPermission()”Check if the user has a specific permission.
import { hasPermission } from '@cloudwerk/auth'
if (hasPermission('posts:delete')) { // User can delete posts}requireRole()
Section titled “requireRole()”Require a specific role; throws ForbiddenError if lacking.
import { requireRole } from '@cloudwerk/auth'
requireRole('admin') // Throws if not adminrequireRole(['admin', 'moderator']) // Any of these rolesrequirePermission()
Section titled “requirePermission()”Require a specific permission; throws ForbiddenError if lacking.
import { requirePermission } from '@cloudwerk/auth'
requirePermission('posts:create')Middleware API
Section titled “Middleware API”authMiddleware()
Section titled “authMiddleware()”Create authentication middleware for route protection.
import { authMiddleware } from '@cloudwerk/auth/middleware'
export const middleware = authMiddleware(options)AuthMiddlewareOptions
Section titled “AuthMiddlewareOptions”interface AuthMiddlewareOptions { // Authentication requirements required?: boolean // Require authentication (default: true) unauthenticatedRedirect?: string // Redirect URL for unauthenticated
// Role requirements role?: string // Required role roles?: string[] // Any of these roles
// Permission requirements permission?: string // Required permission permissions?: string[] // Any of these permissions
// Custom authorization authorize?: (user: User, request: Request) => Awaitable<boolean>
// Forbidden handling unauthorizedRedirect?: string // Redirect for unauthorized (403)}Examples:
// Require authenticationauthMiddleware({ unauthenticatedRedirect: '/login',})
// Require specific roleauthMiddleware({ role: 'admin', unauthorizedRedirect: '/forbidden',})
// Custom authorization logicauthMiddleware({ async authorize(user, request) { const url = new URL(request.url) const resourceId = url.pathname.split('/').pop() const resource = await getResource(resourceId) return resource.ownerId === user.id },})Multi-Tenancy API
Section titled “Multi-Tenancy API”createTenantResolver()
Section titled “createTenantResolver()”Create a tenant resolver for multi-tenant applications.
import { createTenantResolver, createD1TenantStorage } from '@cloudwerk/auth/tenant'
const storage = createD1TenantStorage(env.DB)const resolver = createTenantResolver(storage, options)TenantResolverOptions
Section titled “TenantResolverOptions”interface TenantResolverOptions { strategy: 'subdomain' | 'path' | 'header' | 'cookie' baseDomain?: string // For subdomain strategy pathPrefix?: string // For path strategy (default: '/t/') headerName?: string // For header strategy (default: 'X-Tenant-ID') cookieName?: string // For cookie strategy (default: 'tenant')}TenantResolver Methods
Section titled “TenantResolver Methods”interface TenantResolver { resolve(request: Request): Promise<Tenant | null> require(request: Request): Promise<{ tenant: Tenant }> // Throws if not found}Storage Adapters
Section titled “Storage Adapters”import { createD1TenantStorage, createKVTenantStorage, createMemoryTenantStorage,} from '@cloudwerk/auth/tenant'
// D1 storageconst storage = createD1TenantStorage(env.DB, { tableName: 'tenants', // Default})
// KV storageconst storage = createKVTenantStorage(env.TENANTS_KV, { prefix: 'tenant:', // Default})
// In-memory (for testing)const storage = createMemoryTenantStorage()Rate Limiting API
Section titled “Rate Limiting API”createLoginRateLimiter()
Section titled “createLoginRateLimiter()”Rate limiter for login attempts.
import { createLoginRateLimiter, createFixedWindowStorage } from '@cloudwerk/auth/rate-limit'
const storage = createFixedWindowStorage(env.RATE_LIMIT_KV)const limiter = createLoginRateLimiter(storage, { limit: 5, // Max attempts window: 900, // Per 15 minutes (in seconds)})createPasswordResetRateLimiter()
Section titled “createPasswordResetRateLimiter()”Rate limiter for password reset requests.
import { createPasswordResetRateLimiter } from '@cloudwerk/auth/rate-limit'
const limiter = createPasswordResetRateLimiter(storage, { limit: 3, // Max attempts window: 3600, // Per hour})createEmailVerificationRateLimiter()
Section titled “createEmailVerificationRateLimiter()”Rate limiter for email verification requests.
import { createEmailVerificationRateLimiter } from '@cloudwerk/auth/rate-limit'
const limiter = createEmailVerificationRateLimiter(storage, { limit: 5, // Max attempts window: 3600, // Per hour})Rate Limiter Methods
Section titled “Rate Limiter Methods”interface RateLimiter { check(request: Request): Promise<RateLimitResult>}
interface RateLimitResult { success: boolean // Whether request is allowed limit: number // Configured limit remaining: number // Remaining requests reset: number // Seconds until reset response?: Response // 429 response if rate limited}Storage Implementations
Section titled “Storage Implementations”import { createFixedWindowStorage, createSlidingWindowStorage,} from '@cloudwerk/auth/rate-limit'
// Fixed window (simpler, less accurate)const storage = createFixedWindowStorage(env.KV)
// Sliding window (more accurate, higher storage)const storage = createSlidingWindowStorage(env.KV)Client API
Section titled “Client API”signIn()
Section titled “signIn()”Initiate sign-in flow.
import { signIn } from '@cloudwerk/auth/client'
// OAuth providerawait signIn('github')await signIn('google', { callbackUrl: '/dashboard' })
// Credentialsawait signIn('credentials', { password: 'password', redirectTo: '/dashboard',})
// Email/magic linksignOut()
Section titled “signOut()”Sign out the current user.
import { signOut } from '@cloudwerk/auth/client'
await signOut()await signOut({ redirectTo: '/' })await signOut({ redirect: false }) // Returns session datagetSession() (Client)
Section titled “getSession() (Client)”Get the current session on the client.
import { getSession } from '@cloudwerk/auth/client'
const session = await getSession()if (session) { console.log('Logged in as', session.user.email)}createAuthStore()
Section titled “createAuthStore()”Create a reactive auth store for frameworks.
import { createAuthStore } from '@cloudwerk/auth/client'
const store = createAuthStore()
// Subscribe to changesstore.subscribe((state) => { console.log('Auth state:', state.status, state.user)})
// Get current stateconst { user, status } = store.getState()
// Status: 'loading' | 'authenticated' | 'unauthenticated'Password Utilities
Section titled “Password Utilities”hashPassword()
Section titled “hashPassword()”Hash a password for storage.
import { hashPassword } from '@cloudwerk/auth'
const hash = await hashPassword('user_password')// Store hash in databaseverifyPassword()
Section titled “verifyPassword()”Verify a password against a hash.
import { verifyPassword } from '@cloudwerk/auth'
const isValid = await verifyPassword('user_password', storedHash)generateToken()
Section titled “generateToken()”Generate a secure random token.
import { generateToken } from '@cloudwerk/auth'
const token = await generateToken() // 32 bytes, hex encodedconst token = await generateToken(64) // Custom lengthError Classes
Section titled “Error Classes”UnauthenticatedError
Section titled “UnauthenticatedError”Thrown when authentication is required but missing.
import { UnauthenticatedError } from '@cloudwerk/auth'
class UnauthenticatedError extends Error { readonly code: 'UNAUTHENTICATED' readonly status: 401}ForbiddenError
Section titled “ForbiddenError”Thrown when user lacks required role/permission.
import { ForbiddenError } from '@cloudwerk/auth'
class ForbiddenError extends Error { readonly code: 'FORBIDDEN' readonly status: 403 readonly requiredRole?: string readonly requiredPermission?: string}InvalidCredentialsError
Section titled “InvalidCredentialsError”Thrown when credentials are invalid.
import { InvalidCredentialsError } from '@cloudwerk/auth'
class InvalidCredentialsError extends Error { readonly code: 'INVALID_CREDENTIALS' readonly status: 401}SessionExpiredError
Section titled “SessionExpiredError”Thrown when session has expired.
import { SessionExpiredError } from '@cloudwerk/auth'
class SessionExpiredError extends Error { readonly code: 'SESSION_EXPIRED' readonly status: 401}Type Definitions
Section titled “Type Definitions”interface User { id: string email?: string | null name?: string | null image?: string | null emailVerified?: Date | null role?: string roles?: string[]}Session
Section titled “Session”interface Session { user: User expires: Date}Account
Section titled “Account”interface Account { provider: string providerAccountId: string type: 'oauth' | 'oidc' | 'email' | 'credentials' | 'webauthn' access_token?: string refresh_token?: string expires_at?: number token_type?: string scope?: string}interface JWT { sub: string // Subject (user ID) iat: number // Issued at exp: number // Expires at jti: string // JWT ID [key: string]: unknown // Custom claims}Tenant
Section titled “Tenant”interface Tenant { id: string slug: string name: string settings?: Record<string, unknown> createdAt: Date updatedAt: Date}Next Steps
Section titled “Next Steps”- Authentication Guide - Patterns and best practices
- Middleware API - Advanced middleware patterns
- Database Guide - User storage with D1