mulguard 1.0.1 โ 1.1.0
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/LICENSE +70 -0
- package/README.md +797 -123
- package/dist/client/index.js +1 -1
- package/dist/client/index.mjs +24 -25
- package/dist/core/auth/signin-unified.d.ts +1 -1
- package/dist/index/index.js +1 -1
- package/dist/index/index.mjs +824 -728
- package/dist/mulguard.d.ts +32 -5
- package/package.json +1 -1
- package/dist/signin-unified-BS2gxaG1.mjs +0 -30
- package/dist/signin-unified-Cw41EFkc.js +0 -1
package/README.md
CHANGED
|
@@ -1,93 +1,201 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Mulguard
|
|
2
2
|
|
|
3
|
-
>
|
|
3
|
+
> A modern, backend-first authentication library for Next.js applications
|
|
4
4
|
|
|
5
|
-
[](LICENSE)
|
|
6
6
|
[](https://www.typescriptlang.org/)
|
|
7
|
+
[](https://nextjs.org/)
|
|
7
8
|
|
|
8
|
-
|
|
9
|
+
**Mulguard** is a powerful, flexible authentication library designed specifically for Next.js applications. Built with a backend-first architecture, it gives you complete control over your authentication logic while providing a simple, intuitive API similar to popular libraries like `better-auth` and `auth.js`.
|
|
10
|
+
|
|
11
|
+
**Part of the [Mulverse](https://mulverse.com) ecosystem** - Empowering modern web applications with robust, secure authentication.
|
|
12
|
+
|
|
13
|
+
---
|
|
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
|
+
---
|
|
9
37
|
|
|
10
38
|
## โจ Features
|
|
11
39
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
40
|
+
### ๐ Multiple Authentication Methods
|
|
41
|
+
- **Email/Password** - Traditional credential-based authentication
|
|
42
|
+
- **OAuth 2.1** - Support for Google, GitHub, and other OAuth providers
|
|
43
|
+
- **PassKey/WebAuthn** - Passwordless authentication with biometric support
|
|
44
|
+
- **Two-Factor Authentication (2FA/TOTP)** - Time-based one-time passwords with backup codes
|
|
45
|
+
- **OTP** - One-time password authentication
|
|
46
|
+
|
|
47
|
+
### ๐ฏ Account Picker
|
|
48
|
+
- Remember last logged-in users
|
|
49
|
+
- Quick re-login experience
|
|
50
|
+
- Encrypted local storage for security
|
|
51
|
+
- Configurable account limits
|
|
52
|
+
|
|
53
|
+
### ๐ Security First
|
|
54
|
+
- **CSRF Protection** - Token-based cross-site request forgery protection
|
|
55
|
+
- **XSS Prevention** - Input sanitization and HTML escaping
|
|
56
|
+
- **Security Headers** - Automatic security headers configuration
|
|
57
|
+
- **Input Validation** - Comprehensive validation and sanitization
|
|
58
|
+
- **Rate Limiting** - Built-in rate limiting utilities
|
|
59
|
+
- **Secure Cookies** - HttpOnly, Secure, SameSite cookie configuration
|
|
60
|
+
|
|
61
|
+
### โก Next.js Optimized
|
|
62
|
+
- **Server Components** - Full support for React Server Components
|
|
63
|
+
- **Client Hooks** - `useAuth`, `useSession` for client-side state management
|
|
64
|
+
- **Middleware Integration** - Seamless Next.js middleware support
|
|
65
|
+
- **API Route Handlers** - Pre-built route handlers for authentication endpoints
|
|
66
|
+
- **Server Actions** - Native support for Next.js Server Actions
|
|
67
|
+
|
|
68
|
+
### ๐ Backend-First Architecture
|
|
69
|
+
- **No Database Adapter Required** - Works with your existing backend API
|
|
70
|
+
- **Custom Actions** - Implement your own authentication logic
|
|
71
|
+
- **Flexible Integration** - Works with any backend (REST, GraphQL, etc.)
|
|
72
|
+
- **Mulink Integration** - Optional integration with Mulink API client
|
|
73
|
+
- **Full TypeScript Support** - Complete type safety throughout
|
|
74
|
+
|
|
75
|
+
---
|
|
41
76
|
|
|
42
77
|
## ๐ฆ Installation
|
|
43
78
|
|
|
44
79
|
```bash
|
|
45
|
-
npm install
|
|
80
|
+
npm install mulguard
|
|
46
81
|
```
|
|
47
82
|
|
|
83
|
+
### Peer Dependencies
|
|
84
|
+
|
|
85
|
+
Mulguard requires the following peer dependencies:
|
|
86
|
+
|
|
87
|
+
```bash
|
|
88
|
+
npm install next@>=14.0.0 react@>=18.0.0 react-dom@>=18.0.0
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
---
|
|
92
|
+
|
|
48
93
|
## ๐ Quick Start
|
|
49
94
|
|
|
50
95
|
### 1. Create Auth Configuration
|
|
51
96
|
|
|
52
|
-
|
|
53
|
-
// lib/auth.ts
|
|
54
|
-
import { mukey } from 'mukey'
|
|
55
|
-
// import { apiClient } from '@mulink/client' // Optional: Use Mulink
|
|
97
|
+
Create a new file `lib/auth.ts`:
|
|
56
98
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
99
|
+
```typescript
|
|
100
|
+
import { mulguard } from 'mulguard'
|
|
101
|
+
import type { EmailCredentials, AuthResult, Session } from 'mulguard'
|
|
102
|
+
import { db } from '@/lib/db'
|
|
103
|
+
import { comparePassword } from '@/lib/password'
|
|
60
104
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
105
|
+
export const auth = mulguard({
|
|
106
|
+
// Session configuration
|
|
64
107
|
session: {
|
|
65
|
-
cookieName: '
|
|
108
|
+
cookieName: '__mulguard_session',
|
|
66
109
|
expiresIn: 60 * 60 * 24 * 7, // 7 days
|
|
110
|
+
httpOnly: true,
|
|
111
|
+
secure: process.env.NODE_ENV === 'production', // HTTPS only in production
|
|
112
|
+
sameSite: 'lax',
|
|
113
|
+
},
|
|
114
|
+
|
|
115
|
+
// Required: Implement your authentication actions
|
|
116
|
+
actions: {
|
|
117
|
+
signIn: {
|
|
118
|
+
// โ
Unified interface: auth.signIn('credentials', {...}) or auth.signIn.email({...})
|
|
119
|
+
email: async (credentials: EmailCredentials): Promise<AuthResult> => {
|
|
120
|
+
// Your custom sign-in logic
|
|
121
|
+
const user = await db.user.findUnique({
|
|
122
|
+
where: { email: credentials.email }
|
|
123
|
+
})
|
|
124
|
+
|
|
125
|
+
if (!user || !await comparePassword(credentials.password, user.password)) {
|
|
126
|
+
return {
|
|
127
|
+
success: false,
|
|
128
|
+
error: 'Invalid credentials',
|
|
129
|
+
errorCode: 'INVALID_CREDENTIALS'
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
const session: Session = {
|
|
134
|
+
user: {
|
|
135
|
+
id: user.id,
|
|
136
|
+
email: user.email,
|
|
137
|
+
name: user.name,
|
|
138
|
+
emailVerified: user.emailVerified,
|
|
139
|
+
},
|
|
140
|
+
expiresAt: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000),
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
return { success: true, user: session.user, session }
|
|
144
|
+
},
|
|
145
|
+
},
|
|
146
|
+
|
|
147
|
+
getSession: async (): Promise<Session | null> => {
|
|
148
|
+
// Your custom session retrieval logic
|
|
149
|
+
// Can read from cookie or database
|
|
150
|
+
return await db.getSession()
|
|
151
|
+
},
|
|
152
|
+
|
|
153
|
+
signOut: async () => {
|
|
154
|
+
// Your custom sign-out logic
|
|
155
|
+
await db.invalidateSession()
|
|
156
|
+
return { success: true }
|
|
157
|
+
},
|
|
67
158
|
},
|
|
68
159
|
|
|
160
|
+
// Optional: OAuth providers (auto-generates OAuth actions)
|
|
69
161
|
providers: {
|
|
70
162
|
oauth: {
|
|
71
163
|
google: {
|
|
72
164
|
clientId: process.env.GOOGLE_CLIENT_ID!,
|
|
165
|
+
clientSecret: process.env.GOOGLE_CLIENT_SECRET, // Server-side only
|
|
73
166
|
redirectUri: `${process.env.NEXT_PUBLIC_URL}/api/auth/callback/google`,
|
|
74
167
|
scopes: ['openid', 'profile', 'email'],
|
|
75
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'],
|
|
174
|
+
},
|
|
76
175
|
},
|
|
77
176
|
},
|
|
78
177
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
178
|
+
// Optional: Callbacks for lifecycle events
|
|
179
|
+
callbacks: {
|
|
180
|
+
onSignIn: async (user, session) => {
|
|
181
|
+
// Track sign-in analytics, update last login, etc.
|
|
182
|
+
console.log('User signed in:', user.email)
|
|
183
|
+
},
|
|
184
|
+
onError: async (error, context) => {
|
|
185
|
+
// Log errors for monitoring
|
|
186
|
+
console.error(`Auth error in ${context}:`, error)
|
|
187
|
+
},
|
|
82
188
|
},
|
|
83
189
|
})
|
|
84
190
|
```
|
|
85
191
|
|
|
192
|
+
> ๐ **For a complete guide with best practices**, see [GUIDE.md](./GUIDE.md)
|
|
193
|
+
|
|
86
194
|
### 2. Server-Side Usage
|
|
87
195
|
|
|
88
196
|
```typescript
|
|
89
197
|
// app/dashboard/page.tsx
|
|
90
|
-
import { getServerSession } from '
|
|
198
|
+
import { getServerSession } from 'mulguard/server'
|
|
91
199
|
import { auth } from '@/lib/auth'
|
|
92
200
|
import { redirect } from 'next/navigation'
|
|
93
201
|
|
|
@@ -98,7 +206,12 @@ export default async function DashboardPage() {
|
|
|
98
206
|
redirect('/login')
|
|
99
207
|
}
|
|
100
208
|
|
|
101
|
-
return
|
|
209
|
+
return (
|
|
210
|
+
<div>
|
|
211
|
+
<h1>Welcome, {session.user.name}!</h1>
|
|
212
|
+
<p>Email: {session.user.email}</p>
|
|
213
|
+
</div>
|
|
214
|
+
)
|
|
102
215
|
}
|
|
103
216
|
```
|
|
104
217
|
|
|
@@ -108,7 +221,7 @@ export default async function DashboardPage() {
|
|
|
108
221
|
// app/login/page.tsx
|
|
109
222
|
'use client'
|
|
110
223
|
|
|
111
|
-
import { useAuth } from '
|
|
224
|
+
import { useAuth } from 'mulguard/client'
|
|
112
225
|
import { auth } from '@/lib/auth'
|
|
113
226
|
import { useState } from 'react'
|
|
114
227
|
|
|
@@ -119,9 +232,19 @@ export default function LoginPage() {
|
|
|
119
232
|
|
|
120
233
|
const handleSubmit = async (e: React.FormEvent) => {
|
|
121
234
|
e.preventDefault()
|
|
122
|
-
|
|
235
|
+
|
|
236
|
+
// โ
Unified interface (recommended)
|
|
237
|
+
const result = await signIn('credentials', { email, password })
|
|
238
|
+
|
|
239
|
+
// Or use direct method:
|
|
240
|
+
// const result = await signIn.email({ email, password })
|
|
241
|
+
|
|
123
242
|
if (result.success) {
|
|
124
243
|
// Redirect to dashboard
|
|
244
|
+
window.location.href = '/dashboard'
|
|
245
|
+
} else {
|
|
246
|
+
// Handle error
|
|
247
|
+
alert(result.error)
|
|
125
248
|
}
|
|
126
249
|
}
|
|
127
250
|
|
|
@@ -135,12 +258,14 @@ export default function LoginPage() {
|
|
|
135
258
|
value={email}
|
|
136
259
|
onChange={(e) => setEmail(e.target.value)}
|
|
137
260
|
placeholder="Email"
|
|
261
|
+
required
|
|
138
262
|
/>
|
|
139
263
|
<input
|
|
140
264
|
type="password"
|
|
141
265
|
value={password}
|
|
142
266
|
onChange={(e) => setPassword(e.target.value)}
|
|
143
267
|
placeholder="Password"
|
|
268
|
+
required
|
|
144
269
|
/>
|
|
145
270
|
<button type="submit">Sign In</button>
|
|
146
271
|
</form>
|
|
@@ -152,7 +277,7 @@ export default function LoginPage() {
|
|
|
152
277
|
|
|
153
278
|
```typescript
|
|
154
279
|
// middleware.ts
|
|
155
|
-
import { createAuthMiddleware } from '
|
|
280
|
+
import { createAuthMiddleware } from 'mulguard/middleware'
|
|
156
281
|
import { auth } from '@/lib/auth'
|
|
157
282
|
|
|
158
283
|
export default createAuthMiddleware(auth, {
|
|
@@ -160,44 +285,191 @@ export default createAuthMiddleware(auth, {
|
|
|
160
285
|
redirectTo: '/login',
|
|
161
286
|
redirectIfAuthenticated: '/dashboard',
|
|
162
287
|
})
|
|
288
|
+
|
|
289
|
+
export const config = {
|
|
290
|
+
matcher: ['/dashboard/:path*', '/profile/:path*'],
|
|
291
|
+
}
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
---
|
|
295
|
+
|
|
296
|
+
## ๐ Core Concepts
|
|
297
|
+
|
|
298
|
+
### Backend-First Architecture
|
|
299
|
+
|
|
300
|
+
Mulguard follows a **backend-first** approach, meaning you implement your own authentication logic through **actions**. This gives you:
|
|
301
|
+
|
|
302
|
+
- **Full Control** - Complete control over your authentication flow
|
|
303
|
+
- **Flexibility** - Works with any backend architecture
|
|
304
|
+
- **No Lock-in** - No database adapter required
|
|
305
|
+
- **Custom Logic** - Implement business-specific authentication rules
|
|
306
|
+
|
|
307
|
+
### Actions
|
|
308
|
+
|
|
309
|
+
Actions are functions you provide to handle authentication operations:
|
|
310
|
+
|
|
311
|
+
```typescript
|
|
312
|
+
actions: {
|
|
313
|
+
signIn: {
|
|
314
|
+
email: async (credentials) => { /* ... */ },
|
|
315
|
+
oauth: async (provider) => { /* ... */ },
|
|
316
|
+
passkey: async (options) => { /* ... */ },
|
|
317
|
+
},
|
|
318
|
+
signUp: async (data) => { /* ... */ },
|
|
319
|
+
signOut: async () => { /* ... */ },
|
|
320
|
+
getSession: async () => { /* ... */ },
|
|
321
|
+
// ... more actions
|
|
322
|
+
}
|
|
323
|
+
```
|
|
324
|
+
|
|
325
|
+
### Sessions
|
|
326
|
+
|
|
327
|
+
Sessions are stored in HTTP-only cookies and contain:
|
|
328
|
+
|
|
329
|
+
```typescript
|
|
330
|
+
interface Session {
|
|
331
|
+
user: {
|
|
332
|
+
id: string
|
|
333
|
+
email: string
|
|
334
|
+
name?: string
|
|
335
|
+
avatar?: string
|
|
336
|
+
emailVerified?: boolean
|
|
337
|
+
}
|
|
338
|
+
expiresAt: Date
|
|
339
|
+
accessToken?: string
|
|
340
|
+
refreshToken?: string
|
|
341
|
+
tokenType?: string
|
|
342
|
+
expiresIn?: number
|
|
343
|
+
}
|
|
344
|
+
```
|
|
345
|
+
|
|
346
|
+
---
|
|
347
|
+
|
|
348
|
+
## ๐ Authentication Methods
|
|
349
|
+
|
|
350
|
+
### Unified Sign-In Interface
|
|
351
|
+
|
|
352
|
+
Mulguard provides a **unified interface** for all authentication methods, similar to auth.js:
|
|
353
|
+
|
|
354
|
+
```typescript
|
|
355
|
+
// โ
Unified interface (recommended)
|
|
356
|
+
await auth.signIn('credentials', { email: 'user@example.com', password: 'password123' })
|
|
357
|
+
await auth.signIn('google')
|
|
358
|
+
await auth.signIn('otp', { email: 'user@example.com', code: '123456' })
|
|
359
|
+
await auth.signIn('passkey', { userId: 'user-id' })
|
|
360
|
+
|
|
361
|
+
// โ
Direct methods (for backward compatibility)
|
|
362
|
+
await auth.signIn.email({ email: 'user@example.com', password: 'password123' })
|
|
363
|
+
await auth.signIn.oauth('google')
|
|
364
|
+
await auth.signIn.otp('user@example.com', '123456')
|
|
365
|
+
```
|
|
366
|
+
|
|
367
|
+
### Email/Password Authentication
|
|
368
|
+
|
|
369
|
+
```typescript
|
|
370
|
+
// Using unified interface
|
|
371
|
+
const result = await auth.signIn('credentials', {
|
|
372
|
+
email: 'user@example.com',
|
|
373
|
+
password: 'password123',
|
|
374
|
+
})
|
|
375
|
+
|
|
376
|
+
// Or using direct method
|
|
377
|
+
const result = await auth.signIn.email({
|
|
378
|
+
email: 'user@example.com',
|
|
379
|
+
password: 'password123',
|
|
380
|
+
})
|
|
381
|
+
|
|
382
|
+
if (result.success) {
|
|
383
|
+
console.log('Signed in:', result.user)
|
|
384
|
+
} else {
|
|
385
|
+
console.error('Error:', result.error, result.errorCode)
|
|
386
|
+
}
|
|
163
387
|
```
|
|
164
388
|
|
|
165
|
-
|
|
389
|
+
### OAuth Authentication
|
|
166
390
|
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
391
|
+
```typescript
|
|
392
|
+
// Initiate OAuth flow (unified interface)
|
|
393
|
+
const { url } = await auth.signIn('google')
|
|
394
|
+
window.location.href = url
|
|
395
|
+
|
|
396
|
+
// Or using direct method
|
|
397
|
+
const { url } = await auth.signIn.oauth('google')
|
|
398
|
+
window.location.href = url
|
|
399
|
+
|
|
400
|
+
// Handle callback (in your API route)
|
|
401
|
+
const result = await auth.oauthCallback(provider, code, state)
|
|
402
|
+
|
|
403
|
+
// โ
OAuth providers are auto-configured when you add them to providers.oauth
|
|
404
|
+
// No need to implement oauth action manually!
|
|
405
|
+
```
|
|
406
|
+
|
|
407
|
+
### PassKey/WebAuthn Authentication
|
|
408
|
+
|
|
409
|
+
```typescript
|
|
410
|
+
// Register PassKey
|
|
411
|
+
const result = await auth.passkey.register({
|
|
412
|
+
name: 'My iPhone',
|
|
413
|
+
userId: 'user-id',
|
|
414
|
+
})
|
|
415
|
+
|
|
416
|
+
// Authenticate with PassKey
|
|
417
|
+
const result = await auth.signIn.passkey({ userId: 'user-id' })
|
|
418
|
+
```
|
|
419
|
+
|
|
420
|
+
### Two-Factor Authentication (2FA)
|
|
421
|
+
|
|
422
|
+
```typescript
|
|
423
|
+
// Enable 2FA
|
|
424
|
+
const enableResult = await auth.twoFactor.enable()
|
|
425
|
+
// Display QR code to user
|
|
174
426
|
|
|
175
|
-
|
|
427
|
+
// Verify 2FA code
|
|
428
|
+
const verifyResult = await auth.twoFactor.verify('123456')
|
|
429
|
+
|
|
430
|
+
// Check if 2FA is enabled
|
|
431
|
+
const isEnabled = await auth.twoFactor.isEnabled()
|
|
432
|
+
```
|
|
433
|
+
|
|
434
|
+
---
|
|
435
|
+
|
|
436
|
+
## โ๏ธ Configuration
|
|
176
437
|
|
|
177
438
|
### Full Configuration Options
|
|
178
439
|
|
|
179
440
|
```typescript
|
|
180
|
-
import {
|
|
441
|
+
import { mulguard } from 'mulguard'
|
|
181
442
|
|
|
182
|
-
export const auth =
|
|
183
|
-
// API Client (from Mulink) or baseURL
|
|
184
|
-
apiClient: apiClient, // Optional
|
|
185
|
-
baseURL: 'http://localhost:3001', // Required if apiClient not provided
|
|
186
|
-
|
|
443
|
+
export const auth = mulguard({
|
|
187
444
|
// Session configuration
|
|
188
445
|
session: {
|
|
189
|
-
cookieName: '
|
|
446
|
+
cookieName: '__mulguard_session',
|
|
190
447
|
expiresIn: 60 * 60 * 24 * 7, // 7 days in seconds
|
|
191
448
|
httpOnly: true,
|
|
192
449
|
secure: process.env.NODE_ENV === 'production',
|
|
193
450
|
sameSite: 'lax',
|
|
451
|
+
path: '/',
|
|
452
|
+
domain: undefined,
|
|
453
|
+
cacheTtl: 5000, // Session cache TTL in milliseconds
|
|
454
|
+
},
|
|
455
|
+
|
|
456
|
+
// Required: Authentication actions
|
|
457
|
+
actions: {
|
|
458
|
+
signIn: {
|
|
459
|
+
email: async (credentials) => { /* ... */ },
|
|
460
|
+
// Optional: oauth, passkey, otp
|
|
461
|
+
},
|
|
462
|
+
getSession: async () => { /* ... */ },
|
|
463
|
+
signOut: async () => { /* ... */ },
|
|
464
|
+
// Optional: signUp, resetPassword, verifyEmail, refreshSession, etc.
|
|
194
465
|
},
|
|
195
466
|
|
|
196
|
-
// OAuth providers
|
|
467
|
+
// Optional: OAuth providers
|
|
197
468
|
providers: {
|
|
198
469
|
oauth: {
|
|
199
470
|
google: {
|
|
200
471
|
clientId: '...',
|
|
472
|
+
clientSecret: '...', // Server-side only
|
|
201
473
|
redirectUri: '...',
|
|
202
474
|
scopes: ['openid', 'profile', 'email'],
|
|
203
475
|
name: 'Google',
|
|
@@ -210,20 +482,20 @@ export const auth = mukey({
|
|
|
210
482
|
},
|
|
211
483
|
},
|
|
212
484
|
|
|
213
|
-
//
|
|
485
|
+
// Optional: Two-Factor Authentication
|
|
214
486
|
twoFactor: {
|
|
215
487
|
enabled: true,
|
|
216
488
|
},
|
|
217
489
|
|
|
218
|
-
// Account Picker
|
|
490
|
+
// Optional: Account Picker
|
|
219
491
|
accountPicker: {
|
|
220
492
|
enabled: true,
|
|
221
493
|
maxAccounts: 3,
|
|
222
494
|
encryptStorage: true,
|
|
223
|
-
storageKey: '
|
|
495
|
+
storageKey: '__mulguard_accounts',
|
|
224
496
|
},
|
|
225
497
|
|
|
226
|
-
// Security
|
|
498
|
+
// Optional: Security settings
|
|
227
499
|
security: {
|
|
228
500
|
csrfProtection: true,
|
|
229
501
|
rateLimiting: {
|
|
@@ -231,15 +503,132 @@ export const auth = mukey({
|
|
|
231
503
|
windowMs: 15 * 60 * 1000, // 15 minutes
|
|
232
504
|
},
|
|
233
505
|
},
|
|
506
|
+
|
|
507
|
+
// Optional: Callbacks
|
|
508
|
+
callbacks: {
|
|
509
|
+
onSignIn: async (user, session) => {
|
|
510
|
+
// Called after successful sign-in
|
|
511
|
+
},
|
|
512
|
+
onSignOut: async (user) => {
|
|
513
|
+
// Called after sign-out
|
|
514
|
+
},
|
|
515
|
+
onSessionExpired: async (session) => {
|
|
516
|
+
// Called when session expires
|
|
517
|
+
},
|
|
518
|
+
onError: async (error, context) => {
|
|
519
|
+
// Called on authentication errors
|
|
520
|
+
},
|
|
521
|
+
},
|
|
522
|
+
|
|
523
|
+
// Optional: Token refresh configuration
|
|
524
|
+
tokenRefresh: {
|
|
525
|
+
enabled: true,
|
|
526
|
+
refreshThreshold: 300, // 5 minutes before expiration
|
|
527
|
+
maxRetries: 0,
|
|
528
|
+
retryDelay: 1000,
|
|
529
|
+
rateLimit: 1, // 1 attempt per minute
|
|
530
|
+
autoSignOutOnFailure: true,
|
|
531
|
+
redirectToLogin: '/login',
|
|
532
|
+
},
|
|
234
533
|
})
|
|
235
534
|
```
|
|
236
535
|
|
|
536
|
+
---
|
|
537
|
+
|
|
538
|
+
## ๐ฅ๏ธ Server-Side Usage
|
|
539
|
+
|
|
540
|
+
### Get Server Session
|
|
541
|
+
|
|
542
|
+
```typescript
|
|
543
|
+
import { getServerSession } from 'mulguard/server'
|
|
544
|
+
import { auth } from '@/lib/auth'
|
|
545
|
+
|
|
546
|
+
// In Server Components
|
|
547
|
+
const session = await getServerSession(auth)
|
|
548
|
+
|
|
549
|
+
// In API Routes
|
|
550
|
+
export async function GET(request: Request) {
|
|
551
|
+
const session = await getServerSession(auth)
|
|
552
|
+
if (!session) {
|
|
553
|
+
return new Response('Unauthorized', { status: 401 })
|
|
554
|
+
}
|
|
555
|
+
return Response.json({ user: session.user })
|
|
556
|
+
}
|
|
557
|
+
```
|
|
558
|
+
|
|
559
|
+
### Server Actions
|
|
560
|
+
|
|
561
|
+
```typescript
|
|
562
|
+
'use server'
|
|
563
|
+
|
|
564
|
+
import { auth } from '@/lib/auth'
|
|
565
|
+
|
|
566
|
+
export async function signInAction(email: string, password: string) {
|
|
567
|
+
const result = await auth.signIn.email({ email, password })
|
|
568
|
+
return result
|
|
569
|
+
}
|
|
570
|
+
```
|
|
571
|
+
|
|
572
|
+
### API Route Handlers
|
|
573
|
+
|
|
574
|
+
```typescript
|
|
575
|
+
// app/api/auth/[...mulguard]/route.ts
|
|
576
|
+
import { createAuthHandler } from 'mulguard/handlers/route'
|
|
577
|
+
import { auth } from '@/lib/auth'
|
|
578
|
+
|
|
579
|
+
export const { GET, POST } = createAuthHandler(auth)
|
|
580
|
+
```
|
|
581
|
+
|
|
582
|
+
---
|
|
583
|
+
|
|
584
|
+
## ๐ป Client-Side Usage
|
|
585
|
+
|
|
586
|
+
### React Hooks
|
|
587
|
+
|
|
588
|
+
```typescript
|
|
589
|
+
'use client'
|
|
590
|
+
|
|
591
|
+
import { useAuth, useSession } from 'mulguard/client'
|
|
592
|
+
import { auth } from '@/lib/auth'
|
|
593
|
+
|
|
594
|
+
export function AuthComponent() {
|
|
595
|
+
const { signIn, signOut, isLoading } = useAuth(auth)
|
|
596
|
+
const { session, isLoading: sessionLoading } = useSession(auth)
|
|
597
|
+
|
|
598
|
+
// Use hooks...
|
|
599
|
+
}
|
|
600
|
+
```
|
|
601
|
+
|
|
602
|
+
### Auth Provider
|
|
603
|
+
|
|
604
|
+
```typescript
|
|
605
|
+
// app/layout.tsx
|
|
606
|
+
'use client'
|
|
607
|
+
|
|
608
|
+
import { AuthProvider } from 'mulguard/client'
|
|
609
|
+
import { auth } from '@/lib/auth'
|
|
610
|
+
|
|
611
|
+
export default function RootLayout({ children }) {
|
|
612
|
+
return (
|
|
613
|
+
<html>
|
|
614
|
+
<body>
|
|
615
|
+
<AuthProvider auth={auth}>
|
|
616
|
+
{children}
|
|
617
|
+
</AuthProvider>
|
|
618
|
+
</body>
|
|
619
|
+
</html>
|
|
620
|
+
)
|
|
621
|
+
}
|
|
622
|
+
```
|
|
623
|
+
|
|
624
|
+
---
|
|
625
|
+
|
|
237
626
|
## ๐จ Components
|
|
238
627
|
|
|
239
628
|
### Account Picker
|
|
240
629
|
|
|
241
630
|
```typescript
|
|
242
|
-
import { AccountPicker } from '
|
|
631
|
+
import { AccountPicker } from 'mulguard'
|
|
243
632
|
|
|
244
633
|
<AccountPicker
|
|
245
634
|
auth={auth}
|
|
@@ -256,7 +645,7 @@ import { AccountPicker } from 'mukey'
|
|
|
256
645
|
### OAuth Button
|
|
257
646
|
|
|
258
647
|
```typescript
|
|
259
|
-
import { OAuthButton } from '
|
|
648
|
+
import { OAuthButton } from 'mulguard'
|
|
260
649
|
|
|
261
650
|
<OAuthButton
|
|
262
651
|
auth={auth}
|
|
@@ -264,13 +653,18 @@ import { OAuthButton } from 'mukey'
|
|
|
264
653
|
onSuccess={() => {
|
|
265
654
|
// Handle success
|
|
266
655
|
}}
|
|
267
|
-
|
|
656
|
+
onError={(error) => {
|
|
657
|
+
// Handle error
|
|
658
|
+
}}
|
|
659
|
+
>
|
|
660
|
+
Sign in with Google
|
|
661
|
+
</OAuthButton>
|
|
268
662
|
```
|
|
269
663
|
|
|
270
|
-
###
|
|
664
|
+
### Two-Factor Setup
|
|
271
665
|
|
|
272
666
|
```typescript
|
|
273
|
-
import { TwoFactorSetup } from '
|
|
667
|
+
import { TwoFactorSetup } from 'mulguard'
|
|
274
668
|
|
|
275
669
|
<TwoFactorSetup
|
|
276
670
|
auth={auth}
|
|
@@ -280,53 +674,286 @@ import { TwoFactorSetup } from 'mukey'
|
|
|
280
674
|
/>
|
|
281
675
|
```
|
|
282
676
|
|
|
677
|
+
### PassKey Components
|
|
678
|
+
|
|
679
|
+
```typescript
|
|
680
|
+
import { PassKeyButton, PassKeyRegister } from 'mulguard'
|
|
681
|
+
|
|
682
|
+
// Register PassKey
|
|
683
|
+
<PassKeyRegister auth={auth} />
|
|
684
|
+
|
|
685
|
+
// Authenticate with PassKey
|
|
686
|
+
<PassKeyButton auth={auth} />
|
|
687
|
+
```
|
|
688
|
+
|
|
689
|
+
---
|
|
690
|
+
|
|
691
|
+
## ๐ Security
|
|
692
|
+
|
|
693
|
+
Mulguard implements **comprehensive security features** with automatic input validation and sanitization:
|
|
694
|
+
|
|
695
|
+
### โ
Built-in Security Features
|
|
696
|
+
|
|
697
|
+
#### Input Validation & Sanitization
|
|
698
|
+
- **Automatic email validation** - Validates and sanitizes email addresses
|
|
699
|
+
- **Password length checks** - Prevents DoS attacks with extremely long passwords
|
|
700
|
+
- **Provider validation** - Sanitizes OAuth provider strings to prevent injection
|
|
701
|
+
- **OTP code validation** - Validates OTP code format before processing
|
|
702
|
+
- **XSS Prevention** - Automatic HTML escaping and input sanitization
|
|
703
|
+
|
|
704
|
+
#### CSRF Protection
|
|
705
|
+
- Token-based CSRF protection for OAuth flows
|
|
706
|
+
- State parameter validation with constant-time comparison
|
|
707
|
+
- Secure state storage (pluggable for production - Redis, Database, etc.)
|
|
708
|
+
|
|
709
|
+
#### Error Handling
|
|
710
|
+
- **Generic error messages** - Prevents information disclosure
|
|
711
|
+
- **Error codes** - Programmatic error handling without exposing details
|
|
712
|
+
- **Security logging** - Logs authentication events (with sensitive data masked)
|
|
713
|
+
|
|
714
|
+
#### Secure Cookies
|
|
715
|
+
- HttpOnly cookies (prevents JavaScript access)
|
|
716
|
+
- Secure flag in production (HTTPS only)
|
|
717
|
+
- SameSite attribute (CSRF protection)
|
|
718
|
+
- Configurable expiration
|
|
719
|
+
|
|
720
|
+
#### Rate Limiting
|
|
721
|
+
- Configurable rate limiting
|
|
722
|
+
- Client-side tracking
|
|
723
|
+
- Automatic throttling
|
|
724
|
+
- Circuit breaker pattern for token refresh
|
|
725
|
+
|
|
726
|
+
### Security Best Practices
|
|
727
|
+
|
|
728
|
+
```typescript
|
|
729
|
+
// โ
Good: Generic error messages
|
|
730
|
+
return {
|
|
731
|
+
success: false,
|
|
732
|
+
error: 'Invalid credentials',
|
|
733
|
+
errorCode: 'INVALID_CREDENTIALS'
|
|
734
|
+
}
|
|
735
|
+
|
|
736
|
+
// โ Bad: Information disclosure
|
|
737
|
+
return {
|
|
738
|
+
success: false,
|
|
739
|
+
error: 'User not found with email: user@example.com'
|
|
740
|
+
}
|
|
741
|
+
```
|
|
742
|
+
|
|
743
|
+
### Security Logging
|
|
744
|
+
|
|
745
|
+
All authentication events are automatically logged with sensitive data masked:
|
|
746
|
+
|
|
747
|
+
```typescript
|
|
748
|
+
// Logs show: "Sign in successful" with email: "use***"
|
|
749
|
+
// Never logs full email addresses or passwords
|
|
750
|
+
```
|
|
751
|
+
|
|
752
|
+
For detailed security documentation, see the [Security Guide](./docs/SECURITY.md) and [Complete Guide](./GUIDE.md).
|
|
753
|
+
|
|
754
|
+
---
|
|
755
|
+
|
|
283
756
|
## ๐ Backend Integration
|
|
284
757
|
|
|
285
|
-
|
|
758
|
+
Mulguard is designed to work with your existing backend. You implement the authentication logic through **actions**, which can:
|
|
286
759
|
|
|
287
|
-
|
|
760
|
+
- Connect to your database
|
|
761
|
+
- Call your API endpoints
|
|
762
|
+
- Use your existing authentication services
|
|
763
|
+
- Integrate with third-party services
|
|
288
764
|
|
|
289
|
-
|
|
290
|
-
- `POST /api/auth/sign-up` - Register new user
|
|
291
|
-
- `POST /api/auth/sign-out` - Sign out
|
|
292
|
-
- `GET /api/auth/session` - Get current session
|
|
293
|
-
- `POST /api/auth/reset-password` - Request password reset
|
|
294
|
-
- `POST /api/auth/verify-email` - Verify email address
|
|
765
|
+
### Example: Database Integration
|
|
295
766
|
|
|
296
|
-
|
|
767
|
+
```typescript
|
|
768
|
+
import { mulguard } from 'mulguard'
|
|
769
|
+
import { db } from '@/lib/db'
|
|
770
|
+
import { comparePassword, hashPassword } from '@/lib/password'
|
|
771
|
+
|
|
772
|
+
export const auth = mulguard({
|
|
773
|
+
actions: {
|
|
774
|
+
signIn: {
|
|
775
|
+
email: async ({ email, password }) => {
|
|
776
|
+
const user = await db.user.findUnique({ where: { email } })
|
|
777
|
+
|
|
778
|
+
if (!user || !await comparePassword(password, user.password)) {
|
|
779
|
+
return { success: false, error: 'Invalid credentials' }
|
|
780
|
+
}
|
|
781
|
+
|
|
782
|
+
return {
|
|
783
|
+
success: true,
|
|
784
|
+
user: {
|
|
785
|
+
id: user.id,
|
|
786
|
+
email: user.email,
|
|
787
|
+
name: user.name,
|
|
788
|
+
},
|
|
789
|
+
session: {
|
|
790
|
+
user: { /* ... */ },
|
|
791
|
+
expiresAt: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000),
|
|
792
|
+
},
|
|
793
|
+
}
|
|
794
|
+
},
|
|
795
|
+
},
|
|
796
|
+
// ... more actions
|
|
797
|
+
},
|
|
798
|
+
})
|
|
799
|
+
```
|
|
297
800
|
|
|
298
|
-
|
|
299
|
-
- `POST /api/auth/oauth/:provider/callback` - Handle OAuth callback
|
|
801
|
+
### Example: API Integration
|
|
300
802
|
|
|
301
|
-
|
|
803
|
+
```typescript
|
|
804
|
+
import { mulguard } from 'mulguard'
|
|
805
|
+
|
|
806
|
+
export const auth = mulguard({
|
|
807
|
+
actions: {
|
|
808
|
+
signIn: {
|
|
809
|
+
email: async ({ email, password }) => {
|
|
810
|
+
const response = await fetch('https://api.example.com/auth/signin', {
|
|
811
|
+
method: 'POST',
|
|
812
|
+
headers: { 'Content-Type': 'application/json' },
|
|
813
|
+
body: JSON.stringify({ email, password }),
|
|
814
|
+
})
|
|
815
|
+
|
|
816
|
+
if (!response.ok) {
|
|
817
|
+
return { success: false, error: 'Authentication failed' }
|
|
818
|
+
}
|
|
819
|
+
|
|
820
|
+
const data = await response.json()
|
|
821
|
+
return {
|
|
822
|
+
success: true,
|
|
823
|
+
user: data.user,
|
|
824
|
+
session: data.session,
|
|
825
|
+
}
|
|
826
|
+
},
|
|
827
|
+
},
|
|
828
|
+
},
|
|
829
|
+
})
|
|
830
|
+
```
|
|
302
831
|
|
|
303
|
-
|
|
304
|
-
- `POST /api/auth/2fa/verify` - Verify 2FA code
|
|
305
|
-
- `POST /api/auth/2fa/disable` - Disable 2FA
|
|
306
|
-
- `POST /api/auth/2fa/regenerate` - Generate backup codes
|
|
307
|
-
- `GET /api/auth/2fa/status` - Check 2FA status
|
|
832
|
+
---
|
|
308
833
|
|
|
309
|
-
|
|
834
|
+
## ๐ Advanced Features
|
|
310
835
|
|
|
311
|
-
|
|
312
|
-
- `POST /api/auth/passkey/authenticate` - Authenticate with PassKey
|
|
313
|
-
- `GET /api/auth/passkey/list` - List registered PassKeys
|
|
314
|
-
- `DELETE /api/auth/passkey/:id` - Remove PassKey
|
|
836
|
+
### Token Refresh
|
|
315
837
|
|
|
316
|
-
|
|
838
|
+
Automatic token refresh is built-in when you provide a `refreshSession` action:
|
|
317
839
|
|
|
318
|
-
|
|
840
|
+
```typescript
|
|
841
|
+
actions: {
|
|
842
|
+
refreshSession: async () => {
|
|
843
|
+
// Your token refresh logic
|
|
844
|
+
const newSession = await yourApi.refreshToken()
|
|
845
|
+
return newSession
|
|
846
|
+
},
|
|
847
|
+
}
|
|
848
|
+
```
|
|
319
849
|
|
|
320
|
-
|
|
850
|
+
### Custom Callbacks
|
|
321
851
|
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
852
|
+
```typescript
|
|
853
|
+
callbacks: {
|
|
854
|
+
onSignIn: async (user, session) => {
|
|
855
|
+
// Track sign-in analytics
|
|
856
|
+
await analytics.track('user_signed_in', { userId: user.id })
|
|
857
|
+
},
|
|
858
|
+
onSignOut: async (user) => {
|
|
859
|
+
// Cleanup logic
|
|
860
|
+
await cleanupUserSession(user.id)
|
|
861
|
+
},
|
|
862
|
+
onError: async (error, context) => {
|
|
863
|
+
// Error logging
|
|
864
|
+
console.error(`Auth error in ${context}:`, error)
|
|
865
|
+
},
|
|
866
|
+
}
|
|
867
|
+
```
|
|
868
|
+
|
|
869
|
+
### OAuth State Store
|
|
870
|
+
|
|
871
|
+
For production, use a persistent OAuth state store:
|
|
872
|
+
|
|
873
|
+
```typescript
|
|
874
|
+
import { createRedisOAuthStateStore } from 'mulguard'
|
|
875
|
+
|
|
876
|
+
const auth = mulguard({
|
|
877
|
+
oauthStateStore: createRedisOAuthStateStore(redisClient),
|
|
878
|
+
// ... rest of config
|
|
879
|
+
})
|
|
880
|
+
```
|
|
881
|
+
|
|
882
|
+
---
|
|
883
|
+
|
|
884
|
+
## ๐ API Reference
|
|
885
|
+
|
|
886
|
+
### Core Functions
|
|
887
|
+
|
|
888
|
+
#### `mulguard(config: MulguardConfig): MulguardInstance`
|
|
889
|
+
|
|
890
|
+
Creates a new Mulguard authentication instance.
|
|
891
|
+
|
|
892
|
+
#### `getServerSession(auth: MulguardInstance): Promise<Session | null>`
|
|
893
|
+
|
|
894
|
+
Gets the current session on the server.
|
|
895
|
+
|
|
896
|
+
### Client Hooks
|
|
897
|
+
|
|
898
|
+
#### `useAuth(auth: MulguardInstance)`
|
|
899
|
+
|
|
900
|
+
Returns authentication methods and state.
|
|
901
|
+
|
|
902
|
+
```typescript
|
|
903
|
+
const { signIn, signOut, isLoading } = useAuth(auth)
|
|
904
|
+
```
|
|
905
|
+
|
|
906
|
+
#### `useSession(auth: MulguardInstance)`
|
|
907
|
+
|
|
908
|
+
Returns the current session.
|
|
909
|
+
|
|
910
|
+
```typescript
|
|
911
|
+
const { session, isLoading } = useSession(auth)
|
|
912
|
+
```
|
|
913
|
+
|
|
914
|
+
### Instance Methods
|
|
915
|
+
|
|
916
|
+
#### `auth.getSession(): Promise<Session | null>`
|
|
917
|
+
|
|
918
|
+
Gets the current session.
|
|
919
|
+
|
|
920
|
+
#### `auth.signIn.email(credentials): Promise<AuthResult>`
|
|
921
|
+
|
|
922
|
+
Signs in with email and password.
|
|
923
|
+
|
|
924
|
+
#### `auth.signIn.oauth(provider): Promise<{ url: string; state: string }>`
|
|
925
|
+
|
|
926
|
+
Initiates OAuth flow.
|
|
328
927
|
|
|
329
|
-
|
|
928
|
+
#### `auth.signOut(): Promise<{ success: boolean }>`
|
|
929
|
+
|
|
930
|
+
Signs out the current user.
|
|
931
|
+
|
|
932
|
+
For complete API documentation, see the [API Reference](./docs/API_REFERENCE.md).
|
|
933
|
+
|
|
934
|
+
---
|
|
935
|
+
|
|
936
|
+
## ๐ Examples & Guides
|
|
937
|
+
|
|
938
|
+
### Complete Guide
|
|
939
|
+
|
|
940
|
+
๐ **[Complete Usage Guide](./GUIDE.md)** - Comprehensive guide with:
|
|
941
|
+
- Best practices for writing `auth.ts`
|
|
942
|
+
- Production-ready examples
|
|
943
|
+
- Security recommendations
|
|
944
|
+
- Backend integration patterns
|
|
945
|
+
- Advanced usage examples
|
|
946
|
+
|
|
947
|
+
### Code Examples
|
|
948
|
+
|
|
949
|
+
- **[Basic Email Authentication](./src/examples/email-auth.ts)** - Simple email/password setup
|
|
950
|
+
- **[OAuth Authentication](./src/examples/oauth-auth.ts)** - Google, GitHub, etc.
|
|
951
|
+
- **[PassKey Authentication](./src/examples/passkey-auth.ts)** - WebAuthn/PassKey setup
|
|
952
|
+
- **[Two-Factor Authentication](./src/examples/two-factor-auth.ts)** - 2FA/TOTP implementation
|
|
953
|
+
- **[Middleware Integration](./src/examples/middleware-example.ts)** - Next.js middleware
|
|
954
|
+
- **[Server Components](./src/examples/server-component-example.tsx)** - Server-side usage
|
|
955
|
+
|
|
956
|
+
---
|
|
330
957
|
|
|
331
958
|
## ๐งช Testing
|
|
332
959
|
|
|
@@ -339,30 +966,77 @@ npm run test:watch
|
|
|
339
966
|
|
|
340
967
|
# Coverage
|
|
341
968
|
npm run test:coverage
|
|
969
|
+
|
|
970
|
+
# Type checking
|
|
971
|
+
npm run type-check
|
|
342
972
|
```
|
|
343
973
|
|
|
344
|
-
|
|
974
|
+
---
|
|
975
|
+
|
|
976
|
+
## ๐ค Contributing
|
|
977
|
+
|
|
978
|
+
Contributions are welcome! Please read our contributing guidelines before submitting a pull request.
|
|
979
|
+
|
|
980
|
+
1. Fork the repository
|
|
981
|
+
2. Create your feature branch (`git checkout -b feature/amazing-feature`)
|
|
982
|
+
3. Commit your changes (`git commit -m 'Add some amazing feature'`)
|
|
983
|
+
4. Push to the branch (`git push origin feature/amazing-feature`)
|
|
984
|
+
5. Open a Pull Request
|
|
985
|
+
|
|
986
|
+
### Development Setup
|
|
345
987
|
|
|
346
|
-
|
|
988
|
+
```bash
|
|
989
|
+
# Clone the repository
|
|
990
|
+
git clone https://github.com/mulverse/mulguard.git
|
|
991
|
+
|
|
992
|
+
# Install dependencies
|
|
993
|
+
npm install
|
|
347
994
|
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
- **Security**: Uses OIDC (OpenID Connect) for authentication
|
|
995
|
+
# Run development build
|
|
996
|
+
npm run dev
|
|
351
997
|
|
|
352
|
-
|
|
998
|
+
# Run tests
|
|
999
|
+
npm test
|
|
1000
|
+
```
|
|
1001
|
+
|
|
1002
|
+
---
|
|
353
1003
|
|
|
354
1004
|
## ๐ License
|
|
355
1005
|
|
|
356
|
-
|
|
1006
|
+
This project is licensed under the **MUV (Mulverse M.U.V General Public License)**.
|
|
357
1007
|
|
|
358
|
-
|
|
1008
|
+
Copyright (C) 2022 Mulverse Inc.
|
|
1009
|
+
|
|
1010
|
+
See the [LICENSE](./LICENSE) file for details.
|
|
1011
|
+
|
|
1012
|
+
---
|
|
1013
|
+
|
|
1014
|
+
## ๐ฌ Support
|
|
1015
|
+
|
|
1016
|
+
- **๐ Complete Guide**: [GUIDE.md](./GUIDE.md) - Comprehensive usage guide with best practices
|
|
1017
|
+
- **๐ Documentation**: Check the [docs](./docs) directory for detailed guides
|
|
1018
|
+
- **๐ Issues**: [GitHub Issues](https://github.com/mulverse/mulguard/issues)
|
|
1019
|
+
- **๐ฌ Discussions**: [GitHub Discussions](https://github.com/mulverse/mulguard/discussions)
|
|
1020
|
+
|
|
1021
|
+
## ๐ฏ Key Features & Improvements
|
|
1022
|
+
|
|
1023
|
+
### โจ Latest Updates
|
|
1024
|
+
|
|
1025
|
+
- **โ
Unified Sign-In Interface** - Use `auth.signIn('credentials', {...})` or `auth.signIn.email({...})`
|
|
1026
|
+
- **โ
Automatic Input Validation** - Built-in email, password, and provider validation
|
|
1027
|
+
- **โ
Enhanced Security** - Generic error messages, security logging, XSS/Injection prevention
|
|
1028
|
+
- **โ
Single Unified Logic** - All sign-in methods use the same core logic
|
|
1029
|
+
- **โ
Type-Safe** - Full TypeScript support with proper typing
|
|
1030
|
+
- **โ
Production Ready** - Best practices built-in by default
|
|
1031
|
+
|
|
1032
|
+
---
|
|
359
1033
|
|
|
360
|
-
|
|
1034
|
+
## ๐ About Mulverse
|
|
361
1035
|
|
|
362
|
-
|
|
1036
|
+
**Mulguard** is part of the **Mulverse** ecosystem - a collection of modern, developer-friendly libraries and tools for building next-generation web applications.
|
|
363
1037
|
|
|
364
|
-
|
|
1038
|
+
Visit [mulverse.com](https://mulverse.com) to learn more about our other projects.
|
|
365
1039
|
|
|
366
1040
|
---
|
|
367
1041
|
|
|
368
|
-
Made with โค๏ธ by the
|
|
1042
|
+
**Made with โค๏ธ by the Mulverse Team**
|