Skip to content

Commit 923bb22

Browse files
fix: enhance explicit config functions for jwt signing (#25)
Add consumers to provide explicit configurations instead of relying on environment settings only Written-by: Chris Lyons
1 parent 9fdabf9 commit 923bb22

48 files changed

Lines changed: 5849 additions & 269 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

IMPLEMENTATION_SUMMARY.md

Lines changed: 269 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,269 @@
1+
# Implementation Summary: Explicit Configuration API
2+
3+
## Problem Statement
4+
5+
The flarelette-jwt-kit library had complex environment detection that made it difficult to use in development:
6+
7+
1. **Global State Dependency**: All functions read from `globalThis.__FLARELETTE_ENV` or `process.env`
8+
2. **Miniflare Complexity**: Required `bindEnv()` middleware to mutate global state before JWT operations
9+
3. **No Explicit Config**: Impossible to pass configuration directly as parameters
10+
4. **Testing Pain**: Hard to write isolated tests without environment pollution
11+
12+
This led to developers **bypassing the library entirely** and implementing JWT signing directly with Web Crypto API (like in bond-math).
13+
14+
## Solution: Dual API Approach
15+
16+
Added a new **explicit configuration API** that allows passing config objects directly, while keeping the existing environment-based API for backward compatibility.
17+
18+
### Environment-Based API (Original)
19+
20+
```typescript
21+
// Requires environment variables or bindEnv() middleware
22+
import { sign, verify } from '@chrislyons-dev/flarelette-jwt'
23+
24+
const token = await sign({ sub: 'user123' })
25+
const payload = await verify(token)
26+
```
27+
28+
**Use when:**
29+
30+
- Production deployment with Cloudflare bindings
31+
- Environment-based configuration is desired
32+
- Zero configuration code needed
33+
34+
### Explicit Configuration API (New) ✨
35+
36+
```typescript
37+
// No environment variables required!
38+
import {
39+
signWithConfig,
40+
verifyWithConfig,
41+
createHS512Config,
42+
} from '@chrislyons-dev/flarelette-jwt'
43+
44+
const config = createHS512Config('secret', {
45+
iss: 'https://gateway.example.com',
46+
aud: 'api.example.com',
47+
})
48+
49+
const token = await signWithConfig({ sub: 'user123' }, config)
50+
const payload = await verifyWithConfig(token, config)
51+
```
52+
53+
**Use when:**
54+
55+
- Development environment setup
56+
- Testing without environment pollution
57+
- Multiple JWT configurations needed
58+
- Explicit control over every parameter
59+
60+
## What Was Added
61+
62+
### 1. Core Module: `src/explicit.ts`
63+
64+
**Configuration Types:**
65+
66+
- `BaseJwtConfig` - Shared config (iss, aud, ttlSeconds, leeway)
67+
- `HS512Config` - Symmetric (shared secret) configuration
68+
- `EdDSASignConfig` - Asymmetric signing configuration
69+
- `EdDSAVerifyConfig` - Asymmetric verification configuration
70+
71+
**Core Functions:**
72+
73+
- `signWithConfig()` - Sign JWT with explicit config
74+
- `verifyWithConfig()` - Verify JWT with explicit config
75+
76+
**High-Level Functions:**
77+
78+
- `createTokenWithConfig()` - Convenience wrapper for signing
79+
- `createDelegatedTokenWithConfig()` - RFC 8693 service delegation
80+
- `checkAuthWithConfig()` - Verify + authorize with policies
81+
82+
**Helper Functions:**
83+
84+
- `createHS512Config()` - Build HS512 config from base64url secret
85+
- `createEdDSASignConfig()` - Build EdDSA sign config from JWK
86+
- `createEdDSAVerifyConfig()` - Build EdDSA verify config from JWK
87+
88+
### 2. Updated Exports: `src/index.ts`
89+
90+
Added exports for all new explicit API functions and types.
91+
92+
### 3. Comprehensive Tests: `tests/explicit.test.ts`
93+
94+
**25 tests covering:**
95+
96+
- Token signing with explicit config
97+
- Token verification with explicit config
98+
- Delegation patterns (RFC 8693)
99+
- Authorization policies
100+
- Error cases (wrong issuer, audience, secret, expired tokens)
101+
- Isolation (no environment pollution, multiple configs)
102+
103+
**All tests pass! ✓**
104+
105+
### 4. Documentation
106+
107+
**New Guide:** `docs/explicit-config.md`
108+
109+
- Complete API reference
110+
- Use case examples
111+
- Migration guide
112+
- Best practices
113+
114+
**Updated:** `README.md`
115+
116+
- Added "Two APIs: Choose Your Style" section
117+
- Highlighted new explicit config option
118+
- Added link to new documentation
119+
120+
**Example:** `examples/explicit-config-example.ts`
121+
122+
- HS512 example
123+
- EdDSA example
124+
- Service delegation example
125+
- Development environment setup
126+
- Testing example
127+
128+
## Benefits
129+
130+
### For Development
131+
132+
**Before:**
133+
134+
```typescript
135+
// Required .env file with JWT_SECRET_NAME, JWT_ISS, JWT_AUD
136+
// Required bindEnv() middleware
137+
// Hard to pass different configs to different services
138+
```
139+
140+
**After:**
141+
142+
```typescript
143+
// No .env file needed!
144+
const config = {
145+
alg: 'HS512' as const,
146+
secret: new Uint8Array(32), // Simple dev secret
147+
iss: 'http://localhost:3000',
148+
aud: ['http://localhost:3001', 'http://localhost:3002'],
149+
}
150+
151+
const token = await signWithConfig({ sub: 'dev-user' }, config)
152+
```
153+
154+
### For Testing
155+
156+
**Before:**
157+
158+
```typescript
159+
// Tests polluted environment variables
160+
// Hard to isolate tests
161+
// Mock process.env needed
162+
```
163+
164+
**After:**
165+
166+
```typescript
167+
describe('JWT tests', () => {
168+
const testConfig = {
169+
alg: 'HS512' as const,
170+
secret: new Uint8Array(32),
171+
iss: 'test-issuer',
172+
aud: 'test-audience',
173+
}
174+
175+
it('should work without env vars', async () => {
176+
const token = await signWithConfig({ sub: 'test' }, testConfig)
177+
const payload = await verifyWithConfig(token, testConfig)
178+
expect(payload?.sub).toBe('test')
179+
})
180+
})
181+
```
182+
183+
### For Multi-Tenant Apps
184+
185+
**Before:**
186+
187+
```typescript
188+
// Difficult to use different configs per tenant
189+
// Global state made this essentially impossible
190+
```
191+
192+
**After:**
193+
194+
```typescript
195+
const tenantConfigs = new Map<string, HS512Config>()
196+
tenantConfigs.set('tenant-a', createHS512Config(secretA, { ... }))
197+
tenantConfigs.set('tenant-b', createHS512Config(secretB, { ... }))
198+
199+
const config = tenantConfigs.get(tenantId)
200+
const token = await signWithConfig(claims, config)
201+
```
202+
203+
## Backward Compatibility
204+
205+
**100% backward compatible**
206+
207+
- Existing environment-based API unchanged
208+
- No breaking changes to existing code
209+
- Both APIs can be used simultaneously
210+
211+
## Code Quality
212+
213+
- ✅ All tests pass (151 passed)
214+
- ✅ No TypeScript errors
215+
- ✅ Comprehensive JSDoc comments
216+
- ✅ Complete type safety
217+
- ✅ Zero environment dependencies in explicit API
218+
219+
## Usage in flarelette-demo
220+
221+
The explicit API can now be used in the gateway auth.ts:
222+
223+
```typescript
224+
import { signWithConfig, createHS512Config } from '@chrislyons-dev/flarelette-jwt'
225+
226+
// Create config once at startup
227+
const jwtConfig = createHS512Config(env.JWT_SECRET, {
228+
iss: 'http://localhost:8787',
229+
aud: ['http://localhost:8788', 'http://localhost:8789'],
230+
ttlSeconds: 900,
231+
})
232+
233+
// Use in auth endpoint
234+
export async function mintToken(c: Context) {
235+
// No need for bindEnv() middleware!
236+
const token = await signWithConfig(
237+
{
238+
sub: 'user123',
239+
permissions: ['read:data'],
240+
},
241+
jwtConfig
242+
)
243+
244+
return c.json({ token })
245+
}
246+
```
247+
248+
## Next Steps
249+
250+
1. **Update flarelette-demo** to use explicit API
251+
2. **Consider adding EdDSA examples** with real key generation
252+
3. **Update flarelette-hono** to support explicit config
253+
4. **Add Python equivalent** of explicit API
254+
5. **Publish new version** (v1.9.0)
255+
256+
## Files Changed
257+
258+
-**Added:** `packages/flarelette-jwt-ts/src/explicit.ts` (489 lines)
259+
-**Added:** `packages/flarelette-jwt-ts/tests/explicit.test.ts` (470 lines)
260+
-**Added:** `docs/explicit-config.md` (complete guide)
261+
-**Added:** `examples/explicit-config-example.ts` (example code)
262+
- 📝 **Updated:** `packages/flarelette-jwt-ts/src/index.ts` (added exports)
263+
- 📝 **Updated:** `README.md` (added new API section)
264+
265+
## Impact
266+
267+
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.
268+
269+
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.

0 commit comments

Comments
 (0)