@jezweb/oauth-token-manager
v0.1.3
Published
OAuth token management for Cloudflare Workers - store, refresh, and retrieve tokens for downstream API access
Readme
@jezweb/oauth-token-manager
OAuth token management for Cloudflare Workers. Store, refresh, and retrieve tokens for downstream API access.
The Problem
When your application needs to call APIs on behalf of users (Google Calendar, GitHub, Xero, etc.), you need to:
- Store OAuth tokens securely (encrypted at rest)
- Refresh expired tokens automatically
- Retrieve valid tokens for API calls
- Handle errors gracefully (expired, revoked, insufficient scopes)
Most auth libraries focus on identity ("who is this user?") not API access ("act on their behalf"). This package fills that gap.
Installation
npm install @jezweb/oauth-token-managerQuick Start
import { TokenManager, KVStorage } from '@jezweb/oauth-token-manager';
// Initialize
const tokens = new TokenManager({
storage: new KVStorage({
namespace: env.TOKEN_KV,
encryptionKey: env.TOKEN_ENCRYPTION_KEY,
}),
providers: {
google: {
clientId: env.GOOGLE_CLIENT_ID,
clientSecret: env.GOOGLE_CLIENT_SECRET,
},
},
});
// Store token after OAuth callback
await tokens.store({
userId: 'user-123',
provider: 'google',
accessToken: 'ya29.xxx',
refreshToken: '1//xxx',
expiresAt: Date.now() + 3600000,
scopes: ['https://www.googleapis.com/auth/calendar'],
});
// Get valid token (auto-refreshes if expired)
const { accessToken } = await tokens.get({
userId: 'user-123',
provider: 'google',
});
// Use token for API call
const response = await fetch('https://www.googleapis.com/calendar/v3/calendars', {
headers: { Authorization: `Bearer ${accessToken}` },
});Features
- Encrypted storage - Tokens encrypted at rest using AES-256-GCM
- Automatic refresh - Tokens refreshed before expiry (5 min buffer by default)
- Scope validation - Verify required scopes before returning tokens
- Built-in providers - Google, Microsoft, GitHub out of the box
- Cloudflare-native - Built specifically for Workers + KV
- Clear errors - Typed errors guide recovery actions
API
TokenManager
Main class for token management.
const tokens = new TokenManager({
storage: TokenStorage, // KVStorage or custom (handles encryption)
providers: { // Provider configs for refresh
google?: ProviderConfig,
microsoft?: ProviderConfig,
github?: ProviderConfig,
},
defaultRefreshBuffer?: number, // ms before expiry to refresh (default: 5 min)
});Methods
| Method | Description |
|--------|-------------|
| store(options) | Store a new token or update existing |
| get(options) | Get valid token (auto-refreshes) |
| list(options) | List connected providers for a user |
| revoke(options) | Delete a token |
| has(userId, provider) | Check if token exists |
Error Types
| Error | Meaning | Recovery |
|-------|---------|----------|
| TokenNotFoundError | No token for user/provider | Redirect to OAuth |
| TokenExpiredError | Token expired, refresh failed | Redirect to OAuth |
| InsufficientScopesError | Missing required scopes | Redirect to OAuth with incremental consent |
| ProviderNotConfiguredError | Provider not in config | Add provider config |
Supported Providers
| Provider | Refresh Support | Token Lifetime | Notes |
|----------|-----------------|----------------|-------|
| Google | ✅ Yes | ~1 hour | Requires access_type=offline |
| Microsoft | ✅ Yes | ~1 hour | Token rotation by default |
| GitHub | ❌ No | Never expires | Tokens valid until revoked |
Storage Adapters
KV Storage (Recommended)
import { KVStorage } from '@jezweb/oauth-token-manager/storage/kv';
const storage = new KVStorage({
namespace: env.TOKEN_KV,
encryptionKey: env.TOKEN_ENCRYPTION_KEY,
keyPrefix: 'tokens', // optional, default: 'tokens'
});D1 Storage (Coming Soon)
D1 adapter for stronger consistency and complex queries.
Wrangler Setup
# wrangler.toml
kv_namespaces = [
{ binding = "TOKEN_KV", id = "your-kv-id" }
]
[vars]
# Store encryption key as secret, not here!# Set encryption key (generate with: openssl rand -base64 32)
echo "your-32-byte-key" | wrangler secret put TOKEN_ENCRYPTION_KEYUse Cases
- MCP Servers - Call Google Calendar, GitHub, etc. on behalf of users
- CRM integrations - Sync with external calendars, email
- Social media tools - Post to Twitter, LinkedIn
- Accounting apps - Connect to Xero, QuickBooks
Architecture
This package handles outbound OAuth (your app calling external APIs).
For inbound OAuth (clients authenticating to your app), use:
┌─────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ MCP Client │────▶│ Your App │────▶│ External API │
│ (Claude.ai) │ │ │ │ (Google, etc) │
└─────────────┘ └─────────────────┘ └─────────────────┘
│ │ │
▼ ▼ ▼
Inbound auth Token Manager External OAuth
(who is client?) (this package) (act on behalf)Security
See SECURITY.md for security considerations.
License
MIT © Jezweb
