@bolkauth/core
v0.2.1
Published
Core framework-agnostic auth engine for BolkAuth
Readme
@bolkauth/core
@bolkauth/core is a lightweight, framework-agnostic, zero-dependency authentication engine built on the standard Web Crypto API (crypto.subtle). Designed for Node.js, Next.js, Cloudflare Workers, Bun, Deno, and Vercel Edge Runtime, @bolkauth/core provides complete authentication flows out of the box with zero external cryptographic dependencies.
Table of Contents
- Features
- Installation
- Quickstart
- Core Architecture & Security
- API Reference
- Supported HTTP Endpoints
- License
Features
- 🔐 Zero External Crypto Dependencies: Native Web Crypto API (
crypto.subtle) for PBKDF2-SHA256 password hashing and HMAC-SHA256 JWT signing. - 🔑 Cryptographically Secure Email OTP:
crypto.getRandomValues()generation, SHA-256 hashed storage, oracle attack prevention, and strict attempt limits. - 🎟️ Session Management: Dual support for JWTs and Database sessions with
HttpOnly,SameSite=Laxcookies. - ⚡ OAuth 2.0 Integration: Out-of-the-box support for GitHub and Google with PKCE-like state parameter CSRF validation.
- 📋 Onboarding FSM: Step-by-step onboarding progress tracking & completion state machine stored via user metadata.
- 🌐 Edge & Multi-Runtime Ready: Fully compatible with Node.js 18+, Next.js (App Router & Pages Router), Cloudflare Workers, Vercel Edge, Deno, and Bun.
- 🔌 Pluggable Database Adapters: Clean TypeScript adapter pattern for Prisma, Drizzle, Kysely, MongoDB, PostgreSQL, or custom ORMs.
Installation
# npm
npm install @bolkauth/core
# pnpm
pnpm add @bolkauth/core
# yarn
yarn add @bolkauth/coreQuickstart
Initialize BolkAuth with your database adapter and secret:
import { createBolkAuth, type BolkAuthAdapter } from '@bolkauth/core';
// Define or import your database adapter
const adapter: BolkAuthAdapter = {
async createUser(user) { /* ... database query ... */ },
async findUserByEmail(email) { /* ... database query ... */ },
async findUserById(id) { /* ... database query ... */ },
async updateUser(id, user) { /* ... database query ... */ },
async deleteUser(id) { /* ... database query ... */ },
async createSession(session) { /* ... database query ... */ },
async findSessionByToken(token) { /* ... database query ... */ },
async updateSession(id, session) { /* ... database query ... */ },
async deleteSession(id) { /* ... database query ... */ },
async deleteUserSessions(userId) { /* ... database query ... */ },
async createAccount(account) { /* ... database query ... */ },
async findAccountByProvider(provider, providerAccountId) { /* ... database query ... */ },
async createVerificationToken(token) { /* ... database query ... */ },
async findVerificationToken(identifier, token) { /* ... database query ... */ },
async deleteVerificationToken(identifier, token) { /* ... database query ... */ },
async getUserMetadata(userId, key) { /* ... database query ... */ },
async updateUserMetadata(userId, key, value) { /* ... database query ... */ },
};
export const auth = createBolkAuth({
secret: process.env.BOLKAUTH_SECRET || 'super-secret-key-32-chars-long!',
adapter,
session: {
maxAge: 30 * 24 * 60 * 60, // 30 days
cookieName: 'bolkauth.session',
},
emailAndPassword: {
enabled: true,
minPasswordLength: 8,
},
socialProviders: {
github: {
clientId: process.env.GITHUB_CLIENT_ID!,
clientSecret: process.env.GITHUB_CLIENT_SECRET!,
},
},
});Next.js App Router Catch-all API Route (app/api/auth/[...bolkauth]/route.ts)
import { auth } from '@/lib/auth';
export async function GET(request: Request) {
return auth.handleRequest(request);
}
export async function POST(request: Request) {
return auth.handleRequest(request);
}Core Architecture & Security
Web Crypto PBKDF2 Password Hashing
@bolkauth/core uses standard Web Crypto SubtleCrypto primitives for password hashing, removing the need for native C++ bindings like bcrypt.
- Salt: 16 bytes of cryptographically random data generated via
crypto.getRandomValues(). - Key Derivation:
PBKDF2with 100,000 iterations andSHA-256. - Output Format:
${saltHex}:${hashHex}.
import { hashPassword, verifyPassword } from '@bolkauth/core';
// Hash a user's password
const storedHash = await hashPassword('mySecurePassword123');
// -> "a1f8c...:9e2b4..."
// Verify credentials during sign-in
const isValid = await verifyPassword('mySecurePassword123', storedHash);
// -> trueJWT & Session Management
Tokens are signed using HMAC-SHA256 via Web Crypto (subtle.importKey + subtle.sign).
- Cookie Security: Set with
HttpOnly; SameSite=Lax; Path=/; Max-Age=<seconds>. - Session Lookup: Sessions are indexed and validated in both database adapters and signed JWT payloads.
import { signJwt, verifyJwt, hashToken } from '@bolkauth/core';
// Sign a session payload
const jwt = await signJwt({ sessionId: 'sess_123', userId: 'user_456' }, secret, 86400);
// Verify and decode JWT
const payload = await verifyJwt(jwt, secret);Email OTP & Magic Links
Email OTP uses high-entropy numeric generation combined with secure one-time database token storage.
- Generation:
crypto.getRandomValues()produces a uniform 4, 6, or 8-digit numeric code. - Storage: Hashed with SHA-256 prior to database storage (
otp:<email>namespace). - Verification: Input is sanitized, hashed, and checked in constant time (
timingSafeEqual). - Oracle & Brute-Force Protection: Uses unified timing-safe responses to prevent email enumeration.
import { generateOTPCode, hashOTPCode, timingSafeEqual } from '@bolkauth/core';
const code = generateOTPCode(6); // e.g. "849201"
const hashed = await hashOTPCode(code);
// Constant-time check
const isMatch = timingSafeEqual(hashed, storedHashedCode);OAuth 2.0 Authentication
Built-in support for provider authorization URLs, state cookies for CSRF prevention, code-for-token exchange, and user profile retrieval.
import { buildAuthorizationUrl, exchangeCodeForToken, fetchUserInfo } from '@bolkauth/core';
// Generates authorization URL for GitHub/Google
const url = buildAuthorizationUrl(
'github',
{ clientId: 'XYZ', scopes: ['read:user', 'user:email'] },
'https://example.com/api/auth/oauth/github/callback',
'state_nonce_123'
);Onboarding Finite State Machine (FSM)
Track onboarding progress across steps using user metadata key-value storage:
- Step data saved via
/onboarding/stependpoint (key: 'onboarding_step'). - Completion toggled via
/onboarding/complete(key: 'onboarding_complete').
API Reference
createBolkAuth(config)
Factory function to instantiate the authentication engine.
function createBolkAuth(config: BolkAuthConfig): BolkAuthInstanceBolkAuthInstance
The instantiated engine handling incoming HTTP requests and auth state.
Methods
handleRequest(req: Request): Promise<Response>
Main handler mapping URL paths to internal handlers (sign-up, sign-in, OTP, session, metadata, OAuth).
Configuration Options (BolkAuthConfig)
export interface BolkAuthConfig {
adapter: BolkAuthAdapter;
secret: string;
session?: SessionConfig;
email?: EmailConfig;
emailAndPassword?: {
enabled?: boolean;
requireEmailVerification?: boolean;
minPasswordLength?: number;
};
socialProviders?: Record<string, { clientId: string; clientSecret: string; scopes?: string[] }>;
social?: SocialConfig;
onboarding?: OnboardingConfig;
otp?: OTPConfig;
}SessionConfig
maxAge?: number(Default: 30 days in seconds)updateAge?: numberstrategy?: 'jwt' | 'database'cookieName?: string(Default:'bolkauth.session')
EmailConfig
sendVerificationRequest?: (params: { identifier: string; url: string; token: string }) => Promise<void>sendMagicLink?: (params: { identifier: string; url: string; token: string }) => Promise<void>sendOTP?: (params: { email: string; code: string; expiresAt: Date }) => Promise<void>verifyRedirectUrl?: string
OTPConfig
expiresIn?: number(Default:600seconds / 10 minutes)codeLength?: 4 | 6 | 8(Default:6)maxAttempts?: number(Default:5)onRateLimitExceeded?: (email: string) => Promise<void>
Database Adapter Interface (BolkAuthAdapter)
Implement this interface to plug in any database:
export interface BolkAuthAdapter {
createUser(user: Omit<User, 'id' | 'createdAt' | 'updatedAt'>): Promise<User>;
findUserById(id: string): Promise<User | null>;
findUserByEmail(email: string): Promise<User | null>;
updateUser(id: string, user: Partial<User>): Promise<User>;
deleteUser(id: string): Promise<void>;
createSession(session: Omit<Session, 'id' | 'createdAt' | 'updatedAt'>): Promise<Session>;
findSessionByToken(token: string): Promise<Session | null>;
updateSession(id: string, session: Partial<Session>): Promise<Session>;
deleteSession(id: string): Promise<void>;
deleteUserSessions(userId: string): Promise<void>;
createAccount(account: Omit<Account, 'id' | 'createdAt' | 'updatedAt'>): Promise<Account>;
findAccountByProvider(provider: string, providerAccountId: string): Promise<Account | null>;
createVerificationToken(token: Omit<VerificationToken, 'createdAt'>): Promise<VerificationToken>;
findVerificationToken(identifier: string, token: string): Promise<VerificationToken | null>;
deleteVerificationToken(identifier: string, token: string): Promise<void>;
getUserMetadata(userId: string, key: string): Promise<UserMetadata | null>;
updateUserMetadata(userId: string, key: string, value: string): Promise<UserMetadata>;
}Cryptographic Utilities
// Password hashing
export function hashPassword(password: string): Promise<string>;
export function verifyPassword(password: string, storedHash: string): Promise<boolean>;
// Session & JWT
export function hashToken(token: string): Promise<string>;
export function signJwt(payload: any, secret: string, expiresIn?: number): Promise<string>;
export function verifyJwt(token: string, secret: string): Promise<any>;
// Email OTP
export function generateOTPCode(length?: 4 | 6 | 8): string;
export function hashOTPCode(code: string): Promise<string>;
export function timingSafeEqual(a: string, b: string): boolean;Supported HTTP Endpoints
When handleRequest(req) is invoked, it routes the following endpoints based on req.url:
| Method | Endpoint | Description |
| :--- | :--- | :--- |
| POST | /sign-up | Registers a new user with email & password. Returns user & session cookie. |
| POST | /sign-in/email | Authenticates existing user with email & password. Sets session cookie. |
| POST | /sign-in/magic-link | Sends a passwordless login link to user's email. |
| GET | /verify | Validates magic link token and redirects user. |
| POST | /otp/send | Generates & sends email OTP using config.email.sendOTP. |
| POST | /otp/verify | Validates email OTP, creates/logs in user, sets session cookie. |
| POST | /sign-out | Invalidates active session and clears session cookie. |
| GET | /session | Retrieves current session state and authenticated user payload. |
| POST | /user/metadata | Updates key-value metadata for the authenticated user. |
| GET | /user/metadata | Retrieves metadata by key search parameter. |
| POST | /onboarding/step | Saves onboarding step progress (onboarding_step). |
| POST | /onboarding/complete | Marks onboarding as complete (onboarding_complete). |
| GET | /oauth/:provider | Initiates OAuth 2.0 authorization redirect. |
| GET | /oauth/:provider/callback | Handles OAuth callback, exchanges authorization code, signs in user. |
License
MIT © BolkAuth
