mulguard 1.1.6 → 1.1.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +210 -706
- package/dist/actions-CMtg7FGv.js +1 -0
- package/dist/{actions-DeCfLtHA.mjs → actions-CjQUKaXF.mjs} +54 -38
- package/dist/client/index.js +1 -1
- package/dist/client/index.mjs +84 -78
- package/dist/core/auth/email-password.d.ts +145 -0
- package/dist/core/auth/oauth/index.d.ts +14 -0
- package/dist/core/auth/oauth/oauth-handler.d.ts +172 -0
- package/dist/core/auth/oauth/pkce.d.ts +168 -0
- package/dist/core/auth/{oauth-providers.d.ts → oauth/providers.d.ts} +8 -7
- package/dist/core/auth/{oauth-state-store-cookie.d.ts → oauth/state-store-cookie.d.ts} +4 -4
- package/dist/core/auth/{oauth-state-store-redis.d.ts → oauth/state-store-redis.d.ts} +1 -1
- package/dist/core/auth/{oauth-state-store.d.ts → oauth/state-store.d.ts} +4 -1
- package/dist/core/auth/otp.d.ts +184 -0
- package/dist/core/errors/index.d.ts +269 -0
- package/dist/core/index.d.ts +1 -3
- package/dist/core/logger/index.d.ts +147 -0
- package/dist/core/mulguard/integration.d.ts +104 -0
- package/dist/core/mulguard/oauth-handler.d.ts +1 -1
- package/dist/core/security/security-manager.d.ts +236 -0
- package/dist/core/session/session-manager.d.ts +235 -0
- package/dist/core/types/index.d.ts +27 -5
- package/dist/index/index.js +1 -1
- package/dist/index/index.mjs +1388 -881
- package/dist/index.d.ts +3 -6
- package/dist/{client → nextjs/client}/hooks.d.ts +2 -2
- package/dist/nextjs/client/index.d.ts +13 -0
- package/dist/{client → nextjs/client}/provider.d.ts +1 -1
- package/dist/{client → nextjs/client}/server-actions-helper.d.ts +2 -2
- package/dist/{handlers → nextjs/handlers}/api.d.ts +1 -1
- package/dist/nextjs/handlers/index.d.ts +9 -0
- package/dist/{handlers → nextjs/handlers}/route.d.ts +1 -1
- package/dist/nextjs/index.d.ts +15 -0
- package/dist/nextjs/proxy/index.d.ts +149 -0
- package/dist/nextjs/server/actions.d.ts +30 -0
- package/dist/{server → nextjs/server}/auth.d.ts +6 -6
- package/dist/{server → nextjs/server}/cookies.d.ts +5 -6
- package/dist/nextjs/server/index.d.ts +18 -0
- package/dist/{server → nextjs/server}/oauth-state.d.ts +5 -3
- package/dist/{server → nextjs/server}/session-helpers.d.ts +1 -3
- package/dist/nextjs/server/session.d.ts +144 -0
- package/dist/oauth-state-Drwz6fES.js +1 -0
- package/dist/oauth-state-pdypStuS.mjs +210 -0
- package/dist/server/index.js +1 -1
- package/dist/server/index.mjs +27 -29
- package/package.json +64 -11
- package/dist/actions-CExpv_dD.js +0 -1
- package/dist/client/index.d.ts +0 -5
- package/dist/core/auth/index.d.ts +0 -40
- package/dist/core/auth/oauth.d.ts +0 -20
- package/dist/middleware/index.d.ts +0 -28
- package/dist/middleware/proxy.d.ts +0 -53
- package/dist/oauth-state-DKle8eCr.mjs +0 -289
- package/dist/oauth-state-DlvrCV11.js +0 -1
- package/dist/server/actions.d.ts +0 -86
- package/dist/server/helpers.d.ts +0 -10
- package/dist/server/index.d.ts +0 -14
- package/dist/server/middleware.d.ts +0 -39
- package/dist/server/session.d.ts +0 -28
- package/dist/server/utils.d.ts +0 -10
- /package/dist/{middleware → nextjs/proxy}/security.d.ts +0 -0
package/README.md
CHANGED
|
@@ -1,76 +1,66 @@
|
|
|
1
1
|
# Mulguard
|
|
2
2
|
|
|
3
|
-
> A modern,
|
|
3
|
+
> A modern, production-ready authentication library for Next.js 16+ (App Router)
|
|
4
4
|
|
|
5
5
|
[](LICENSE)
|
|
6
6
|
[](https://www.typescriptlang.org/)
|
|
7
|
-
[](https://nextjs.org/)
|
|
8
8
|
|
|
9
|
-
**Mulguard** is a powerful, flexible authentication library designed specifically for Next.js applications. Built with a backend-first architecture
|
|
9
|
+
**Mulguard** is a powerful, flexible authentication library designed specifically for Next.js 16+ applications. Built with a **backend-first architecture**, it gives you complete control over your authentication logic while providing a simple, intuitive API.
|
|
10
10
|
|
|
11
11
|
**Part of the [Mulverse](https://mulverse.com) ecosystem** - Empowering modern web applications with robust, secure authentication.
|
|
12
12
|
|
|
13
13
|
---
|
|
14
14
|
|
|
15
|
-
## Table of Contents
|
|
16
|
-
|
|
17
|
-
- [Features](#-features)
|
|
18
|
-
- [Installation](#-installation)
|
|
19
|
-
- [Quick Start](#-quick-start)
|
|
20
|
-
- [Core Concepts](#-core-concepts)
|
|
21
|
-
- [Authentication Methods](#-authentication-methods)
|
|
22
|
-
- [Configuration](#-configuration)
|
|
23
|
-
- [Server-Side Usage](#-server-side-usage)
|
|
24
|
-
- [Client-Side Usage](#-client-side-usage)
|
|
25
|
-
- [Components](#-components)
|
|
26
|
-
- [Security](#-security)
|
|
27
|
-
- [Backend Integration](#-backend-integration)
|
|
28
|
-
- [Advanced Features](#-advanced-features)
|
|
29
|
-
- [API Reference](#-api-reference)
|
|
30
|
-
- [Examples](#-examples)
|
|
31
|
-
- [Testing](#-testing)
|
|
32
|
-
- [Contributing](#-contributing)
|
|
33
|
-
- [License](#-license)
|
|
34
|
-
- [Support](#-support)
|
|
35
|
-
|
|
36
|
-
---
|
|
37
|
-
|
|
38
15
|
## ✨ Features
|
|
39
16
|
|
|
40
17
|
### 🔐 Multiple Authentication Methods
|
|
41
|
-
- **Email/Password** - Traditional
|
|
42
|
-
- **OAuth 2.
|
|
43
|
-
- **PassKey/WebAuthn** - Passwordless authentication with biometric support
|
|
44
|
-
- **Two-Factor Authentication (2FA/TOTP)** - Time-based one-time passwords with backup codes
|
|
18
|
+
- **Email/Password** - Traditional email and password authentication
|
|
19
|
+
- **OAuth 2.0 with PKCE** - Support for Google, GitHub, and other OAuth providers with automatic PKCE
|
|
45
20
|
- **OTP** - One-time password authentication
|
|
21
|
+
- **PassKey/WebAuthn** - Passwordless authentication (coming soon)
|
|
22
|
+
- **Two-Factor Authentication (2FA/TOTP)** - Multi-factor authentication support
|
|
46
23
|
|
|
47
|
-
###
|
|
48
|
-
-
|
|
49
|
-
-
|
|
50
|
-
-
|
|
51
|
-
-
|
|
24
|
+
### ⚡ Performance
|
|
25
|
+
- **Edge Runtime Support** - Optimized for Edge Runtime where possible
|
|
26
|
+
- **Session Caching** - Built-in session caching for improved performance
|
|
27
|
+
- **Optimized Bundle Size** - Tree-shakeable with minimal bundle impact
|
|
28
|
+
- **Server Actions First** - Leverages Next.js Server Actions for optimal performance
|
|
52
29
|
|
|
53
30
|
### 🔒 Security First
|
|
54
|
-
- **
|
|
55
|
-
- **
|
|
56
|
-
- **
|
|
57
|
-
- **
|
|
58
|
-
- **
|
|
59
|
-
- **
|
|
60
|
-
|
|
61
|
-
###
|
|
62
|
-
- **
|
|
63
|
-
- **
|
|
64
|
-
- **
|
|
65
|
-
- **
|
|
66
|
-
- **
|
|
67
|
-
|
|
68
|
-
### 🔌
|
|
69
|
-
- **
|
|
70
|
-
- **Custom Actions** - Implement your own authentication logic
|
|
71
|
-
- **
|
|
72
|
-
- **
|
|
73
|
-
|
|
31
|
+
- **HttpOnly Cookies** - Secure cookie handling prevents XSS attacks
|
|
32
|
+
- **PKCE for OAuth** - Automatic PKCE implementation for OAuth flows
|
|
33
|
+
- **CSRF Protection** - Built-in CSRF token validation
|
|
34
|
+
- **Rate Limiting** - Configurable rate limiting to prevent brute force attacks
|
|
35
|
+
- **Input Validation** - Comprehensive input validation and sanitization
|
|
36
|
+
- **XSS Prevention** - Automatic XSS protection for user inputs
|
|
37
|
+
|
|
38
|
+
### 🎯 Developer Experience
|
|
39
|
+
- **Type-Safe** - Full TypeScript support with generics and type inference
|
|
40
|
+
- **Simple Setup** - Get started in minutes with minimal configuration
|
|
41
|
+
- **Clear Errors** - Descriptive error messages with error codes
|
|
42
|
+
- **Centralized Logging** - Professional logging system (no manual `console.log` needed)
|
|
43
|
+
- **Unified API** - Single, consistent interface for all authentication methods
|
|
44
|
+
|
|
45
|
+
### 🔌 Flexible & Extensible
|
|
46
|
+
- **Backend-First** - Works with any backend or database (Prisma, Drizzle, MongoDB, etc.)
|
|
47
|
+
- **Custom Actions** - Implement your own authentication logic without adapters
|
|
48
|
+
- **Plugin System** - Extensible architecture for additional features
|
|
49
|
+
- **Next.js 16+ Proxy** - Uses Proxy pattern instead of middleware (Next.js 16+)
|
|
50
|
+
|
|
51
|
+
---
|
|
52
|
+
|
|
53
|
+
## 🎯 Why Mulguard?
|
|
54
|
+
|
|
55
|
+
### Problems We Solve
|
|
56
|
+
|
|
57
|
+
- ✅ **High Stability** - Production-ready library without common bugs
|
|
58
|
+
- ✅ **Simple Setup** - Quick and easy setup without complexity
|
|
59
|
+
- ✅ **No console.log Needed** - Professional centralized logging system
|
|
60
|
+
- ✅ **No SSR/CSR Issues** - Clear separation between server-side and client-side
|
|
61
|
+
- ✅ **Easy OAuth** - Simple OAuth setup with automatic PKCE
|
|
62
|
+
- ✅ **Excellent Error Handling** - Clear and coded error messages
|
|
63
|
+
- ✅ **Type Safety** - Full TypeScript support with generics
|
|
74
64
|
|
|
75
65
|
---
|
|
76
66
|
|
|
@@ -82,10 +72,8 @@ npm install mulguard
|
|
|
82
72
|
|
|
83
73
|
### Peer Dependencies
|
|
84
74
|
|
|
85
|
-
Mulguard requires the following peer dependencies:
|
|
86
|
-
|
|
87
75
|
```bash
|
|
88
|
-
npm install next@>=
|
|
76
|
+
npm install next@>=16.0.0 react@>=18.0.0 react-dom@>=18.0.0
|
|
89
77
|
```
|
|
90
78
|
|
|
91
79
|
---
|
|
@@ -103,19 +91,18 @@ import { db } from '@/lib/db'
|
|
|
103
91
|
import { comparePassword } from '@/lib/password'
|
|
104
92
|
|
|
105
93
|
export const auth = mulguard({
|
|
106
|
-
// Session configuration
|
|
94
|
+
// Session configuration (optional)
|
|
107
95
|
session: {
|
|
108
96
|
cookieName: '__mulguard_session',
|
|
109
97
|
expiresIn: 60 * 60 * 24 * 7, // 7 days
|
|
110
98
|
httpOnly: true,
|
|
111
|
-
secure: process.env.NODE_ENV === 'production',
|
|
99
|
+
secure: process.env.NODE_ENV === 'production',
|
|
112
100
|
sameSite: 'lax',
|
|
113
101
|
},
|
|
114
102
|
|
|
115
103
|
// Required: Implement your authentication actions
|
|
116
104
|
actions: {
|
|
117
105
|
signIn: {
|
|
118
|
-
// ✅ Unified interface: auth.signIn('credentials', {...}) or auth.signIn.email({...})
|
|
119
106
|
email: async (credentials: EmailCredentials): Promise<AuthResult> => {
|
|
120
107
|
// Your custom sign-in logic
|
|
121
108
|
const user = await db.user.findUnique({
|
|
@@ -146,14 +133,13 @@ export const auth = mulguard({
|
|
|
146
133
|
|
|
147
134
|
getSession: async (): Promise<Session | null> => {
|
|
148
135
|
// Your custom session retrieval logic
|
|
149
|
-
//
|
|
150
|
-
return
|
|
136
|
+
// The library handles cookie reading automatically
|
|
137
|
+
return null
|
|
151
138
|
},
|
|
152
139
|
|
|
153
|
-
signOut: async () => {
|
|
140
|
+
signOut: async (): Promise<void> => {
|
|
154
141
|
// Your custom sign-out logic
|
|
155
|
-
|
|
156
|
-
return { success: true }
|
|
142
|
+
// The library handles cookie deletion automatically
|
|
157
143
|
},
|
|
158
144
|
},
|
|
159
145
|
|
|
@@ -162,15 +148,10 @@ export const auth = mulguard({
|
|
|
162
148
|
oauth: {
|
|
163
149
|
google: {
|
|
164
150
|
clientId: process.env.GOOGLE_CLIENT_ID!,
|
|
165
|
-
clientSecret: process.env.GOOGLE_CLIENT_SECRET,
|
|
151
|
+
clientSecret: process.env.GOOGLE_CLIENT_SECRET,
|
|
166
152
|
redirectUri: `${process.env.NEXT_PUBLIC_URL}/api/auth/callback/google`,
|
|
167
153
|
scopes: ['openid', 'profile', 'email'],
|
|
168
|
-
|
|
169
|
-
github: {
|
|
170
|
-
clientId: process.env.GITHUB_CLIENT_ID!,
|
|
171
|
-
clientSecret: process.env.GITHUB_CLIENT_SECRET,
|
|
172
|
-
redirectUri: `${process.env.NEXT_PUBLIC_URL}/api/auth/callback/github`,
|
|
173
|
-
scopes: ['user:email'],
|
|
154
|
+
pkce: true, // Enable PKCE (recommended)
|
|
174
155
|
},
|
|
175
156
|
},
|
|
176
157
|
},
|
|
@@ -179,53 +160,26 @@ export const auth = mulguard({
|
|
|
179
160
|
callbacks: {
|
|
180
161
|
onSignIn: async (user, session) => {
|
|
181
162
|
// Track sign-in analytics, update last login, etc.
|
|
182
|
-
console.log('User signed in:', user.email)
|
|
183
163
|
},
|
|
184
164
|
onError: async (error, context) => {
|
|
185
|
-
// Log errors for monitoring
|
|
186
|
-
console.error(`Auth error in ${context}:`, error)
|
|
187
|
-
},
|
|
188
|
-
// ✅ OAuth callback: receives userInfo with accessToken, refreshToken, profile, etc.
|
|
189
|
-
onOAuthUser: async (userInfo, provider) => {
|
|
190
|
-
// Backend API example:
|
|
191
|
-
// const response = await fetch('/api/v1/auth/oauth/callback', {
|
|
192
|
-
// method: 'POST',
|
|
193
|
-
// body: JSON.stringify({
|
|
194
|
-
// provider,
|
|
195
|
-
// access_token: userInfo.accessToken,
|
|
196
|
-
// refresh_token: userInfo.refreshToken,
|
|
197
|
-
// profile: userInfo,
|
|
198
|
-
// email: userInfo.email,
|
|
199
|
-
// })
|
|
200
|
-
// })
|
|
201
|
-
// return await response.json()
|
|
202
|
-
|
|
203
|
-
// Database example:
|
|
204
|
-
let user = await db.user.findUnique({ where: { email: userInfo.email } })
|
|
205
|
-
if (!user) {
|
|
206
|
-
user = await db.user.create({
|
|
207
|
-
data: {
|
|
208
|
-
email: userInfo.email,
|
|
209
|
-
name: userInfo.name,
|
|
210
|
-
emailVerified: userInfo.emailVerified,
|
|
211
|
-
oauthProvider: provider,
|
|
212
|
-
}
|
|
213
|
-
})
|
|
214
|
-
}
|
|
215
|
-
return {
|
|
216
|
-
id: user.id,
|
|
217
|
-
email: user.email,
|
|
218
|
-
name: user.name,
|
|
219
|
-
emailVerified: user.emailVerified,
|
|
220
|
-
}
|
|
165
|
+
// Log errors for monitoring (centralized logging, no console.log needed)
|
|
221
166
|
},
|
|
222
167
|
},
|
|
223
168
|
})
|
|
224
169
|
```
|
|
225
170
|
|
|
226
|
-
|
|
171
|
+
### 2. Create Route Handler
|
|
172
|
+
|
|
173
|
+
Create `app/api/auth/[...mulguard]/route.ts`:
|
|
174
|
+
|
|
175
|
+
```typescript
|
|
176
|
+
import { toNextJsHandler } from 'mulguard/handlers/route'
|
|
177
|
+
import { auth } from '@/lib/auth'
|
|
178
|
+
|
|
179
|
+
export const { GET, POST } = toNextJsHandler(auth)
|
|
180
|
+
```
|
|
227
181
|
|
|
228
|
-
###
|
|
182
|
+
### 3. Server-Side Usage
|
|
229
183
|
|
|
230
184
|
```typescript
|
|
231
185
|
// app/dashboard/page.tsx
|
|
@@ -249,7 +203,7 @@ export default async function DashboardPage() {
|
|
|
249
203
|
}
|
|
250
204
|
```
|
|
251
205
|
|
|
252
|
-
###
|
|
206
|
+
### 4. Client-Side Usage
|
|
253
207
|
|
|
254
208
|
```typescript
|
|
255
209
|
// app/login/page.tsx
|
|
@@ -260,216 +214,112 @@ import { auth } from '@/lib/auth'
|
|
|
260
214
|
import { useState } from 'react'
|
|
261
215
|
|
|
262
216
|
export default function LoginPage() {
|
|
263
|
-
const { signIn,
|
|
264
|
-
const [
|
|
265
|
-
const [password, setPassword] = useState('')
|
|
217
|
+
const { signIn, isLoading } = useAuth(auth)
|
|
218
|
+
const [error, setError] = useState<string | null>(null)
|
|
266
219
|
|
|
267
|
-
const handleSubmit = async (e: React.FormEvent) => {
|
|
220
|
+
const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
|
|
268
221
|
e.preventDefault()
|
|
222
|
+
setError(null)
|
|
269
223
|
|
|
270
|
-
|
|
271
|
-
const result = await signIn('credentials', {
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
224
|
+
const formData = new FormData(e.currentTarget)
|
|
225
|
+
const result = await signIn('credentials', {
|
|
226
|
+
email: formData.get('email') as string,
|
|
227
|
+
password: formData.get('password') as string,
|
|
228
|
+
})
|
|
275
229
|
|
|
276
230
|
if (result.success) {
|
|
277
|
-
// Redirect to dashboard
|
|
278
231
|
window.location.href = '/dashboard'
|
|
279
232
|
} else {
|
|
280
|
-
|
|
281
|
-
alert(result.error)
|
|
233
|
+
setError(result.error || 'Login failed')
|
|
282
234
|
}
|
|
283
235
|
}
|
|
284
236
|
|
|
285
|
-
if (isLoading) return <div>Loading...</div>
|
|
286
|
-
if (session) return <div>Already logged in</div>
|
|
287
|
-
|
|
288
237
|
return (
|
|
289
238
|
<form onSubmit={handleSubmit}>
|
|
290
|
-
<input
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
/>
|
|
297
|
-
<input
|
|
298
|
-
type="password"
|
|
299
|
-
value={password}
|
|
300
|
-
onChange={(e) => setPassword(e.target.value)}
|
|
301
|
-
placeholder="Password"
|
|
302
|
-
required
|
|
303
|
-
/>
|
|
304
|
-
<button type="submit">Sign In</button>
|
|
239
|
+
<input name="email" type="email" placeholder="Email" required />
|
|
240
|
+
<input name="password" type="password" placeholder="Password" required />
|
|
241
|
+
{error && <p className="error">{error}</p>}
|
|
242
|
+
<button type="submit" disabled={isLoading}>
|
|
243
|
+
{isLoading ? 'Signing in...' : 'Sign In'}
|
|
244
|
+
</button>
|
|
305
245
|
</form>
|
|
306
246
|
)
|
|
307
247
|
}
|
|
308
248
|
```
|
|
309
249
|
|
|
310
|
-
###
|
|
250
|
+
### 5. Route Protection (Optional)
|
|
251
|
+
|
|
252
|
+
Create `proxy.ts` in your project root:
|
|
311
253
|
|
|
312
254
|
```typescript
|
|
313
|
-
//
|
|
314
|
-
import {
|
|
255
|
+
// proxy.ts (Next.js 16+)
|
|
256
|
+
import { createProxyMiddleware } from 'mulguard/proxy'
|
|
315
257
|
import { auth } from '@/lib/auth'
|
|
316
258
|
|
|
317
|
-
export default
|
|
259
|
+
export default createProxyMiddleware(auth, {
|
|
318
260
|
protectedRoutes: ['/dashboard', '/profile'],
|
|
319
261
|
redirectTo: '/login',
|
|
320
262
|
redirectIfAuthenticated: '/dashboard',
|
|
321
263
|
})
|
|
322
264
|
|
|
323
265
|
export const config = {
|
|
324
|
-
matcher: ['/
|
|
266
|
+
matcher: ['/((?!api|_next/static|_next/image|favicon.ico).*)'],
|
|
325
267
|
}
|
|
326
268
|
```
|
|
327
269
|
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
## 🎓 Core Concepts
|
|
331
|
-
|
|
332
|
-
### Backend-First Architecture
|
|
333
|
-
|
|
334
|
-
Mulguard follows a **backend-first** approach, meaning you implement your own authentication logic through **actions**. This gives you:
|
|
335
|
-
|
|
336
|
-
- **Full Control** - Complete control over your authentication flow
|
|
337
|
-
- **Flexibility** - Works with any backend architecture
|
|
338
|
-
- **No Lock-in** - No database adapter required
|
|
339
|
-
- **Custom Logic** - Implement business-specific authentication rules
|
|
340
|
-
|
|
341
|
-
### Actions
|
|
342
|
-
|
|
343
|
-
Actions are functions you provide to handle authentication operations:
|
|
344
|
-
|
|
345
|
-
```typescript
|
|
346
|
-
actions: {
|
|
347
|
-
signIn: {
|
|
348
|
-
email: async (credentials) => { /* ... */ },
|
|
349
|
-
oauth: async (provider) => { /* ... */ },
|
|
350
|
-
passkey: async (options) => { /* ... */ },
|
|
351
|
-
},
|
|
352
|
-
signUp: async (data) => { /* ... */ },
|
|
353
|
-
signOut: async () => { /* ... */ },
|
|
354
|
-
getSession: async () => { /* ... */ },
|
|
355
|
-
// ... more actions
|
|
356
|
-
}
|
|
357
|
-
```
|
|
358
|
-
|
|
359
|
-
### Sessions
|
|
360
|
-
|
|
361
|
-
Sessions are stored in HTTP-only cookies and contain:
|
|
362
|
-
|
|
363
|
-
```typescript
|
|
364
|
-
interface Session {
|
|
365
|
-
user: {
|
|
366
|
-
id: string
|
|
367
|
-
email: string
|
|
368
|
-
name?: string
|
|
369
|
-
avatar?: string
|
|
370
|
-
emailVerified?: boolean
|
|
371
|
-
}
|
|
372
|
-
expiresAt: Date
|
|
373
|
-
accessToken?: string
|
|
374
|
-
refreshToken?: string
|
|
375
|
-
tokenType?: string
|
|
376
|
-
expiresIn?: number
|
|
377
|
-
}
|
|
378
|
-
```
|
|
270
|
+
> **Note**: In Next.js 16+, `middleware.ts` has been replaced with `proxy.ts`. Mulguard uses the Proxy pattern for route protection.
|
|
379
271
|
|
|
380
272
|
---
|
|
381
273
|
|
|
382
|
-
##
|
|
274
|
+
## 📋 API Overview
|
|
383
275
|
|
|
384
276
|
### Unified Sign-In Interface
|
|
385
277
|
|
|
386
|
-
Mulguard provides a
|
|
278
|
+
Mulguard provides a unified, type-safe API for all authentication methods:
|
|
387
279
|
|
|
388
280
|
```typescript
|
|
389
281
|
// ✅ Unified interface (recommended)
|
|
390
|
-
await auth.signIn('credentials', { email
|
|
282
|
+
await auth.signIn('credentials', { email, password })
|
|
391
283
|
await auth.signIn('google')
|
|
392
|
-
await auth.signIn('otp', { email
|
|
393
|
-
await auth.signIn('passkey', { userId: 'user-id' })
|
|
284
|
+
await auth.signIn('otp', { email, code })
|
|
394
285
|
|
|
395
|
-
// ✅ Direct methods (
|
|
396
|
-
await auth.signIn.email({ email
|
|
286
|
+
// ✅ Direct methods (also available)
|
|
287
|
+
await auth.signIn.email({ email, password })
|
|
397
288
|
await auth.signIn.oauth('google')
|
|
398
|
-
await auth.signIn.otp('user@example.com', '123456')
|
|
399
289
|
```
|
|
400
290
|
|
|
401
|
-
###
|
|
291
|
+
### Type Safety
|
|
292
|
+
|
|
293
|
+
Full TypeScript support with generics:
|
|
402
294
|
|
|
403
295
|
```typescript
|
|
404
|
-
|
|
405
|
-
const result = await auth.signIn('credentials', {
|
|
406
|
-
email: 'user@example.com',
|
|
407
|
-
password: 'password123',
|
|
408
|
-
})
|
|
296
|
+
import type { User, Session } from 'mulguard'
|
|
409
297
|
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
298
|
+
const auth = mulguard<User, Session>({
|
|
299
|
+
// Type-safe configuration
|
|
300
|
+
actions: {
|
|
301
|
+
signIn: {
|
|
302
|
+
email: async (credentials) => {
|
|
303
|
+
// TypeScript knows the return type
|
|
304
|
+
return { success: true, user, session }
|
|
305
|
+
},
|
|
306
|
+
},
|
|
307
|
+
},
|
|
414
308
|
})
|
|
415
309
|
|
|
310
|
+
// Type-safe results
|
|
311
|
+
const result = await auth.signIn('credentials', { email, password })
|
|
416
312
|
if (result.success) {
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
console.error('Error:', result.error, result.errorCode)
|
|
313
|
+
// result.user and result.session are fully typed
|
|
314
|
+
console.log(result.user.email)
|
|
420
315
|
}
|
|
421
316
|
```
|
|
422
317
|
|
|
423
|
-
### OAuth Authentication
|
|
424
|
-
|
|
425
|
-
```typescript
|
|
426
|
-
// Initiate OAuth flow (unified interface)
|
|
427
|
-
const { url } = await auth.signIn('google')
|
|
428
|
-
window.location.href = url
|
|
429
|
-
|
|
430
|
-
// Or using direct method
|
|
431
|
-
const { url } = await auth.signIn.oauth('google')
|
|
432
|
-
window.location.href = url
|
|
433
|
-
|
|
434
|
-
// Handle callback (in your API route)
|
|
435
|
-
const result = await auth.oauthCallback(provider, code, state)
|
|
436
|
-
|
|
437
|
-
// ✅ OAuth providers are auto-configured when you add them to providers.oauth
|
|
438
|
-
// No need to implement oauth action manually!
|
|
439
|
-
```
|
|
440
|
-
|
|
441
|
-
### PassKey/WebAuthn Authentication
|
|
442
|
-
|
|
443
|
-
```typescript
|
|
444
|
-
// Register PassKey
|
|
445
|
-
const result = await auth.passkey.register({
|
|
446
|
-
name: 'My iPhone',
|
|
447
|
-
userId: 'user-id',
|
|
448
|
-
})
|
|
449
|
-
|
|
450
|
-
// Authenticate with PassKey
|
|
451
|
-
const result = await auth.signIn.passkey({ userId: 'user-id' })
|
|
452
|
-
```
|
|
453
|
-
|
|
454
|
-
### Two-Factor Authentication (2FA)
|
|
455
|
-
|
|
456
|
-
```typescript
|
|
457
|
-
// Enable 2FA
|
|
458
|
-
const enableResult = await auth.twoFactor.enable()
|
|
459
|
-
// Display QR code to user
|
|
460
|
-
|
|
461
|
-
// Verify 2FA code
|
|
462
|
-
const verifyResult = await auth.twoFactor.verify('123456')
|
|
463
|
-
|
|
464
|
-
// Check if 2FA is enabled
|
|
465
|
-
const isEnabled = await auth.twoFactor.isEnabled()
|
|
466
|
-
```
|
|
467
|
-
|
|
468
318
|
---
|
|
469
319
|
|
|
470
320
|
## ⚙️ Configuration
|
|
471
321
|
|
|
472
|
-
###
|
|
322
|
+
### Complete Configuration Example
|
|
473
323
|
|
|
474
324
|
```typescript
|
|
475
325
|
import { mulguard } from 'mulguard'
|
|
@@ -478,167 +328,130 @@ export const auth = mulguard({
|
|
|
478
328
|
// Session configuration
|
|
479
329
|
session: {
|
|
480
330
|
cookieName: '__mulguard_session',
|
|
481
|
-
expiresIn: 60 * 60 * 24 * 7, // 7 days in seconds
|
|
331
|
+
expiresIn: 60 * 60 * 24 * 7, // 7 days (in seconds)
|
|
482
332
|
httpOnly: true,
|
|
483
333
|
secure: process.env.NODE_ENV === 'production',
|
|
484
334
|
sameSite: 'lax',
|
|
485
335
|
path: '/',
|
|
486
|
-
domain: undefined,
|
|
487
|
-
cacheTtl: 5000, // Session cache TTL in milliseconds
|
|
488
336
|
},
|
|
489
|
-
|
|
490
|
-
// Required:
|
|
337
|
+
|
|
338
|
+
// Required: Custom authentication actions
|
|
491
339
|
actions: {
|
|
492
340
|
signIn: {
|
|
493
|
-
email: async (credentials) => {
|
|
494
|
-
|
|
341
|
+
email: async (credentials) => {
|
|
342
|
+
// Your custom logic
|
|
343
|
+
},
|
|
344
|
+
},
|
|
345
|
+
signUp: async (data) => {
|
|
346
|
+
// Your custom logic
|
|
347
|
+
},
|
|
348
|
+
getSession: async () => {
|
|
349
|
+
// Your custom logic
|
|
350
|
+
},
|
|
351
|
+
signOut: async () => {
|
|
352
|
+
// Your custom logic
|
|
495
353
|
},
|
|
496
|
-
getSession: async () => { /* ... */ },
|
|
497
|
-
signOut: async () => { /* ... */ },
|
|
498
|
-
// Optional: signUp, resetPassword, verifyEmail, refreshSession, etc.
|
|
499
354
|
},
|
|
500
|
-
|
|
355
|
+
|
|
501
356
|
// Optional: OAuth providers
|
|
502
357
|
providers: {
|
|
503
358
|
oauth: {
|
|
504
359
|
google: {
|
|
505
|
-
clientId:
|
|
506
|
-
clientSecret:
|
|
507
|
-
redirectUri:
|
|
360
|
+
clientId: process.env.GOOGLE_CLIENT_ID!,
|
|
361
|
+
clientSecret: process.env.GOOGLE_CLIENT_SECRET,
|
|
362
|
+
redirectUri: `${process.env.NEXT_PUBLIC_URL}/api/auth/callback/google`,
|
|
508
363
|
scopes: ['openid', 'profile', 'email'],
|
|
509
|
-
|
|
364
|
+
pkce: true,
|
|
510
365
|
},
|
|
511
366
|
github: {
|
|
512
|
-
clientId:
|
|
513
|
-
|
|
367
|
+
clientId: process.env.GITHUB_CLIENT_ID!,
|
|
368
|
+
clientSecret: process.env.GITHUB_CLIENT_SECRET,
|
|
369
|
+
redirectUri: `${process.env.NEXT_PUBLIC_URL}/api/auth/callback/github`,
|
|
514
370
|
scopes: ['user:email'],
|
|
371
|
+
pkce: true,
|
|
515
372
|
},
|
|
516
373
|
},
|
|
517
374
|
},
|
|
518
|
-
|
|
519
|
-
// Optional:
|
|
520
|
-
twoFactor: {
|
|
521
|
-
enabled: true,
|
|
522
|
-
},
|
|
523
|
-
|
|
524
|
-
// Optional: Account Picker
|
|
525
|
-
accountPicker: {
|
|
526
|
-
enabled: true,
|
|
527
|
-
maxAccounts: 3,
|
|
528
|
-
encryptStorage: true,
|
|
529
|
-
storageKey: '__mulguard_accounts',
|
|
530
|
-
},
|
|
531
|
-
|
|
532
|
-
// Optional: Security settings
|
|
375
|
+
|
|
376
|
+
// Optional: Security configuration
|
|
533
377
|
security: {
|
|
534
378
|
csrfProtection: true,
|
|
535
379
|
rateLimiting: {
|
|
380
|
+
enabled: true,
|
|
536
381
|
maxAttempts: 5,
|
|
537
382
|
windowMs: 15 * 60 * 1000, // 15 minutes
|
|
538
383
|
},
|
|
539
384
|
},
|
|
540
|
-
|
|
385
|
+
|
|
541
386
|
// Optional: Callbacks
|
|
542
387
|
callbacks: {
|
|
543
388
|
onSignIn: async (user, session) => {
|
|
544
|
-
// Called after successful sign
|
|
389
|
+
// Called after successful sign in
|
|
545
390
|
},
|
|
546
391
|
onSignOut: async (user) => {
|
|
547
|
-
// Called after sign
|
|
392
|
+
// Called after sign out
|
|
548
393
|
},
|
|
549
|
-
|
|
550
|
-
//
|
|
394
|
+
onOAuthUser: async (userInfo, provider) => {
|
|
395
|
+
// Handle OAuth user creation/update
|
|
396
|
+
// Return user object for session
|
|
551
397
|
},
|
|
552
398
|
onError: async (error, context) => {
|
|
553
|
-
//
|
|
399
|
+
// Handle errors
|
|
554
400
|
},
|
|
555
401
|
},
|
|
556
|
-
|
|
557
|
-
// Optional: Token refresh configuration
|
|
558
|
-
tokenRefresh: {
|
|
559
|
-
enabled: true,
|
|
560
|
-
refreshThreshold: 300, // 5 minutes before expiration
|
|
561
|
-
maxRetries: 0,
|
|
562
|
-
retryDelay: 1000,
|
|
563
|
-
rateLimit: 1, // 1 attempt per minute
|
|
564
|
-
autoSignOutOnFailure: true,
|
|
565
|
-
redirectToLogin: '/login',
|
|
566
|
-
},
|
|
567
402
|
})
|
|
568
403
|
```
|
|
569
404
|
|
|
570
405
|
---
|
|
571
406
|
|
|
572
|
-
##
|
|
407
|
+
## 📖 Examples
|
|
573
408
|
|
|
574
|
-
###
|
|
409
|
+
### OAuth Sign-In
|
|
575
410
|
|
|
576
411
|
```typescript
|
|
577
|
-
|
|
412
|
+
// app/login/page.tsx
|
|
413
|
+
'use client'
|
|
414
|
+
|
|
415
|
+
import { useAuth } from 'mulguard/client'
|
|
578
416
|
import { auth } from '@/lib/auth'
|
|
579
417
|
|
|
580
|
-
|
|
581
|
-
const
|
|
418
|
+
export default function LoginPage() {
|
|
419
|
+
const { signIn } = useAuth(auth)
|
|
582
420
|
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
if (!session) {
|
|
587
|
-
return new Response('Unauthorized', { status: 401 })
|
|
421
|
+
const handleGoogleSignIn = async () => {
|
|
422
|
+
const { url } = await signIn('google')
|
|
423
|
+
window.location.href = url
|
|
588
424
|
}
|
|
589
|
-
return Response.json({ user: session.user })
|
|
590
|
-
}
|
|
591
|
-
```
|
|
592
|
-
|
|
593
|
-
### Server Actions
|
|
594
|
-
|
|
595
|
-
```typescript
|
|
596
|
-
'use server'
|
|
597
|
-
|
|
598
|
-
import { auth } from '@/lib/auth'
|
|
599
425
|
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
426
|
+
return (
|
|
427
|
+
<div>
|
|
428
|
+
<button onClick={handleGoogleSignIn}>
|
|
429
|
+
Sign in with Google
|
|
430
|
+
</button>
|
|
431
|
+
</div>
|
|
432
|
+
)
|
|
603
433
|
}
|
|
604
434
|
```
|
|
605
435
|
|
|
606
|
-
###
|
|
436
|
+
### Protected Route with Role Check
|
|
607
437
|
|
|
608
438
|
```typescript
|
|
609
|
-
// app/
|
|
610
|
-
import {
|
|
439
|
+
// app/admin/page.tsx
|
|
440
|
+
import { requireRole } from 'mulguard/server'
|
|
611
441
|
import { auth } from '@/lib/auth'
|
|
612
442
|
|
|
613
|
-
export
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
---
|
|
617
|
-
|
|
618
|
-
## 💻 Client-Side Usage
|
|
619
|
-
|
|
620
|
-
### React Hooks
|
|
621
|
-
|
|
622
|
-
```typescript
|
|
623
|
-
'use client'
|
|
624
|
-
|
|
625
|
-
import { useAuth, useSession } from 'mulguard/client'
|
|
626
|
-
import { auth } from '@/lib/auth'
|
|
627
|
-
|
|
628
|
-
export function AuthComponent() {
|
|
629
|
-
const { signIn, signOut, isLoading } = useAuth(auth)
|
|
630
|
-
const { session, isLoading: sessionLoading } = useSession(auth)
|
|
443
|
+
export default async function AdminPage() {
|
|
444
|
+
const session = await requireRole(auth, 'admin', '/unauthorized')
|
|
631
445
|
|
|
632
|
-
//
|
|
446
|
+
// If user doesn't have 'admin' role, redirects to /unauthorized
|
|
447
|
+
return <div>Admin Dashboard</div>
|
|
633
448
|
}
|
|
634
449
|
```
|
|
635
450
|
|
|
636
|
-
###
|
|
451
|
+
### Using Provider (Recommended)
|
|
637
452
|
|
|
638
453
|
```typescript
|
|
639
454
|
// app/layout.tsx
|
|
640
|
-
'use client'
|
|
641
|
-
|
|
642
455
|
import { MulguardProvider } from 'mulguard/client'
|
|
643
456
|
import { auth } from '@/lib/auth'
|
|
644
457
|
|
|
@@ -655,339 +468,57 @@ export default function RootLayout({ children }) {
|
|
|
655
468
|
}
|
|
656
469
|
```
|
|
657
470
|
|
|
658
|
-
> **Note:** `AuthProvider` is still available for backward compatibility but is deprecated. Use `MulguardProvider` instead.
|
|
659
|
-
|
|
660
|
-
---
|
|
661
|
-
|
|
662
|
-
## 🎨 Components
|
|
663
|
-
|
|
664
|
-
### Account Picker
|
|
665
|
-
|
|
666
|
-
```typescript
|
|
667
|
-
import { AccountPicker } from 'mulguard'
|
|
668
|
-
|
|
669
|
-
<AccountPicker
|
|
670
|
-
auth={auth}
|
|
671
|
-
onSelectUser={(user) => {
|
|
672
|
-
// Pre-fill email or handle quick login
|
|
673
|
-
setEmail(user.email)
|
|
674
|
-
}}
|
|
675
|
-
onUseDifferentAccount={() => {
|
|
676
|
-
// Show full login form
|
|
677
|
-
}}
|
|
678
|
-
/>
|
|
679
|
-
```
|
|
680
|
-
|
|
681
|
-
### OAuth Button
|
|
682
|
-
|
|
683
|
-
```typescript
|
|
684
|
-
import { OAuthButton } from 'mulguard'
|
|
685
|
-
|
|
686
|
-
<OAuthButton
|
|
687
|
-
auth={auth}
|
|
688
|
-
provider="google"
|
|
689
|
-
onSuccess={() => {
|
|
690
|
-
// Handle success
|
|
691
|
-
}}
|
|
692
|
-
onError={(error) => {
|
|
693
|
-
// Handle error
|
|
694
|
-
}}
|
|
695
|
-
>
|
|
696
|
-
Sign in with Google
|
|
697
|
-
</OAuthButton>
|
|
698
|
-
```
|
|
699
|
-
|
|
700
|
-
### Two-Factor Setup
|
|
701
|
-
|
|
702
|
-
```typescript
|
|
703
|
-
import { TwoFactorSetup } from 'mulguard'
|
|
704
|
-
|
|
705
|
-
<TwoFactorSetup
|
|
706
|
-
auth={auth}
|
|
707
|
-
onSuccess={() => {
|
|
708
|
-
// 2FA enabled
|
|
709
|
-
}}
|
|
710
|
-
/>
|
|
711
|
-
```
|
|
712
|
-
|
|
713
|
-
### PassKey Components
|
|
714
|
-
|
|
715
471
|
```typescript
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
// Register PassKey
|
|
719
|
-
<PassKeyRegister auth={auth} />
|
|
720
|
-
|
|
721
|
-
// Authenticate with PassKey
|
|
722
|
-
<PassKeyButton auth={auth} />
|
|
723
|
-
```
|
|
724
|
-
|
|
725
|
-
---
|
|
726
|
-
|
|
727
|
-
## 🔒 Security
|
|
728
|
-
|
|
729
|
-
Mulguard implements **comprehensive security features** with automatic input validation and sanitization:
|
|
730
|
-
|
|
731
|
-
### ✅ Built-in Security Features
|
|
732
|
-
|
|
733
|
-
#### Input Validation & Sanitization
|
|
734
|
-
- **Automatic email validation** - Validates and sanitizes email addresses
|
|
735
|
-
- **Password length checks** - Prevents DoS attacks with extremely long passwords
|
|
736
|
-
- **Provider validation** - Sanitizes OAuth provider strings to prevent injection
|
|
737
|
-
- **OTP code validation** - Validates OTP code format before processing
|
|
738
|
-
- **XSS Prevention** - Automatic HTML escaping and input sanitization
|
|
739
|
-
|
|
740
|
-
#### CSRF Protection
|
|
741
|
-
- Token-based CSRF protection for OAuth flows
|
|
742
|
-
- State parameter validation with constant-time comparison
|
|
743
|
-
- Secure state storage (pluggable for production - Redis, Database, etc.)
|
|
744
|
-
|
|
745
|
-
#### Error Handling
|
|
746
|
-
- **Generic error messages** - Prevents information disclosure
|
|
747
|
-
- **Error codes** - Programmatic error handling without exposing details
|
|
748
|
-
- **Security logging** - Logs authentication events (with sensitive data masked)
|
|
749
|
-
|
|
750
|
-
#### Secure Cookies
|
|
751
|
-
- HttpOnly cookies (prevents JavaScript access)
|
|
752
|
-
- Secure flag in production (HTTPS only)
|
|
753
|
-
- SameSite attribute (CSRF protection)
|
|
754
|
-
- Configurable expiration
|
|
755
|
-
|
|
756
|
-
#### Rate Limiting
|
|
757
|
-
- Configurable rate limiting
|
|
758
|
-
- Client-side tracking
|
|
759
|
-
- Automatic throttling
|
|
760
|
-
- Circuit breaker pattern for token refresh
|
|
761
|
-
|
|
762
|
-
### Security Best Practices
|
|
472
|
+
// app/profile/page.tsx
|
|
473
|
+
'use client'
|
|
763
474
|
|
|
764
|
-
|
|
765
|
-
// ✅ Good: Generic error messages
|
|
766
|
-
return {
|
|
767
|
-
success: false,
|
|
768
|
-
error: 'Invalid credentials',
|
|
769
|
-
errorCode: 'INVALID_CREDENTIALS'
|
|
770
|
-
}
|
|
475
|
+
import { useAuthFromContext } from 'mulguard/client'
|
|
771
476
|
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
477
|
+
export default function ProfilePage() {
|
|
478
|
+
const { session, signOut } = useAuthFromContext()
|
|
479
|
+
|
|
480
|
+
// No need to pass auth instance
|
|
481
|
+
return <div>...</div>
|
|
776
482
|
}
|
|
777
483
|
```
|
|
778
484
|
|
|
779
|
-
### Security Logging
|
|
780
|
-
|
|
781
|
-
All authentication events are automatically logged with sensitive data masked:
|
|
782
|
-
|
|
783
|
-
```typescript
|
|
784
|
-
// Logs show: "Sign in successful" with email: "use***"
|
|
785
|
-
// Never logs full email addresses or passwords
|
|
786
|
-
```
|
|
787
|
-
|
|
788
|
-
For detailed security documentation, see the [Security Guide](./docs/SECURITY.md) and [Complete Guide](./GUIDE.md).
|
|
789
|
-
|
|
790
485
|
---
|
|
791
486
|
|
|
792
|
-
##
|
|
487
|
+
## 🏗️ Architecture
|
|
793
488
|
|
|
794
|
-
Mulguard
|
|
489
|
+
Mulguard follows a **modular, backend-first architecture**:
|
|
795
490
|
|
|
796
|
-
-
|
|
797
|
-
-
|
|
798
|
-
-
|
|
799
|
-
- Integrate with third-party services
|
|
491
|
+
- **Core Layer** - Framework-agnostic authentication logic
|
|
492
|
+
- **Next.js Integration** - Server Actions, Server Components, Proxy middleware
|
|
493
|
+
- **Plugin System** - Extensible features (MFA, Account Picker, etc.)
|
|
800
494
|
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
import { comparePassword, hashPassword } from '@/lib/password'
|
|
807
|
-
|
|
808
|
-
export const auth = mulguard({
|
|
809
|
-
actions: {
|
|
810
|
-
signIn: {
|
|
811
|
-
email: async ({ email, password }) => {
|
|
812
|
-
const user = await db.user.findUnique({ where: { email } })
|
|
813
|
-
|
|
814
|
-
if (!user || !await comparePassword(password, user.password)) {
|
|
815
|
-
return { success: false, error: 'Invalid credentials' }
|
|
816
|
-
}
|
|
817
|
-
|
|
818
|
-
return {
|
|
819
|
-
success: true,
|
|
820
|
-
user: {
|
|
821
|
-
id: user.id,
|
|
822
|
-
email: user.email,
|
|
823
|
-
name: user.name,
|
|
824
|
-
},
|
|
825
|
-
session: {
|
|
826
|
-
user: { /* ... */ },
|
|
827
|
-
expiresAt: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000),
|
|
828
|
-
},
|
|
829
|
-
}
|
|
830
|
-
},
|
|
831
|
-
},
|
|
832
|
-
// ... more actions
|
|
833
|
-
},
|
|
834
|
-
})
|
|
835
|
-
```
|
|
836
|
-
|
|
837
|
-
### Example: API Integration
|
|
838
|
-
|
|
839
|
-
```typescript
|
|
840
|
-
import { mulguard } from 'mulguard'
|
|
841
|
-
|
|
842
|
-
export const auth = mulguard({
|
|
843
|
-
actions: {
|
|
844
|
-
signIn: {
|
|
845
|
-
email: async ({ email, password }) => {
|
|
846
|
-
const response = await fetch('https://api.example.com/auth/signin', {
|
|
847
|
-
method: 'POST',
|
|
848
|
-
headers: { 'Content-Type': 'application/json' },
|
|
849
|
-
body: JSON.stringify({ email, password }),
|
|
850
|
-
})
|
|
851
|
-
|
|
852
|
-
if (!response.ok) {
|
|
853
|
-
return { success: false, error: 'Authentication failed' }
|
|
854
|
-
}
|
|
855
|
-
|
|
856
|
-
const data = await response.json()
|
|
857
|
-
return {
|
|
858
|
-
success: true,
|
|
859
|
-
user: data.user,
|
|
860
|
-
session: data.session,
|
|
861
|
-
}
|
|
862
|
-
},
|
|
863
|
-
},
|
|
864
|
-
},
|
|
865
|
-
})
|
|
866
|
-
```
|
|
495
|
+
The library is designed to be:
|
|
496
|
+
- **Framework Agnostic** - Core logic works with any framework
|
|
497
|
+
- **Backend-First** - You implement your own database logic
|
|
498
|
+
- **Type-Safe** - Full TypeScript support throughout
|
|
499
|
+
- **Secure by Default** - Security features enabled by default
|
|
867
500
|
|
|
868
501
|
---
|
|
869
502
|
|
|
870
|
-
##
|
|
871
|
-
|
|
872
|
-
### Token Refresh
|
|
873
|
-
|
|
874
|
-
Automatic token refresh is built-in when you provide a `refreshSession` action:
|
|
875
|
-
|
|
876
|
-
```typescript
|
|
877
|
-
actions: {
|
|
878
|
-
refreshSession: async () => {
|
|
879
|
-
// Your token refresh logic
|
|
880
|
-
const newSession = await yourApi.refreshToken()
|
|
881
|
-
return newSession
|
|
882
|
-
},
|
|
883
|
-
}
|
|
884
|
-
```
|
|
885
|
-
|
|
886
|
-
### Custom Callbacks
|
|
887
|
-
|
|
888
|
-
```typescript
|
|
889
|
-
callbacks: {
|
|
890
|
-
onSignIn: async (user, session) => {
|
|
891
|
-
// Track sign-in analytics
|
|
892
|
-
await analytics.track('user_signed_in', { userId: user.id })
|
|
893
|
-
},
|
|
894
|
-
onSignOut: async (user) => {
|
|
895
|
-
// Cleanup logic
|
|
896
|
-
await cleanupUserSession(user.id)
|
|
897
|
-
},
|
|
898
|
-
onError: async (error, context) => {
|
|
899
|
-
// Error logging
|
|
900
|
-
console.error(`Auth error in ${context}:`, error)
|
|
901
|
-
},
|
|
902
|
-
}
|
|
903
|
-
```
|
|
904
|
-
|
|
905
|
-
### OAuth State Store
|
|
906
|
-
|
|
907
|
-
For production, use a persistent OAuth state store:
|
|
908
|
-
|
|
909
|
-
```typescript
|
|
910
|
-
import { createRedisOAuthStateStore } from 'mulguard'
|
|
911
|
-
|
|
912
|
-
const auth = mulguard({
|
|
913
|
-
oauthStateStore: createRedisOAuthStateStore(redisClient),
|
|
914
|
-
// ... rest of config
|
|
915
|
-
})
|
|
916
|
-
```
|
|
917
|
-
|
|
918
|
-
---
|
|
919
|
-
|
|
920
|
-
## 📚 API Reference
|
|
921
|
-
|
|
922
|
-
### Core Functions
|
|
923
|
-
|
|
924
|
-
#### `mulguard(config: MulguardConfig): MulguardInstance`
|
|
925
|
-
|
|
926
|
-
Creates a new Mulguard authentication instance.
|
|
927
|
-
|
|
928
|
-
#### `getServerSession(auth: MulguardInstance): Promise<Session | null>`
|
|
929
|
-
|
|
930
|
-
Gets the current session on the server.
|
|
931
|
-
|
|
932
|
-
### Client Hooks
|
|
933
|
-
|
|
934
|
-
#### `useAuth(auth: MulguardInstance)`
|
|
935
|
-
|
|
936
|
-
Returns authentication methods and state.
|
|
937
|
-
|
|
938
|
-
```typescript
|
|
939
|
-
const { signIn, signOut, isLoading } = useAuth(auth)
|
|
940
|
-
```
|
|
941
|
-
|
|
942
|
-
#### `useSession(auth: MulguardInstance)`
|
|
943
|
-
|
|
944
|
-
Returns the current session.
|
|
945
|
-
|
|
946
|
-
```typescript
|
|
947
|
-
const { session, isLoading } = useSession(auth)
|
|
948
|
-
```
|
|
949
|
-
|
|
950
|
-
### Instance Methods
|
|
951
|
-
|
|
952
|
-
#### `auth.getSession(): Promise<Session | null>`
|
|
953
|
-
|
|
954
|
-
Gets the current session.
|
|
955
|
-
|
|
956
|
-
#### `auth.signIn.email(credentials): Promise<AuthResult>`
|
|
957
|
-
|
|
958
|
-
Signs in with email and password.
|
|
959
|
-
|
|
960
|
-
#### `auth.signIn.oauth(provider): Promise<{ url: string; state: string }>`
|
|
961
|
-
|
|
962
|
-
Initiates OAuth flow.
|
|
963
|
-
|
|
964
|
-
#### `auth.signOut(): Promise<{ success: boolean }>`
|
|
503
|
+
## 🔒 Security
|
|
965
504
|
|
|
966
|
-
|
|
505
|
+
Mulguard implements **comprehensive security features**:
|
|
967
506
|
|
|
968
|
-
|
|
507
|
+
- ✅ **HttpOnly Cookies** - Prevents XSS attacks
|
|
508
|
+
- ✅ **Secure Flag** - HTTPS-only cookies in production
|
|
509
|
+
- ✅ **SameSite=Strict** - CSRF protection
|
|
510
|
+
- ✅ **PKCE for OAuth** - Prevents code interception attacks
|
|
511
|
+
- ✅ **Rate Limiting** - Prevents brute force attacks
|
|
512
|
+
- ✅ **Input Validation** - Prevents injection attacks
|
|
513
|
+
- ✅ **XSS Prevention** - Automatic XSS protection
|
|
514
|
+
- ✅ **Generic Error Messages** - Prevents information disclosure
|
|
969
515
|
|
|
970
516
|
---
|
|
971
517
|
|
|
972
|
-
##
|
|
973
|
-
|
|
974
|
-
### Complete Guide
|
|
975
|
-
|
|
976
|
-
📚 **[Complete Usage Guide](./GUIDE.md)** - Comprehensive guide with:
|
|
977
|
-
- Best practices for writing `auth.ts`
|
|
978
|
-
- Production-ready examples
|
|
979
|
-
- Security recommendations
|
|
980
|
-
- Backend integration patterns
|
|
981
|
-
- Advanced usage examples
|
|
518
|
+
## 📚 Documentation
|
|
982
519
|
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
- **[Basic Email Authentication](./src/examples/email-auth.ts)** - Simple email/password setup
|
|
986
|
-
- **[OAuth Authentication](./src/examples/oauth-auth.ts)** - Google, GitHub, etc.
|
|
987
|
-
- **[PassKey Authentication](./src/examples/passkey-auth.ts)** - WebAuthn/PassKey setup
|
|
988
|
-
- **[Two-Factor Authentication](./src/examples/two-factor-auth.ts)** - 2FA/TOTP implementation
|
|
989
|
-
- **[Middleware Integration](./src/examples/middleware-example.ts)** - Next.js middleware
|
|
990
|
-
- **[Server Components](./src/examples/server-component-example.tsx)** - Server-side usage
|
|
520
|
+
- **[GUIDE.md](./GUIDE.md)** - Complete usage guide with examples
|
|
521
|
+
- **[API Reference](./docs/API_DESIGN.md)** - Full API documentation
|
|
991
522
|
|
|
992
523
|
---
|
|
993
524
|
|
|
@@ -1019,29 +550,13 @@ Contributions are welcome! Please read our contributing guidelines before submit
|
|
|
1019
550
|
4. Push to the branch (`git push origin feature/amazing-feature`)
|
|
1020
551
|
5. Open a Pull Request
|
|
1021
552
|
|
|
1022
|
-
### Development Setup
|
|
1023
|
-
|
|
1024
|
-
```bash
|
|
1025
|
-
# Clone the repository
|
|
1026
|
-
git clone https://github.com/mulverse/mulguard.git
|
|
1027
|
-
|
|
1028
|
-
# Install dependencies
|
|
1029
|
-
npm install
|
|
1030
|
-
|
|
1031
|
-
# Run development build
|
|
1032
|
-
npm run dev
|
|
1033
|
-
|
|
1034
|
-
# Run tests
|
|
1035
|
-
npm test
|
|
1036
|
-
```
|
|
1037
|
-
|
|
1038
553
|
---
|
|
1039
554
|
|
|
1040
555
|
## 📝 License
|
|
1041
556
|
|
|
1042
557
|
This project is licensed under the **MUV (Mulverse M.U.V General Public License)**.
|
|
1043
558
|
|
|
1044
|
-
Copyright (C)
|
|
559
|
+
Copyright (C) 2024 Mulverse Inc.
|
|
1045
560
|
|
|
1046
561
|
See the [LICENSE](./LICENSE) file for details.
|
|
1047
562
|
|
|
@@ -1049,22 +564,10 @@ See the [LICENSE](./LICENSE) file for details.
|
|
|
1049
564
|
|
|
1050
565
|
## 💬 Support
|
|
1051
566
|
|
|
1052
|
-
- **📖
|
|
1053
|
-
- **📚 Documentation**: Check the [docs](./docs) directory for detailed guides
|
|
567
|
+
- **📖 Documentation**: Check the [GUIDE.md](./GUIDE.md) for detailed usage examples
|
|
1054
568
|
- **🐛 Issues**: [GitHub Issues](https://github.com/mulverse/mulguard/issues)
|
|
1055
569
|
- **💬 Discussions**: [GitHub Discussions](https://github.com/mulverse/mulguard/discussions)
|
|
1056
570
|
|
|
1057
|
-
## 🎯 Key Features & Improvements
|
|
1058
|
-
|
|
1059
|
-
### ✨ Latest Updates
|
|
1060
|
-
|
|
1061
|
-
- **✅ Unified Sign-In Interface** - Use `auth.signIn('credentials', {...})` or `auth.signIn.email({...})`
|
|
1062
|
-
- **✅ Automatic Input Validation** - Built-in email, password, and provider validation
|
|
1063
|
-
- **✅ Enhanced Security** - Generic error messages, security logging, XSS/Injection prevention
|
|
1064
|
-
- **✅ Single Unified Logic** - All sign-in methods use the same core logic
|
|
1065
|
-
- **✅ Type-Safe** - Full TypeScript support with proper typing
|
|
1066
|
-
- **✅ Production Ready** - Best practices built-in by default
|
|
1067
|
-
|
|
1068
571
|
---
|
|
1069
572
|
|
|
1070
573
|
## 🌟 About Mulverse
|
|
@@ -1076,3 +579,4 @@ Visit [mulverse.com](https://mulverse.com) to learn more about our other project
|
|
|
1076
579
|
---
|
|
1077
580
|
|
|
1078
581
|
**Made with ❤️ by the Mulverse Team**
|
|
582
|
+
|