mulguard 1.0.1
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 +368 -0
- package/dist/actions-CExpv_dD.js +1 -0
- package/dist/actions-DeCfLtHA.mjs +184 -0
- package/dist/client/hooks.d.ts +122 -0
- package/dist/client/index.d.ts +5 -0
- package/dist/client/index.js +1 -0
- package/dist/client/index.mjs +476 -0
- package/dist/client/provider.d.ts +25 -0
- package/dist/client/server-actions-helper.d.ts +22 -0
- package/dist/components/AccountPicker.d.ts +11 -0
- package/dist/components/OAuthButton.d.ts +11 -0
- package/dist/components/PassKeyButton.d.ts +11 -0
- package/dist/components/PassKeyRegister.d.ts +10 -0
- package/dist/components/TwoFactorSetup.d.ts +8 -0
- package/dist/components/TwoFactorVerify.d.ts +9 -0
- package/dist/core/account-picker/encryption.d.ts +22 -0
- package/dist/core/account-picker/index.d.ts +22 -0
- package/dist/core/auth/index.d.ts +40 -0
- package/dist/core/auth/oauth-providers.d.ts +69 -0
- package/dist/core/auth/oauth-state-store.d.ts +44 -0
- package/dist/core/auth/oauth.d.ts +20 -0
- package/dist/core/auth/passkey.d.ts +35 -0
- package/dist/core/auth/password.d.ts +22 -0
- package/dist/core/auth/signin-unified.d.ts +33 -0
- package/dist/core/auth/two-factor.d.ts +28 -0
- package/dist/core/client/index.d.ts +132 -0
- package/dist/core/client/token-refresh-manager.d.ts +48 -0
- package/dist/core/index.d.ts +10 -0
- package/dist/core/security/csrf.d.ts +46 -0
- package/dist/core/security/headers.d.ts +24 -0
- package/dist/core/security/index.d.ts +28 -0
- package/dist/core/security/rate-limit.d.ts +39 -0
- package/dist/core/security/validation.d.ts +53 -0
- package/dist/core/security/xss.d.ts +20 -0
- package/dist/core/session/index.d.ts +35 -0
- package/dist/core/types/auth.d.ts +131 -0
- package/dist/core/types/errors.d.ts +44 -0
- package/dist/core/types/index.d.ts +369 -0
- package/dist/core/utils/auth-helpers.d.ts +136 -0
- package/dist/core/utils/logger.d.ts +17 -0
- package/dist/handlers/api.d.ts +10 -0
- package/dist/handlers/route.d.ts +22 -0
- package/dist/index/index.js +1 -0
- package/dist/index/index.mjs +1633 -0
- package/dist/index.d.ts +21 -0
- package/dist/middleware/index.d.ts +28 -0
- package/dist/middleware/proxy.d.ts +53 -0
- package/dist/middleware/security.d.ts +9 -0
- package/dist/mulguard.d.ts +263 -0
- package/dist/oauth-state-CzIWQq3s.js +1 -0
- package/dist/oauth-state-LE-qeq-K.mjs +282 -0
- package/dist/server/actions.d.ts +86 -0
- package/dist/server/auth.d.ts +65 -0
- package/dist/server/cookies.d.ts +42 -0
- package/dist/server/helpers.d.ts +10 -0
- package/dist/server/index.d.ts +14 -0
- package/dist/server/index.js +1 -0
- package/dist/server/index.mjs +31 -0
- package/dist/server/middleware.d.ts +39 -0
- package/dist/server/oauth-state.d.ts +24 -0
- package/dist/server/session-helpers.d.ts +26 -0
- package/dist/server/session.d.ts +28 -0
- package/dist/server/utils.d.ts +10 -0
- package/dist/signin-unified-BS2gxaG1.mjs +30 -0
- package/dist/signin-unified-Cw41EFkc.js +1 -0
- package/package.json +73 -0
package/README.md
ADDED
|
@@ -0,0 +1,368 @@
|
|
|
1
|
+
# Mukey - Modern Authentication Library
|
|
2
|
+
|
|
3
|
+
> Backend-first authentication library for Next.js and modern web applications
|
|
4
|
+
|
|
5
|
+
[](LICENSE)
|
|
6
|
+
[](https://www.typescriptlang.org/)
|
|
7
|
+
|
|
8
|
+
Mukey is a modern, secure authentication library designed for Next.js applications with a backend-first architecture. It provides a simple API similar to `better-auth` and `auth.js`, while maintaining full control over your backend authentication logic.
|
|
9
|
+
|
|
10
|
+
## ✨ Features
|
|
11
|
+
|
|
12
|
+
- 🔐 **Multiple Auth Methods**
|
|
13
|
+
- Email/Password authentication
|
|
14
|
+
- OAuth 2.1 (Google, GitHub, etc.)
|
|
15
|
+
- PassKey/WebAuthn support
|
|
16
|
+
- Two-Factor Authentication (2FA/TOTP)
|
|
17
|
+
|
|
18
|
+
- 🎯 **Account Picker**
|
|
19
|
+
- Remember last logged-in users
|
|
20
|
+
- Quick re-login experience
|
|
21
|
+
- Encrypted local storage
|
|
22
|
+
|
|
23
|
+
- 🔒 **Security First**
|
|
24
|
+
- CSRF protection
|
|
25
|
+
- XSS prevention
|
|
26
|
+
- Input validation & sanitization
|
|
27
|
+
- Security headers
|
|
28
|
+
- Rate limiting utilities
|
|
29
|
+
|
|
30
|
+
- ⚡ **Next.js Optimized**
|
|
31
|
+
- Server Components support
|
|
32
|
+
- Client hooks (`useAuth`, `useSession`)
|
|
33
|
+
- Middleware integration
|
|
34
|
+
- API route handlers
|
|
35
|
+
|
|
36
|
+
- 🔌 **Backend-First**
|
|
37
|
+
- No database adapter required
|
|
38
|
+
- Works with your existing backend API
|
|
39
|
+
- Mulink integration support
|
|
40
|
+
- Full TypeScript support
|
|
41
|
+
|
|
42
|
+
## 📦 Installation
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
npm install mukey
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## 🚀 Quick Start
|
|
49
|
+
|
|
50
|
+
### 1. Create Auth Configuration
|
|
51
|
+
|
|
52
|
+
```typescript
|
|
53
|
+
// lib/auth.ts
|
|
54
|
+
import { mukey } from 'mukey'
|
|
55
|
+
// import { apiClient } from '@mulink/client' // Optional: Use Mulink
|
|
56
|
+
|
|
57
|
+
export const auth = mukey({
|
|
58
|
+
// Option 1: Use Mulink API client
|
|
59
|
+
// apiClient: apiClient,
|
|
60
|
+
|
|
61
|
+
// Option 2: Use baseURL
|
|
62
|
+
baseURL: process.env.NEXT_PUBLIC_API_URL || 'http://localhost:3001',
|
|
63
|
+
|
|
64
|
+
session: {
|
|
65
|
+
cookieName: '__mukey_session',
|
|
66
|
+
expiresIn: 60 * 60 * 24 * 7, // 7 days
|
|
67
|
+
},
|
|
68
|
+
|
|
69
|
+
providers: {
|
|
70
|
+
oauth: {
|
|
71
|
+
google: {
|
|
72
|
+
clientId: process.env.GOOGLE_CLIENT_ID!,
|
|
73
|
+
redirectUri: `${process.env.NEXT_PUBLIC_URL}/api/auth/callback/google`,
|
|
74
|
+
scopes: ['openid', 'profile', 'email'],
|
|
75
|
+
},
|
|
76
|
+
},
|
|
77
|
+
},
|
|
78
|
+
|
|
79
|
+
accountPicker: {
|
|
80
|
+
enabled: true,
|
|
81
|
+
maxAccounts: 3,
|
|
82
|
+
},
|
|
83
|
+
})
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
### 2. Server-Side Usage
|
|
87
|
+
|
|
88
|
+
```typescript
|
|
89
|
+
// app/dashboard/page.tsx
|
|
90
|
+
import { getServerSession } from 'mukey/server'
|
|
91
|
+
import { auth } from '@/lib/auth'
|
|
92
|
+
import { redirect } from 'next/navigation'
|
|
93
|
+
|
|
94
|
+
export default async function DashboardPage() {
|
|
95
|
+
const session = await getServerSession(auth)
|
|
96
|
+
|
|
97
|
+
if (!session) {
|
|
98
|
+
redirect('/login')
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
return <div>Welcome, {session.user.name}!</div>
|
|
102
|
+
}
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
### 3. Client-Side Usage
|
|
106
|
+
|
|
107
|
+
```typescript
|
|
108
|
+
// app/login/page.tsx
|
|
109
|
+
'use client'
|
|
110
|
+
|
|
111
|
+
import { useAuth } from 'mukey/client'
|
|
112
|
+
import { auth } from '@/lib/auth'
|
|
113
|
+
import { useState } from 'react'
|
|
114
|
+
|
|
115
|
+
export default function LoginPage() {
|
|
116
|
+
const { signIn, session, isLoading } = useAuth(auth)
|
|
117
|
+
const [email, setEmail] = useState('')
|
|
118
|
+
const [password, setPassword] = useState('')
|
|
119
|
+
|
|
120
|
+
const handleSubmit = async (e: React.FormEvent) => {
|
|
121
|
+
e.preventDefault()
|
|
122
|
+
const result = await signIn.email({ email, password })
|
|
123
|
+
if (result.success) {
|
|
124
|
+
// Redirect to dashboard
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
if (isLoading) return <div>Loading...</div>
|
|
129
|
+
if (session) return <div>Already logged in</div>
|
|
130
|
+
|
|
131
|
+
return (
|
|
132
|
+
<form onSubmit={handleSubmit}>
|
|
133
|
+
<input
|
|
134
|
+
type="email"
|
|
135
|
+
value={email}
|
|
136
|
+
onChange={(e) => setEmail(e.target.value)}
|
|
137
|
+
placeholder="Email"
|
|
138
|
+
/>
|
|
139
|
+
<input
|
|
140
|
+
type="password"
|
|
141
|
+
value={password}
|
|
142
|
+
onChange={(e) => setPassword(e.target.value)}
|
|
143
|
+
placeholder="Password"
|
|
144
|
+
/>
|
|
145
|
+
<button type="submit">Sign In</button>
|
|
146
|
+
</form>
|
|
147
|
+
)
|
|
148
|
+
}
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
### 4. Middleware (Optional)
|
|
152
|
+
|
|
153
|
+
```typescript
|
|
154
|
+
// middleware.ts
|
|
155
|
+
import { createAuthMiddleware } from 'mukey/middleware'
|
|
156
|
+
import { auth } from '@/lib/auth'
|
|
157
|
+
|
|
158
|
+
export default createAuthMiddleware(auth, {
|
|
159
|
+
protectedRoutes: ['/dashboard', '/profile'],
|
|
160
|
+
redirectTo: '/login',
|
|
161
|
+
redirectIfAuthenticated: '/dashboard',
|
|
162
|
+
})
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
## 📚 Documentation
|
|
166
|
+
|
|
167
|
+
- [Getting Started](./docs/GETTING_STARTED.md)
|
|
168
|
+
- [Authentication Methods](./docs/AUTH_METHODS.md)
|
|
169
|
+
- [Server-Side Usage](./docs/SERVER_USAGE.md)
|
|
170
|
+
- [Client-Side Usage](./docs/CLIENT_USAGE.md)
|
|
171
|
+
- [Security](./docs/SECURITY.md)
|
|
172
|
+
- [Backend Integration](./docs/BACKEND_INTEGRATION.md)
|
|
173
|
+
- [API Reference](./docs/API_REFERENCE.md)
|
|
174
|
+
|
|
175
|
+
## 🔧 Configuration
|
|
176
|
+
|
|
177
|
+
### Full Configuration Options
|
|
178
|
+
|
|
179
|
+
```typescript
|
|
180
|
+
import { mukey } from 'mukey'
|
|
181
|
+
|
|
182
|
+
export const auth = mukey({
|
|
183
|
+
// API Client (from Mulink) or baseURL
|
|
184
|
+
apiClient: apiClient, // Optional
|
|
185
|
+
baseURL: 'http://localhost:3001', // Required if apiClient not provided
|
|
186
|
+
|
|
187
|
+
// Session configuration
|
|
188
|
+
session: {
|
|
189
|
+
cookieName: '__mukey_session',
|
|
190
|
+
expiresIn: 60 * 60 * 24 * 7, // 7 days in seconds
|
|
191
|
+
httpOnly: true,
|
|
192
|
+
secure: process.env.NODE_ENV === 'production',
|
|
193
|
+
sameSite: 'lax',
|
|
194
|
+
},
|
|
195
|
+
|
|
196
|
+
// OAuth providers
|
|
197
|
+
providers: {
|
|
198
|
+
oauth: {
|
|
199
|
+
google: {
|
|
200
|
+
clientId: '...',
|
|
201
|
+
redirectUri: '...',
|
|
202
|
+
scopes: ['openid', 'profile', 'email'],
|
|
203
|
+
name: 'Google',
|
|
204
|
+
},
|
|
205
|
+
github: {
|
|
206
|
+
clientId: '...',
|
|
207
|
+
redirectUri: '...',
|
|
208
|
+
scopes: ['user:email'],
|
|
209
|
+
},
|
|
210
|
+
},
|
|
211
|
+
},
|
|
212
|
+
|
|
213
|
+
// 2FA configuration
|
|
214
|
+
twoFactor: {
|
|
215
|
+
enabled: true,
|
|
216
|
+
},
|
|
217
|
+
|
|
218
|
+
// Account Picker
|
|
219
|
+
accountPicker: {
|
|
220
|
+
enabled: true,
|
|
221
|
+
maxAccounts: 3,
|
|
222
|
+
encryptStorage: true,
|
|
223
|
+
storageKey: '__mukey_accounts',
|
|
224
|
+
},
|
|
225
|
+
|
|
226
|
+
// Security
|
|
227
|
+
security: {
|
|
228
|
+
csrfProtection: true,
|
|
229
|
+
rateLimiting: {
|
|
230
|
+
maxAttempts: 5,
|
|
231
|
+
windowMs: 15 * 60 * 1000, // 15 minutes
|
|
232
|
+
},
|
|
233
|
+
},
|
|
234
|
+
})
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
## 🎨 Components
|
|
238
|
+
|
|
239
|
+
### Account Picker
|
|
240
|
+
|
|
241
|
+
```typescript
|
|
242
|
+
import { AccountPicker } from 'mukey'
|
|
243
|
+
|
|
244
|
+
<AccountPicker
|
|
245
|
+
auth={auth}
|
|
246
|
+
onSelectUser={(user) => {
|
|
247
|
+
// Pre-fill email or handle quick login
|
|
248
|
+
setEmail(user.email)
|
|
249
|
+
}}
|
|
250
|
+
onUseDifferentAccount={() => {
|
|
251
|
+
// Show full login form
|
|
252
|
+
}}
|
|
253
|
+
/>
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
### OAuth Button
|
|
257
|
+
|
|
258
|
+
```typescript
|
|
259
|
+
import { OAuthButton } from 'mukey'
|
|
260
|
+
|
|
261
|
+
<OAuthButton
|
|
262
|
+
auth={auth}
|
|
263
|
+
provider="google"
|
|
264
|
+
onSuccess={() => {
|
|
265
|
+
// Handle success
|
|
266
|
+
}}
|
|
267
|
+
/>
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
### 2FA Setup
|
|
271
|
+
|
|
272
|
+
```typescript
|
|
273
|
+
import { TwoFactorSetup } from 'mukey'
|
|
274
|
+
|
|
275
|
+
<TwoFactorSetup
|
|
276
|
+
auth={auth}
|
|
277
|
+
onSuccess={() => {
|
|
278
|
+
// 2FA enabled
|
|
279
|
+
}}
|
|
280
|
+
/>
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
## 🔌 Backend Integration
|
|
284
|
+
|
|
285
|
+
Mukey expects your backend to provide the following endpoints:
|
|
286
|
+
|
|
287
|
+
### Authentication Endpoints
|
|
288
|
+
|
|
289
|
+
- `POST /api/auth/sign-in` - Sign in with email/password
|
|
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
|
|
295
|
+
|
|
296
|
+
### OAuth Endpoints
|
|
297
|
+
|
|
298
|
+
- `GET /api/auth/oauth/:provider` - Initiate OAuth flow
|
|
299
|
+
- `POST /api/auth/oauth/:provider/callback` - Handle OAuth callback
|
|
300
|
+
|
|
301
|
+
### 2FA Endpoints
|
|
302
|
+
|
|
303
|
+
- `POST /api/auth/2fa/enable` - Enable 2FA
|
|
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
|
|
308
|
+
|
|
309
|
+
### PassKey Endpoints
|
|
310
|
+
|
|
311
|
+
- `POST /api/auth/passkey/register` - Register PassKey
|
|
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
|
|
315
|
+
|
|
316
|
+
See [Backend Integration Guide](./docs/BACKEND_INTEGRATION.md) for detailed API specifications.
|
|
317
|
+
|
|
318
|
+
## 🔒 Security
|
|
319
|
+
|
|
320
|
+
Mukey implements multiple security features:
|
|
321
|
+
|
|
322
|
+
- **CSRF Protection** - Token-based CSRF protection
|
|
323
|
+
- **XSS Prevention** - Input sanitization and HTML escaping
|
|
324
|
+
- **Rate Limiting** - Client-side rate limit tracking
|
|
325
|
+
- **Security Headers** - Automatic security headers
|
|
326
|
+
- **Input Validation** - Comprehensive input validation
|
|
327
|
+
- **Secure Cookies** - HttpOnly, Secure, SameSite cookies
|
|
328
|
+
|
|
329
|
+
See [Security Guide](./docs/SECURITY.md) for more details.
|
|
330
|
+
|
|
331
|
+
## 🧪 Testing
|
|
332
|
+
|
|
333
|
+
```bash
|
|
334
|
+
# Run tests
|
|
335
|
+
npm test
|
|
336
|
+
|
|
337
|
+
# Watch mode
|
|
338
|
+
npm run test:watch
|
|
339
|
+
|
|
340
|
+
# Coverage
|
|
341
|
+
npm run test:coverage
|
|
342
|
+
```
|
|
343
|
+
|
|
344
|
+
## 🚀 CI/CD
|
|
345
|
+
|
|
346
|
+
This project uses GitHub Actions with npm Trusted Publisher for secure publishing.
|
|
347
|
+
|
|
348
|
+
- **CI**: Runs on every push/PR (lint, test, build)
|
|
349
|
+
- **Publishing**: Automatic publishing via Changesets or manual release
|
|
350
|
+
- **Security**: Uses OIDC (OpenID Connect) for authentication
|
|
351
|
+
|
|
352
|
+
See [README_CI.md](./README_CI.md) for setup instructions.
|
|
353
|
+
|
|
354
|
+
## 📝 License
|
|
355
|
+
|
|
356
|
+
MUV License - See [LICENSE](./LICENSE) file for details.
|
|
357
|
+
|
|
358
|
+
## 🤝 Contributing
|
|
359
|
+
|
|
360
|
+
Contributions are welcome! Please read our contributing guidelines first.
|
|
361
|
+
|
|
362
|
+
## 📧 Support
|
|
363
|
+
|
|
364
|
+
For questions and support, please open an issue on GitHub.
|
|
365
|
+
|
|
366
|
+
---
|
|
367
|
+
|
|
368
|
+
Made with ❤️ by the Mukey Team
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";const O=require("next/headers");var r=(s=>(s.INVALID_CREDENTIALS="INVALID_CREDENTIALS",s.ACCOUNT_LOCKED="ACCOUNT_LOCKED",s.ACCOUNT_INACTIVE="ACCOUNT_INACTIVE",s.TWO_FA_REQUIRED="TWO_FA_REQUIRED",s.INVALID_TWO_FA_CODE="INVALID_TWO_FA_CODE",s.SESSION_EXPIRED="SESSION_EXPIRED",s.UNAUTHORIZED="UNAUTHORIZED",s.NETWORK_ERROR="NETWORK_ERROR",s.VALIDATION_ERROR="VALIDATION_ERROR",s.RATE_LIMITED="RATE_LIMITED",s.UNKNOWN_ERROR="UNKNOWN_ERROR",s))(r||{});async function R(s){var n;try{return(n=(await O.cookies()).get(s))==null?void 0:n.value}catch(e){const o=(e==null?void 0:e.message)||"";if(o.includes("cookies")||o.includes("request scope")||o.includes("outside")||o.includes("dynamic"))return;throw e}}async function u(s){try{return(await O.cookies()).set({name:s.name,value:s.value,maxAge:s.maxAge,expires:s.expires,httpOnly:s.httpOnly??!0,secure:s.secure,sameSite:s.sameSite??"lax",path:s.path??"/",domain:s.domain}),{success:!0}}catch(n){const e=(n==null?void 0:n.message)||"";if(e.includes("cookies")||e.includes("request scope")||e.includes("outside")||e.includes("dynamic")){const o=`Cannot set cookie "${s.name}" outside request scope. Make sure this is called from a Server Action or Route Handler.`;return process.env.NODE_ENV==="development"&&console.warn(`[Mulguard] ${o}`),{success:!1,error:e,warning:o}}throw n}}async function f(s,n){try{(await O.cookies()).set({name:s,value:"",maxAge:0,expires:new Date(0),httpOnly:!0,path:(n==null?void 0:n.path)??"/",domain:n==null?void 0:n.domain})}catch(e){const o=(e==null?void 0:e.message)||"";if(o.includes("cookies")||o.includes("request scope")||o.includes("outside")||o.includes("dynamic")){process.env.NODE_ENV==="development"&&console.warn(`[Mulguard] Cannot delete cookie "${s}" outside request scope`);return}throw e}}function l(s,n,e){const o=process.env.NODE_ENV==="production";return{name:s,value:n,maxAge:e.expiresIn,httpOnly:e.httpOnly??!0,secure:e.secure??o,sameSite:e.sameSite??"lax",path:e.path??"/"}}async function g(s,n){if(!s.verify2FA)return{success:!1,error:"2FA verification is not configured",errorCode:r.VALIDATION_ERROR};try{const e=await s.verify2FA(n,{skipCookieSave:!0});if(e.success&&e.session)try{const{cookieName:o,config:t}=s._getSessionConfig(),c=typeof e.session=="object"&&"token"in e.session?String(e.session.token):JSON.stringify(e.session),i=l(o,c,t),a=await u(i);a.success||process.env.NODE_ENV==="development"&&console.warn("[Mulguard] Failed to save session after 2FA verification:",a.error||a.warning)}catch(o){process.env.NODE_ENV==="development"&&console.warn("[Mulguard] Failed to save session cookie:",o)}return e}catch(e){return{success:!1,error:e instanceof Error?e.message:"2FA verification failed",errorCode:r.UNKNOWN_ERROR}}}async function N(s){var n;try{const e=await s.getSession(),o=e==null?void 0:e.user;s.signOut&&await s.signOut();const{cookieName:t,config:c}=s._getSessionConfig();await f(t,{path:c.path||"/"});const i=(n=s._getCallbacks)==null?void 0:n.call(s);return o&&(i!=null&&i.onSignOut)&&await i.onSignOut(o),{success:!0}}catch(e){return{success:!1,error:e instanceof Error?e.message:"Sign out failed"}}}async function d(s,n){var e;if(!((e=s.signIn)!=null&&e.email))return{success:!1,error:"Email sign in is not configured",errorCode:r.VALIDATION_ERROR};try{const o=await s.signIn.email(n);if(o.success&&o.session)try{const{cookieName:t,config:c}=s._getSessionConfig(),i=typeof o.session=="object"&&"token"in o.session?String(o.session.token):JSON.stringify(o.session),a=l(t,i,c);await u(a)}catch(t){process.env.NODE_ENV==="development"&&console.warn("[Mulguard] Failed to save session cookie:",t)}return o}catch(o){return{success:!1,error:o instanceof Error?o.message:"Sign in failed",errorCode:r.UNKNOWN_ERROR}}}async function E(s,n){if(!s.signUp)return{success:!1,error:"Sign up is not configured",errorCode:r.VALIDATION_ERROR};try{const e=await s.signUp(n);if(e.success&&e.session)try{const{cookieName:o,config:t}=s._getSessionConfig(),c=typeof e.session=="object"&&"token"in e.session?String(e.session.token):JSON.stringify(e.session),i=l(o,c,t);await u(i)}catch(o){process.env.NODE_ENV==="development"&&console.warn("[Mulguard] Failed to save session cookie:",o)}return e}catch(e){return{success:!1,error:e instanceof Error?e.message:"Sign up failed",errorCode:r.UNKNOWN_ERROR}}}const _=Object.freeze(Object.defineProperty({__proto__:null,signInEmailAction:d,signOutAction:N,signUpAction:E,verify2FAAction:g},Symbol.toStringTag,{value:"Module"}));exports.AuthErrorCode=r;exports.actions=_;exports.buildCookieOptions=l;exports.deleteCookie=f;exports.getCookie=R;exports.setCookie=u;exports.signInEmailAction=d;exports.signOutAction=N;exports.signUpAction=E;exports.verify2FAAction=g;
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
import { cookies as u } from "next/headers";
|
|
2
|
+
var c = /* @__PURE__ */ ((s) => (s.INVALID_CREDENTIALS = "INVALID_CREDENTIALS", s.ACCOUNT_LOCKED = "ACCOUNT_LOCKED", s.ACCOUNT_INACTIVE = "ACCOUNT_INACTIVE", s.TWO_FA_REQUIRED = "TWO_FA_REQUIRED", s.INVALID_TWO_FA_CODE = "INVALID_TWO_FA_CODE", s.SESSION_EXPIRED = "SESSION_EXPIRED", s.UNAUTHORIZED = "UNAUTHORIZED", s.NETWORK_ERROR = "NETWORK_ERROR", s.VALIDATION_ERROR = "VALIDATION_ERROR", s.RATE_LIMITED = "RATE_LIMITED", s.UNKNOWN_ERROR = "UNKNOWN_ERROR", s))(c || {});
|
|
3
|
+
async function _(s) {
|
|
4
|
+
var o;
|
|
5
|
+
try {
|
|
6
|
+
return (o = (await u()).get(s)) == null ? void 0 : o.value;
|
|
7
|
+
} catch (e) {
|
|
8
|
+
const n = (e == null ? void 0 : e.message) || "";
|
|
9
|
+
if (n.includes("cookies") || n.includes("request scope") || n.includes("outside") || n.includes("dynamic"))
|
|
10
|
+
return;
|
|
11
|
+
throw e;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
async function l(s) {
|
|
15
|
+
try {
|
|
16
|
+
return (await u()).set({
|
|
17
|
+
name: s.name,
|
|
18
|
+
value: s.value,
|
|
19
|
+
maxAge: s.maxAge,
|
|
20
|
+
expires: s.expires,
|
|
21
|
+
httpOnly: s.httpOnly ?? !0,
|
|
22
|
+
secure: s.secure,
|
|
23
|
+
sameSite: s.sameSite ?? "lax",
|
|
24
|
+
path: s.path ?? "/",
|
|
25
|
+
domain: s.domain
|
|
26
|
+
}), { success: !0 };
|
|
27
|
+
} catch (o) {
|
|
28
|
+
const e = (o == null ? void 0 : o.message) || "";
|
|
29
|
+
if (e.includes("cookies") || e.includes("request scope") || e.includes("outside") || e.includes("dynamic")) {
|
|
30
|
+
const n = `Cannot set cookie "${s.name}" outside request scope. Make sure this is called from a Server Action or Route Handler.`;
|
|
31
|
+
return process.env.NODE_ENV === "development" && console.warn(`[Mulguard] ${n}`), {
|
|
32
|
+
success: !1,
|
|
33
|
+
error: e,
|
|
34
|
+
warning: n
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
throw o;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
async function O(s, o) {
|
|
41
|
+
try {
|
|
42
|
+
(await u()).set({
|
|
43
|
+
name: s,
|
|
44
|
+
value: "",
|
|
45
|
+
maxAge: 0,
|
|
46
|
+
expires: /* @__PURE__ */ new Date(0),
|
|
47
|
+
httpOnly: !0,
|
|
48
|
+
path: (o == null ? void 0 : o.path) ?? "/",
|
|
49
|
+
domain: o == null ? void 0 : o.domain
|
|
50
|
+
});
|
|
51
|
+
} catch (e) {
|
|
52
|
+
const n = (e == null ? void 0 : e.message) || "";
|
|
53
|
+
if (n.includes("cookies") || n.includes("request scope") || n.includes("outside") || n.includes("dynamic")) {
|
|
54
|
+
process.env.NODE_ENV === "development" && console.warn(`[Mulguard] Cannot delete cookie "${s}" outside request scope`);
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
throw e;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
function f(s, o, e) {
|
|
61
|
+
const n = process.env.NODE_ENV === "production";
|
|
62
|
+
return {
|
|
63
|
+
name: s,
|
|
64
|
+
value: o,
|
|
65
|
+
maxAge: e.expiresIn,
|
|
66
|
+
httpOnly: e.httpOnly ?? !0,
|
|
67
|
+
secure: e.secure ?? n,
|
|
68
|
+
sameSite: e.sameSite ?? "lax",
|
|
69
|
+
path: e.path ?? "/"
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
async function g(s, o) {
|
|
73
|
+
if (!s.verify2FA)
|
|
74
|
+
return {
|
|
75
|
+
success: !1,
|
|
76
|
+
error: "2FA verification is not configured",
|
|
77
|
+
errorCode: c.VALIDATION_ERROR
|
|
78
|
+
};
|
|
79
|
+
try {
|
|
80
|
+
const e = await s.verify2FA(o, { skipCookieSave: !0 });
|
|
81
|
+
if (e.success && e.session)
|
|
82
|
+
try {
|
|
83
|
+
const { cookieName: n, config: r } = s._getSessionConfig(), t = typeof e.session == "object" && "token" in e.session ? String(e.session.token) : JSON.stringify(e.session), i = f(n, t, r), a = await l(i);
|
|
84
|
+
a.success || process.env.NODE_ENV === "development" && console.warn("[Mulguard] Failed to save session after 2FA verification:", a.error || a.warning);
|
|
85
|
+
} catch (n) {
|
|
86
|
+
process.env.NODE_ENV === "development" && console.warn("[Mulguard] Failed to save session cookie:", n);
|
|
87
|
+
}
|
|
88
|
+
return e;
|
|
89
|
+
} catch (e) {
|
|
90
|
+
return {
|
|
91
|
+
success: !1,
|
|
92
|
+
error: e instanceof Error ? e.message : "2FA verification failed",
|
|
93
|
+
errorCode: c.UNKNOWN_ERROR
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
async function N(s) {
|
|
98
|
+
var o;
|
|
99
|
+
try {
|
|
100
|
+
const e = await s.getSession(), n = e == null ? void 0 : e.user;
|
|
101
|
+
s.signOut && await s.signOut();
|
|
102
|
+
const { cookieName: r, config: t } = s._getSessionConfig();
|
|
103
|
+
await O(r, {
|
|
104
|
+
path: t.path || "/"
|
|
105
|
+
});
|
|
106
|
+
const i = (o = s._getCallbacks) == null ? void 0 : o.call(s);
|
|
107
|
+
return n && (i != null && i.onSignOut) && await i.onSignOut(n), { success: !0 };
|
|
108
|
+
} catch (e) {
|
|
109
|
+
return {
|
|
110
|
+
success: !1,
|
|
111
|
+
error: e instanceof Error ? e.message : "Sign out failed"
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
async function d(s, o) {
|
|
116
|
+
var e;
|
|
117
|
+
if (!((e = s.signIn) != null && e.email))
|
|
118
|
+
return {
|
|
119
|
+
success: !1,
|
|
120
|
+
error: "Email sign in is not configured",
|
|
121
|
+
errorCode: c.VALIDATION_ERROR
|
|
122
|
+
};
|
|
123
|
+
try {
|
|
124
|
+
const n = await s.signIn.email(o);
|
|
125
|
+
if (n.success && n.session)
|
|
126
|
+
try {
|
|
127
|
+
const { cookieName: r, config: t } = s._getSessionConfig(), i = typeof n.session == "object" && "token" in n.session ? String(n.session.token) : JSON.stringify(n.session), a = f(r, i, t);
|
|
128
|
+
await l(a);
|
|
129
|
+
} catch (r) {
|
|
130
|
+
process.env.NODE_ENV === "development" && console.warn("[Mulguard] Failed to save session cookie:", r);
|
|
131
|
+
}
|
|
132
|
+
return n;
|
|
133
|
+
} catch (n) {
|
|
134
|
+
return {
|
|
135
|
+
success: !1,
|
|
136
|
+
error: n instanceof Error ? n.message : "Sign in failed",
|
|
137
|
+
errorCode: c.UNKNOWN_ERROR
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
async function E(s, o) {
|
|
142
|
+
if (!s.signUp)
|
|
143
|
+
return {
|
|
144
|
+
success: !1,
|
|
145
|
+
error: "Sign up is not configured",
|
|
146
|
+
errorCode: c.VALIDATION_ERROR
|
|
147
|
+
};
|
|
148
|
+
try {
|
|
149
|
+
const e = await s.signUp(o);
|
|
150
|
+
if (e.success && e.session)
|
|
151
|
+
try {
|
|
152
|
+
const { cookieName: n, config: r } = s._getSessionConfig(), t = typeof e.session == "object" && "token" in e.session ? String(e.session.token) : JSON.stringify(e.session), i = f(n, t, r);
|
|
153
|
+
await l(i);
|
|
154
|
+
} catch (n) {
|
|
155
|
+
process.env.NODE_ENV === "development" && console.warn("[Mulguard] Failed to save session cookie:", n);
|
|
156
|
+
}
|
|
157
|
+
return e;
|
|
158
|
+
} catch (e) {
|
|
159
|
+
return {
|
|
160
|
+
success: !1,
|
|
161
|
+
error: e instanceof Error ? e.message : "Sign up failed",
|
|
162
|
+
errorCode: c.UNKNOWN_ERROR
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
const m = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
|
|
167
|
+
__proto__: null,
|
|
168
|
+
signInEmailAction: d,
|
|
169
|
+
signOutAction: N,
|
|
170
|
+
signUpAction: E,
|
|
171
|
+
verify2FAAction: g
|
|
172
|
+
}, Symbol.toStringTag, { value: "Module" }));
|
|
173
|
+
export {
|
|
174
|
+
c as A,
|
|
175
|
+
d as a,
|
|
176
|
+
E as b,
|
|
177
|
+
l as c,
|
|
178
|
+
O as d,
|
|
179
|
+
f as e,
|
|
180
|
+
m as f,
|
|
181
|
+
_ as g,
|
|
182
|
+
N as s,
|
|
183
|
+
g as v
|
|
184
|
+
};
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import { Session, AuthResult, EmailCredentials, RegisterData, RememberedUser, Verify2FAData } from '../core/types';
|
|
2
|
+
import { MulguardInstance } from '../mulguard';
|
|
3
|
+
export interface UseAuthReturn {
|
|
4
|
+
session: Session | null;
|
|
5
|
+
isLoading: boolean;
|
|
6
|
+
/**
|
|
7
|
+
* Unified sign in method - professional interface similar to auth.js
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* ```typescript
|
|
11
|
+
* // OAuth providers
|
|
12
|
+
* await signIn('google')
|
|
13
|
+
* await signIn('github')
|
|
14
|
+
*
|
|
15
|
+
* // Credentials
|
|
16
|
+
* await signIn('credentials', { email: 'user@example.com', password: 'password' })
|
|
17
|
+
*
|
|
18
|
+
* // OTP
|
|
19
|
+
* await signIn('otp', { email: 'user@example.com', code: '123456' })
|
|
20
|
+
* ```
|
|
21
|
+
*/
|
|
22
|
+
signIn(provider: 'google' | 'github' | 'apple' | 'facebook' | string): Promise<{
|
|
23
|
+
url: string;
|
|
24
|
+
state: string;
|
|
25
|
+
}>;
|
|
26
|
+
signIn(provider: 'credentials', credentials: EmailCredentials): Promise<AuthResult>;
|
|
27
|
+
signIn(provider: 'otp', options: {
|
|
28
|
+
email: string;
|
|
29
|
+
code?: string;
|
|
30
|
+
}): Promise<AuthResult>;
|
|
31
|
+
signIn(provider: 'passkey', options?: {
|
|
32
|
+
userId?: string;
|
|
33
|
+
}): Promise<AuthResult>;
|
|
34
|
+
/**
|
|
35
|
+
* Legacy sign in methods (for backward compatibility)
|
|
36
|
+
*/
|
|
37
|
+
signInMethods: {
|
|
38
|
+
email(credentials: EmailCredentials): Promise<AuthResult>;
|
|
39
|
+
oauth(provider: string): Promise<{
|
|
40
|
+
url: string;
|
|
41
|
+
state: string;
|
|
42
|
+
}>;
|
|
43
|
+
passkey(options?: {
|
|
44
|
+
userId?: string;
|
|
45
|
+
}): Promise<AuthResult>;
|
|
46
|
+
otp(email: string, code?: string): Promise<AuthResult>;
|
|
47
|
+
};
|
|
48
|
+
signUp(data: RegisterData): Promise<AuthResult>;
|
|
49
|
+
signOut(): Promise<void>;
|
|
50
|
+
resetPassword(email: string): Promise<{
|
|
51
|
+
success: boolean;
|
|
52
|
+
error?: string;
|
|
53
|
+
}>;
|
|
54
|
+
verifyEmail(token: string): Promise<{
|
|
55
|
+
success: boolean;
|
|
56
|
+
error?: string;
|
|
57
|
+
}>;
|
|
58
|
+
verify2FA(data: Verify2FAData): Promise<AuthResult>;
|
|
59
|
+
}
|
|
60
|
+
export interface UseSessionReturn {
|
|
61
|
+
session: Session | null;
|
|
62
|
+
isLoading: boolean;
|
|
63
|
+
error: Error | null;
|
|
64
|
+
}
|
|
65
|
+
export interface UseAccountPickerReturn {
|
|
66
|
+
lastUsers: RememberedUser[];
|
|
67
|
+
isLoading: boolean;
|
|
68
|
+
rememberUser: (user: {
|
|
69
|
+
id: string;
|
|
70
|
+
email: string;
|
|
71
|
+
name: string;
|
|
72
|
+
avatar?: string;
|
|
73
|
+
}, provider?: 'email' | 'oauth' | 'passkey') => Promise<void>;
|
|
74
|
+
clearUser: (userId: string) => Promise<void>;
|
|
75
|
+
clearAll: () => Promise<void>;
|
|
76
|
+
refresh: () => Promise<void>;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Main authentication hook
|
|
80
|
+
*
|
|
81
|
+
* @example
|
|
82
|
+
* ```tsx
|
|
83
|
+
* import { useAuth } from 'mulguard/client'
|
|
84
|
+
* import { auth } from '@/auth'
|
|
85
|
+
*
|
|
86
|
+
* function LoginButton() {
|
|
87
|
+
* const { session, signIn, signOut } = useAuth(auth)
|
|
88
|
+
* // ...
|
|
89
|
+
* }
|
|
90
|
+
* ```
|
|
91
|
+
*/
|
|
92
|
+
export declare function useAuth(auth: MulguardInstance): UseAuthReturn;
|
|
93
|
+
/**
|
|
94
|
+
* Session hook
|
|
95
|
+
*
|
|
96
|
+
* @example
|
|
97
|
+
* ```tsx
|
|
98
|
+
* import { useSession } from 'mulguard/client'
|
|
99
|
+
* import { auth } from '@/auth'
|
|
100
|
+
*
|
|
101
|
+
* function Profile() {
|
|
102
|
+
* const { session, isLoading } = useSession(auth)
|
|
103
|
+
* // ...
|
|
104
|
+
* }
|
|
105
|
+
* ```
|
|
106
|
+
*/
|
|
107
|
+
export declare function useSession(auth: MulguardInstance): UseSessionReturn;
|
|
108
|
+
/**
|
|
109
|
+
* Account Picker hook
|
|
110
|
+
*
|
|
111
|
+
* @example
|
|
112
|
+
* ```tsx
|
|
113
|
+
* import { useAccountPicker } from 'mulguard/client'
|
|
114
|
+
* import { auth } from '@/auth'
|
|
115
|
+
*
|
|
116
|
+
* function AccountList() {
|
|
117
|
+
* const { lastUsers, clearUser } = useAccountPicker(auth)
|
|
118
|
+
* // ...
|
|
119
|
+
* }
|
|
120
|
+
* ```
|
|
121
|
+
*/
|
|
122
|
+
export declare function useAccountPicker(auth: MulguardInstance): UseAccountPickerReturn;
|