AuthKeeper is a lightweight, plugin-based authentication library that provides JWT token management, automatic refresh, and fetch interception with a clean, modular architecture.
Find a file
Ersin KOÇ 6a13be29d4 Add complete API documentation and examples pages to website
- Add routing for Docs, API, Examples, and Playground pages in App.tsx
- Implement Tabs UI component from Radix UI
- Create comprehensive API Reference page with:
  - Lifecycle methods (init, destroy)
  - Token management methods (setTokens, getAccessToken, etc.)
  - Auth state methods (isAuthenticated, isExpired, etc.)
  - Token refresh methods (refresh, scheduleRefresh, etc.)
  - Event system methods (on, off, emit)
  - TypeScript type definitions
- Create
2025-12-28 17:03:52 +02:00
.github/workflows Release v1.1.0 - 100% Test Coverage & Website Launch 2025-12-28 16:37:52 +02:00
docs Update .gitignore and remove temporary documentation files 2025-12-28 16:49:14 +02:00
src init 2025-12-28 16:00:55 +02:00
tests Release v1.1.0 - 100% Test Coverage & Website Launch 2025-12-28 16:37:52 +02:00
website Add complete API documentation and examples pages to website 2025-12-28 17:03:52 +02:00
.eslintrc.cjs init 2025-12-28 16:00:55 +02:00
.gitignore Update .gitignore and remove temporary documentation files 2025-12-28 16:49:14 +02:00
.prettierrc.json init 2025-12-28 16:00:55 +02:00
CHANGELOG.md Release v1.1.0 - 100% Test Coverage & Website Launch 2025-12-28 16:37:52 +02:00
LICENSE init 2025-12-28 16:00:55 +02:00
package-lock.json init 2025-12-28 16:00:55 +02:00
package.json Release v1.1.0 - 100% Test Coverage & Website Launch 2025-12-28 16:37:52 +02:00
README.md Update .gitignore and remove temporary documentation files 2025-12-28 16:49:14 +02:00
tsconfig.build.json init 2025-12-28 16:00:55 +02:00
tsconfig.json init 2025-12-28 16:00:55 +02:00
tsup.config.ts init 2025-12-28 16:00:55 +02:00
vitest.config.ts init 2025-12-28 16:00:55 +02:00

🔐 AuthKeeper

A micro-kernel authentication library with plugin-based architecture

Tests Coverage TypeScript License

AuthKeeper is a lightweight, plugin-based authentication library that provides JWT token management, automatic refresh, and fetch interception with a clean, modular architecture.

Features

  • 🔌 Plugin-Based Architecture - Extensible micro-kernel design
  • 🔄 Automatic Token Refresh - Smart refresh with queuing and retry logic
  • 🎯 Fetch Interception - Automatic auth header injection and 401 retry
  • 💾 Flexible Storage - Memory, localStorage, sessionStorage, or custom adapters
  • 📡 Event System - Subscribe to auth lifecycle events
  • 🔒 JWT Support - Built-in JWT decoding and validation
  • 🍪 Cookie Support - Parse and manage authentication cookies
  • Zero Dependencies - Pure TypeScript implementation
  • 🧪 100% Tested - 541 tests with 99.83% coverage
  • 📦 Tree-Shakeable - Only bundle what you use

📦 Installation

npm install @oxog/authkeeper
# or
yarn add @oxog/authkeeper
# or
pnpm add @oxog/authkeeper
# or
bun add @oxog/authkeeper

🚀 Quick Start

import { createAuthKeeper } from '@oxog/authkeeper'

// Create AuthKeeper instance
const auth = await createAuthKeeper({
  refreshToken: async (refreshToken) => {
    const res = await fetch('/api/auth/refresh', {
      method: 'POST',
      body: JSON.stringify({ refreshToken }),
    })
    return res.json()
  },
  autoRefresh: true,
  refreshThreshold: 60, // Refresh 60s before expiry
})

// Set initial tokens
auth.setTokens({
  accessToken: 'your-access-token',
  refreshToken: 'your-refresh-token',
  expiresIn: 3600, // 1 hour
})

// Create authenticated fetch
const authFetch = auth.createFetch()

// Make authenticated requests
const response = await authFetch('/api/protected')

📚 Core Concepts

Micro-Kernel Architecture

AuthKeeper uses a plugin-based architecture where most functionality is provided by plugins:

┌─────────────────────────────────────┐
│          AuthKeeper Kernel          │
│  (Orchestrates plugins & events)    │
└─────────────────────────────────────┘
           │
           ├──► TokenStore Plugin (State Management)
           ├──► TokenDecoder Plugin (JWT Decoding)
           ├──► RefreshEngine Plugin (Auto-Refresh)
           └──► FetchInterceptor Plugin (HTTP Interception)

Core Plugins

All core plugins are automatically registered when using createAuthKeeper():

  • token-store - Manages token state and expiration
  • token-decoder - Decodes and validates JWT tokens
  • refresh-engine - Handles automatic token refresh with retry logic
  • fetch-interceptor - Injects auth headers and handles 401 responses

🎯 Usage Examples

Token Management

// Set tokens
auth.setTokens({
  accessToken: 'access-token',
  refreshToken: 'refresh-token',
  expiresIn: 3600, // seconds
})

// Get tokens
const accessToken = auth.getAccessToken()
const refreshToken = auth.getRefreshToken()

// Check authentication
if (auth.isAuthenticated()) {
  console.log('User is authenticated')
}

// Check expiration
if (auth.isExpired()) {
  console.log('Token is expired')
}

// Get expiry info
const expiresAt = auth.getExpiresAt()
const timeUntilExpiry = auth.getTimeUntilExpiry()

// Clear tokens
auth.clearTokens()

Manual Token Refresh

// Manually refresh tokens
try {
  const newTokens = await auth.refresh()
  console.log('Tokens refreshed:', newTokens)
} catch (error) {
  console.error('Refresh failed:', error)
}

Automatic Refresh

// Auto-refresh is enabled by default
const auth = await createAuthKeeper({
  refreshToken: refreshFn,
  autoRefresh: true, // default
  refreshThreshold: 60, // Refresh 60s before expiry
})

// Tokens will be refreshed automatically when they're about to expire

Fetch Interception

// Create authenticated fetch
const authFetch = auth.createFetch({
  baseURL: 'https://api.example.com',
  retry401: true, // Retry with refreshed token on 401
  includeURLs: ['/api/*'], // Only add auth to these URLs
  excludeURLs: ['/api/public/*'], // Except these
})

// Use like normal fetch
const response = await authFetch('/api/users')
const data = await response.json()

Event Handling

// Subscribe to events
auth.on('login', (event) => {
  console.log('User logged in:', event.tokens)
})

auth.on('logout', (event) => {
  console.log('User logged out:', event.reason)
})

auth.on('refresh:success', (event) => {
  console.log('Tokens refreshed:', event.tokens)
})

auth.on('refresh:error', (event) => {
  console.error('Refresh failed:', event.error)
})

auth.on('error', (event) => {
  console.error('Auth error:', event.error)
})

JWT Decoding

// Decode token payload
const payload = auth.decode()
console.log('User ID:', payload?.sub)

// Get specific claim
const userId = auth.getClaim('sub')
const email = auth.getClaim('email')

// Get multiple claims
const { sub, email, exp } = auth.getClaims(['sub', 'email', 'exp'])

Logout

// Simple logout
auth.logout()

// Logout with redirect
auth.logout({ redirect: '/login' })

🔌 Custom Plugins

Create your own plugins to extend functionality:

import type { Plugin, AuthKeeper } from '@oxog/authkeeper'

const myPlugin: Plugin = {
  name: 'my-plugin',
  version: '1.0.0',
  type: 'custom',

  install(kernel: AuthKeeper) {
    // Plugin initialization
    return {
      // Public API
      myMethod() {
        // Your logic
      },
    }
  },

  uninstall() {
    // Cleanup
  },
}

// Register custom plugin
await auth.register(myPlugin)

🧪 Testing

AuthKeeper has comprehensive test coverage:

# Run tests
npm test

# Run with coverage
npm test -- --coverage

# Run in watch mode
npm test -- --watch

# Run with UI
npm test -- --ui

Test Coverage

Test Files:    16/16 passing
Total Tests:   541/541 passing
Coverage:      99.83%
Functions:     100%
Execution:     ~2.5 seconds

See docs/testing/TESTING.md for detailed testing documentation.

📖 API Reference

AuthKeeper Interface

interface AuthKeeper {
  // Lifecycle
  init(): Promise<void>
  destroy(): void

  // Token Management
  setTokens(tokens: TokenSet): void
  getAccessToken(): string | null
  getRefreshToken(): string | null
  clearTokens(): void

  // Auth State
  isAuthenticated(): boolean
  isExpired(): boolean
  getExpiresAt(): Date | null
  getTimeUntilExpiry(): number | null

  // Token Decoding
  decode<T = TokenPayload>(): T | null
  getClaim<K extends keyof TokenPayload>(key: K): TokenPayload[K] | undefined
  getClaims<K extends keyof TokenPayload>(keys: K[]): Pick<TokenPayload, K>

  // Refresh
  refresh(): Promise<TokenSet>
  scheduleRefresh(): void
  cancelScheduledRefresh(): void

  // Logout
  logout(options?: LogoutOptions): void

  // Fetch
  createFetch(options?: FetchInterceptorOptions): AuthFetch
  wrapFetch(fetch: typeof globalThis.fetch): void

  // Plugins
  register(plugin: Plugin): Promise<void>
  unregister(pluginName: string): Promise<void>
  getPlugin<P extends Plugin>(name: string): P | undefined
  listPlugins(): PluginInfo[]

  // Events
  emit(event: AuthEvent): void
  on<E extends EventType>(eventType: E, handler: EventHandler<E>): Unsubscribe
  off<E extends EventType>(eventType: E, handler: EventHandler<E>): void

  // Configuration
  configure(options: Partial<KernelOptions>): void
  getOptions(): KernelOptions
}

Configuration Options

interface KernelOptions {
  // Required
  refreshToken: RefreshTokenFn

  // Optional
  autoRefresh?: boolean // Default: true
  refreshThreshold?: number // Default: 60 (seconds)
  syncTabs?: boolean // Default: true
  storage?: StorageAdapter
  plugins?: Plugin[]
  onError?: (error: Error) => void
}

🏗️ Architecture

src/
├── factory.ts              # Factory function
├── kernel/
│   ├── kernel.ts          # Main kernel
│   ├── event-bus.ts       # Event system
│   └── plugin-registry.ts # Plugin management
├── plugins/core/
│   ├── token-store.ts     # Token state management
│   ├── token-decoder.ts   # JWT decoding
│   ├── refresh-engine.ts  # Auto-refresh logic
│   ├── fetch-interceptor.ts # HTTP interception
│   └── storage-memory.ts  # In-memory storage
└── utils/
    ├── base64.ts          # Base64URL encoding
    ├── cookie.ts          # Cookie utilities
    ├── crypto.ts          # Crypto utilities
    ├── jwt.ts             # JWT parsing
    ├── storage.ts         # Storage helpers
    └── time.ts            # Time utilities

🤝 Contributing

Contributions are welcome! Please read our contributing guidelines first.

  1. Fork the repository
  2. Create your feature branch (git checkout -b feature/amazing-feature)
  3. Write tests for your changes
  4. Ensure all tests pass (npm test)
  5. Commit your changes (git commit -m 'Add amazing feature')
  6. Push to the branch (git push origin feature/amazing-feature)
  7. Open a Pull Request

📄 License

MIT © Ersin KOÇ

💡 Inspiration

AuthKeeper is inspired by:


Built with ❤️ using TypeScript and Vitest