Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
269 changes: 269 additions & 0 deletions IMPLEMENTATION_SUMMARY.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,269 @@
# Implementation Summary: Explicit Configuration API

## Problem Statement

The flarelette-jwt-kit library had complex environment detection that made it difficult to use in development:

1. **Global State Dependency**: All functions read from `globalThis.__FLARELETTE_ENV` or `process.env`
2. **Miniflare Complexity**: Required `bindEnv()` middleware to mutate global state before JWT operations
3. **No Explicit Config**: Impossible to pass configuration directly as parameters
4. **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).

## Solution: Dual API Approach

Added a new **explicit configuration API** that allows passing config objects directly, while keeping the existing environment-based API for backward compatibility.

### Environment-Based API (Original)

```typescript
// 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

### Explicit Configuration API (New) ✨

```typescript
// 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

## What Was Added

### 1. Core Module: `src/explicit.ts`

**Configuration Types:**

- `BaseJwtConfig` - Shared config (iss, aud, ttlSeconds, leeway)
- `HS512Config` - Symmetric (shared secret) configuration
- `EdDSASignConfig` - Asymmetric signing configuration
- `EdDSAVerifyConfig` - Asymmetric verification configuration

**Core Functions:**

- `signWithConfig()` - Sign JWT with explicit config
- `verifyWithConfig()` - Verify JWT with explicit config

**High-Level Functions:**

- `createTokenWithConfig()` - Convenience wrapper for signing
- `createDelegatedTokenWithConfig()` - RFC 8693 service delegation
- `checkAuthWithConfig()` - Verify + authorize with policies

**Helper Functions:**

- `createHS512Config()` - Build HS512 config from base64url secret
- `createEdDSASignConfig()` - Build EdDSA sign config from JWK
- `createEdDSAVerifyConfig()` - Build EdDSA verify config from JWK

### 2. Updated Exports: `src/index.ts`

Added exports for all new explicit API functions and types.

### 3. Comprehensive Tests: `tests/explicit.test.ts`

**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! ✓**

### 4. Documentation

**New Guide:** `docs/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

## Benefits

### For Development

**Before:**

```typescript
// Required .env file with JWT_SECRET_NAME, JWT_ISS, JWT_AUD
// Required bindEnv() middleware
// Hard to pass different configs to different services
```

**After:**

```typescript
// 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)
```

### For Testing

**Before:**

```typescript
// Tests polluted environment variables
// Hard to isolate tests
// Mock process.env needed
```

**After:**

```typescript
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')
})
})
```

### For Multi-Tenant Apps

**Before:**

```typescript
// Difficult to use different configs per tenant
// Global state made this essentially impossible
```

**After:**

```typescript
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)
```

## Backward Compatibility

✅ **100% backward compatible**

- Existing environment-based API unchanged
- No breaking changes to existing code
- Both APIs can be used simultaneously

## Code Quality

- ✅ All tests pass (151 passed)
- ✅ No TypeScript errors
- ✅ Comprehensive JSDoc comments
- ✅ Complete type safety
- ✅ Zero environment dependencies in explicit API

## Usage in flarelette-demo

The explicit API can now be used in the gateway auth.ts:

```typescript
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 })
}
```

## Next Steps

1. **Update flarelette-demo** to use explicit API
2. **Consider adding EdDSA examples** with real key generation
3. **Update flarelette-hono** to support explicit config
4. **Add Python equivalent** of explicit API
5. **Publish new version** (v1.9.0)

## Files Changed

- ✨ **Added:** `packages/flarelette-jwt-ts/src/explicit.ts` (489 lines)
- ✨ **Added:** `packages/flarelette-jwt-ts/tests/explicit.test.ts` (470 lines)
- ✨ **Added:** `docs/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)

## Impact

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.
Loading