The flarelette-jwt-kit library had complex environment detection that made it difficult to use in development:
- Global State Dependency: All functions read from
globalThis.__FLARELETTE_ENVorprocess.env - Miniflare Complexity: Required
bindEnv()middleware to mutate global state before JWT operations - No Explicit Config: Impossible to pass configuration directly as parameters
- Testing Pain: Hard to write isolated tests without environment pollution
This led to developers bypassing the library entirely and implementing JWT signing directly with Web Crypto API (like in bond-math).
Added a new explicit configuration API that allows passing config objects directly, while keeping the existing environment-based API for backward compatibility.
// Requires environment variables or bindEnv() middleware
import { sign, verify } from '@chrislyons-dev/flarelette-jwt'
const token = await sign({ sub: 'user123' })
const payload = await verify(token)Use when:
- Production deployment with Cloudflare bindings
- Environment-based configuration is desired
- Zero configuration code needed
// No environment variables required!
import {
signWithConfig,
verifyWithConfig,
createHS512Config,
} from '@chrislyons-dev/flarelette-jwt'
const config = createHS512Config('secret', {
iss: 'https://gateway.example.com',
aud: 'api.example.com',
})
const token = await signWithConfig({ sub: 'user123' }, config)
const payload = await verifyWithConfig(token, config)Use when:
- Development environment setup
- Testing without environment pollution
- Multiple JWT configurations needed
- Explicit control over every parameter
Configuration Types:
BaseJwtConfig- Shared config (iss, aud, ttlSeconds, leeway)HS512Config- Symmetric (shared secret) configurationEdDSASignConfig- Asymmetric signing configurationEdDSAVerifyConfig- Asymmetric verification configuration
Core Functions:
signWithConfig()- Sign JWT with explicit configverifyWithConfig()- Verify JWT with explicit config
High-Level Functions:
createTokenWithConfig()- Convenience wrapper for signingcreateDelegatedTokenWithConfig()- RFC 8693 service delegationcheckAuthWithConfig()- Verify + authorize with policies
Helper Functions:
createHS512Config()- Build HS512 config from base64url secretcreateEdDSASignConfig()- Build EdDSA sign config from JWKcreateEdDSAVerifyConfig()- Build EdDSA verify config from JWK
Added exports for all new explicit API functions and types.
25 tests covering:
- Token signing with explicit config
- Token verification with explicit config
- Delegation patterns (RFC 8693)
- Authorization policies
- Error cases (wrong issuer, audience, secret, expired tokens)
- Isolation (no environment pollution, multiple configs)
All tests pass! ✓
New Guide: docs/user-guide/explicit-config.md
- Complete API reference
- Use case examples
- Migration guide
- Best practices
Updated: README.md
- Added "Two APIs: Choose Your Style" section
- Highlighted new explicit config option
- Added link to new documentation
Example: examples/explicit-config-example.ts
- HS512 example
- EdDSA example
- Service delegation example
- Development environment setup
- Testing example
Before:
// Required .env file with JWT_SECRET_NAME, JWT_ISS, JWT_AUD
// Required bindEnv() middleware
// Hard to pass different configs to different servicesAfter:
// No .env file needed!
const config = {
alg: 'HS512' as const,
secret: new Uint8Array(32), // Simple dev secret
iss: 'http://localhost:3000',
aud: ['http://localhost:3001', 'http://localhost:3002'],
}
const token = await signWithConfig({ sub: 'dev-user' }, config)Before:
// Tests polluted environment variables
// Hard to isolate tests
// Mock process.env neededAfter:
describe('JWT tests', () => {
const testConfig = {
alg: 'HS512' as const,
secret: new Uint8Array(32),
iss: 'test-issuer',
aud: 'test-audience',
}
it('should work without env vars', async () => {
const token = await signWithConfig({ sub: 'test' }, testConfig)
const payload = await verifyWithConfig(token, testConfig)
expect(payload?.sub).toBe('test')
})
})Before:
// Difficult to use different configs per tenant
// Global state made this essentially impossibleAfter:
const tenantConfigs = new Map<string, HS512Config>()
tenantConfigs.set('tenant-a', createHS512Config(secretA, { ... }))
tenantConfigs.set('tenant-b', createHS512Config(secretB, { ... }))
const config = tenantConfigs.get(tenantId)
const token = await signWithConfig(claims, config)✅ 100% backward compatible
- Existing environment-based API unchanged
- No breaking changes to existing code
- Both APIs can be used simultaneously
- ✅ All tests pass (151 passed)
- ✅ No TypeScript errors
- ✅ Comprehensive JSDoc comments
- ✅ Complete type safety
- ✅ Zero environment dependencies in explicit API
The explicit API can now be used in the gateway auth.ts:
import { signWithConfig, createHS512Config } from '@chrislyons-dev/flarelette-jwt'
// Create config once at startup
const jwtConfig = createHS512Config(env.JWT_SECRET, {
iss: 'http://localhost:8787',
aud: ['http://localhost:8788', 'http://localhost:8789'],
ttlSeconds: 900,
})
// Use in auth endpoint
export async function mintToken(c: Context) {
// No need for bindEnv() middleware!
const token = await signWithConfig(
{
sub: 'user123',
permissions: ['read:data'],
},
jwtConfig
)
return c.json({ token })
}- Update flarelette-demo to use explicit API
- Consider adding EdDSA examples with real key generation
- Update flarelette-hono to support explicit config
- Add Python equivalent of explicit API
- Publish new version (v1.9.0)
- ✨ Added:
packages/flarelette-jwt-ts/src/explicit.ts(489 lines) - ✨ Added:
packages/flarelette-jwt-ts/tests/explicit.test.ts(470 lines) - ✨ Added:
docs/user-guide/explicit-config.md(complete guide) - ✨ Added:
examples/explicit-config-example.ts(example code) - 📝 Updated:
packages/flarelette-jwt-ts/src/index.ts(added exports) - 📝 Updated:
README.md(added new API section)
This change makes flarelette-jwt-kit significantly more developer-friendly while maintaining all production capabilities. Developers no longer need to bypass the library or struggle with environment setup complexity.
The explicit API provides a clear, testable, explicit alternative to the environment-based approach, making the library suitable for a wider range of use cases.