Skip to content

Commit e408455

Browse files
Copilothotlong
andcommitted
Add architecture documentation and usage examples
Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>
1 parent df11d09 commit e408455

2 files changed

Lines changed: 197 additions & 7 deletions

File tree

Lines changed: 176 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
1+
# Better-Auth Integration: Direct Forwarding Approach
2+
3+
## Decision Summary
4+
5+
**Chosen Approach:** Direct Request Forwarding
6+
**Implementation Date:** 2026-02-10
7+
**Status:** ✅ Implemented and Tested
8+
9+
## Problem Statement
10+
11+
When integrating the better-auth library (v1.4.18) into `@objectstack/plugin-auth`, we needed to decide between two architectural approaches:
12+
13+
1. **Direct Forwarding**: Forward all HTTP requests directly to better-auth's universal handler
14+
2. **Manual Implementation**: Implement wrapper methods for each authentication operation
15+
16+
## Analysis
17+
18+
### Better-Auth Architecture
19+
20+
Better-auth v1.4.18 provides a **universal handler** pattern:
21+
22+
```typescript
23+
type Auth = {
24+
handler: (request: Request) => Promise<Response>;
25+
api: InferAPI<...>;
26+
// ...
27+
}
28+
```
29+
30+
This handler:
31+
- Accepts Web standard `Request` objects
32+
- Returns Web standard `Response` objects
33+
- Handles ALL authentication routes internally
34+
- Is framework-agnostic (works with Next.js, Hono, Express, etc.)
35+
36+
### Hono Framework Compatibility
37+
38+
Our HTTP server uses Hono, which already uses Web standard Request/Response:
39+
- Hono Context provides `c.req.raw` → Web `Request`
40+
- Hono accepts Web `Response` objects directly
41+
- **No conversion needed!**
42+
43+
### Approach Comparison
44+
45+
| Aspect | Direct Forwarding ✅ | Manual Implementation |
46+
|--------|---------------------|----------------------|
47+
| Code Size | ~100 lines | ~250 lines |
48+
| Maintenance | Minimal - better-auth handles it | High - must sync with better-auth updates |
49+
| Features | All better-auth features automatic | Must implement each feature manually |
50+
| Type Safety | Full TypeScript from better-auth | Custom types, may drift |
51+
| Bug Risk | Low - using library as designed | High - custom code, edge cases |
52+
| Updates | Get better-auth updates automatically | Must update wrapper code |
53+
| OAuth Support | Built-in, configured via options | Must implement OAuth flows |
54+
| 2FA Support | Built-in, configured via options | Must implement 2FA logic |
55+
| Passkeys | Built-in, configured via options | Must implement WebAuthn |
56+
| Magic Links | Built-in, configured via options | Must implement email flows |
57+
58+
## Decision: Direct Forwarding
59+
60+
### Rationale
61+
62+
1. **Library Design Intent**: Better-auth's universal handler is the **recommended integration pattern**
63+
2. **Minimal Code**: ~150 lines removed, simpler to maintain
64+
3. **Full Feature Support**: All better-auth features work automatically
65+
4. **Future-Proof**: Better-auth updates require no code changes
66+
5. **Type Safety**: Full TypeScript support from better-auth
67+
6. **Standard Pattern**: Aligns with better-auth documentation examples
68+
69+
### Implementation
70+
71+
#### Before (Manual Approach)
72+
```typescript
73+
// Custom wrapper methods (200+ lines)
74+
httpServer.post('/auth/login', async (req, res) => {
75+
const result = await authManager.login(req.body);
76+
res.json(result);
77+
});
78+
79+
httpServer.post('/auth/register', async (req, res) => {
80+
const result = await authManager.register(req.body);
81+
res.json(result);
82+
});
83+
84+
// ... many more routes
85+
```
86+
87+
#### After (Direct Forwarding)
88+
```typescript
89+
// Single wildcard route (~30 lines)
90+
rawApp.all('/api/v1/auth/*', async (c) => {
91+
const request = c.req.raw; // Web Request
92+
const authPath = url.pathname.replace(basePath, '');
93+
const rewrittenRequest = new Request(authPath, { ... });
94+
const response = await authManager.handleRequest(rewrittenRequest);
95+
return response; // Web Response
96+
});
97+
```
98+
99+
### Trade-offs
100+
101+
**Given Up:**
102+
- Fine-grained control over individual routes
103+
- Ability to easily intercept/modify requests
104+
105+
**Solutions:**
106+
- Use Hono middleware for request interception if needed
107+
- Use better-auth plugins for custom behavior
108+
- Access `authManager.api` for programmatic operations
109+
110+
## Results
111+
112+
### Metrics
113+
- **Lines of Code Removed**: 156 (261 → 105 in auth-manager.ts)
114+
- **Test Coverage**: 11/11 tests passing
115+
- **Build Status**: ✅ Success
116+
- **Type Safety**: ✅ Full TypeScript support
117+
118+
### Features Enabled
119+
- ✅ Email/Password Authentication
120+
- ✅ OAuth Providers (Google, GitHub, etc.)
121+
- ✅ Session Management
122+
- ✅ Password Reset
123+
- ✅ Email Verification
124+
- ✅ 2FA (when enabled)
125+
- ✅ Passkeys (when enabled)
126+
- ✅ Magic Links (when enabled)
127+
- ✅ Organizations (when enabled)
128+
129+
## Usage Example
130+
131+
```typescript
132+
import { AuthPlugin } from '@objectstack/plugin-auth';
133+
134+
const plugin = new AuthPlugin({
135+
secret: process.env.AUTH_SECRET,
136+
baseUrl: 'http://localhost:3000',
137+
138+
// OAuth providers - just configuration, no implementation needed
139+
providers: [
140+
{
141+
id: 'google',
142+
clientId: process.env.GOOGLE_CLIENT_ID,
143+
clientSecret: process.env.GOOGLE_CLIENT_SECRET,
144+
}
145+
],
146+
147+
// Advanced features - just enable, no implementation needed
148+
plugins: {
149+
organization: true, // Multi-tenant support
150+
twoFactor: true, // 2FA
151+
passkeys: true, // WebAuthn
152+
magicLink: true, // Passwordless
153+
}
154+
});
155+
```
156+
157+
All better-auth endpoints work immediately:
158+
- `/api/v1/auth/sign-up/email`
159+
- `/api/v1/auth/sign-in/email`
160+
- `/api/v1/auth/authorize/google`
161+
- `/api/v1/auth/two-factor/enable`
162+
- `/api/v1/auth/passkey/register`
163+
- And many more...
164+
165+
## Lessons Learned
166+
167+
1. **Use Libraries as Designed**: Better-auth provides a universal handler for a reason
168+
2. **Less Code = Less Bugs**: The simplest solution is often the best
169+
3. **Trust the Framework**: Better-auth has battle-tested auth logic
170+
4. **Embrace Standards**: Web standard Request/Response makes integration seamless
171+
172+
## References
173+
174+
- [Better-Auth Documentation](https://www.better-auth.com/docs)
175+
- [PR #580](https://github.com/objectstack-ai/spec/pull/580) - Initial better-auth integration
176+
- Analysis Document: `/tmp/better-auth-approach-analysis.md`

packages/plugins/plugin-auth/examples/basic-usage.ts

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,9 @@
33
/**
44
* Auth Plugin Usage Example
55
*
6-
* This example demonstrates how to use the AuthPlugin
7-
* in an ObjectStack application.
6+
* This example demonstrates how to use the AuthPlugin with better-auth
7+
* in an ObjectStack application. All requests are forwarded directly
8+
* to better-auth's universal handler.
89
*/
910

1011
import { ObjectKernel } from '@objectstack/core';
@@ -68,11 +69,24 @@ async function main() {
6869
await kernel.start();
6970

7071
console.log('🚀 Server started with auth plugin');
71-
console.log('📍 Auth endpoints available at:');
72-
console.log(' - POST http://localhost:3000/api/v1/auth/login');
73-
console.log(' - POST http://localhost:3000/api/v1/auth/register');
74-
console.log(' - POST http://localhost:3000/api/v1/auth/logout');
75-
console.log(' - GET http://localhost:3000/api/v1/auth/session');
72+
console.log('📍 Better-auth endpoints available at:');
73+
console.log('');
74+
console.log(' Email/Password:');
75+
console.log(' - POST http://localhost:3000/api/v1/auth/sign-up/email');
76+
console.log(' - POST http://localhost:3000/api/v1/auth/sign-in/email');
77+
console.log(' - POST http://localhost:3000/api/v1/auth/sign-out');
78+
console.log(' - GET http://localhost:3000/api/v1/auth/get-session');
79+
console.log('');
80+
console.log(' Password Management:');
81+
console.log(' - POST http://localhost:3000/api/v1/auth/forget-password');
82+
console.log(' - POST http://localhost:3000/api/v1/auth/reset-password');
83+
console.log('');
84+
console.log(' OAuth (if configured):');
85+
console.log(' - GET http://localhost:3000/api/v1/auth/authorize/google');
86+
console.log(' - GET http://localhost:3000/api/v1/auth/authorize/github');
87+
console.log('');
88+
console.log(' See https://www.better-auth.com/docs for complete API reference');
89+
console.log('');
7690

7791
// Access the auth service from the kernel
7892
const authService = kernel.getService('auth');

0 commit comments

Comments
 (0)