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/dist/index.d.ts
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* mulguard
|
|
3
|
+
* Modern authentication library for Next.js
|
|
4
|
+
*
|
|
5
|
+
* This is the **server-safe** entrypoint. It must NOT import React
|
|
6
|
+
* or any client-only hooks/components, otherwise Next.js will treat
|
|
7
|
+
* anything that imports `mulguard` as a Client Component.
|
|
8
|
+
*
|
|
9
|
+
* - Server / shared functionality is exported from here.
|
|
10
|
+
* - React hooks & components are exposed via subpath exports:
|
|
11
|
+
* - `mulguard/client` for hooks & provider
|
|
12
|
+
* - (future) `mulguard/components/*` for UI components
|
|
13
|
+
*/
|
|
14
|
+
export * from './core';
|
|
15
|
+
export * from './mulguard';
|
|
16
|
+
export * from './server';
|
|
17
|
+
export * from './handlers/route';
|
|
18
|
+
export * from './handlers/api';
|
|
19
|
+
export * from './middleware';
|
|
20
|
+
export { createProxyMiddleware, checkRole as checkRoleProxy, } from './middleware/proxy';
|
|
21
|
+
export * from './middleware/security';
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { NextResponse, NextRequest } from 'next/server';
|
|
2
|
+
import { MulguardInstance } from '../mulguard';
|
|
3
|
+
export interface AuthMiddlewareConfig {
|
|
4
|
+
/**
|
|
5
|
+
* Protected routes - require authentication
|
|
6
|
+
*/
|
|
7
|
+
protectedRoutes?: string[];
|
|
8
|
+
/**
|
|
9
|
+
* Public routes - accessible without authentication
|
|
10
|
+
*/
|
|
11
|
+
publicRoutes?: string[];
|
|
12
|
+
/**
|
|
13
|
+
* Redirect to login if not authenticated
|
|
14
|
+
*/
|
|
15
|
+
redirectTo?: string;
|
|
16
|
+
/**
|
|
17
|
+
* Redirect to home if authenticated (for login/register pages)
|
|
18
|
+
*/
|
|
19
|
+
redirectIfAuthenticated?: string;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Create authentication middleware
|
|
23
|
+
*/
|
|
24
|
+
export declare function createAuthMiddleware(auth: MulguardInstance, config?: AuthMiddlewareConfig): (request: NextRequest) => Promise<NextResponse<unknown>>;
|
|
25
|
+
/**
|
|
26
|
+
* Helper to check if user has required role
|
|
27
|
+
*/
|
|
28
|
+
export declare function checkRole(auth: MulguardInstance, requiredRole: string): Promise<boolean>;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { NextResponse, NextRequest } from 'next/server';
|
|
2
|
+
import { MulguardInstance } from '../mulguard';
|
|
3
|
+
export interface ProxyMiddlewareConfig {
|
|
4
|
+
/**
|
|
5
|
+
* Auth instance
|
|
6
|
+
*/
|
|
7
|
+
auth: MulguardInstance;
|
|
8
|
+
/**
|
|
9
|
+
* Protected routes - require authentication
|
|
10
|
+
*/
|
|
11
|
+
protectedRoutes?: string[];
|
|
12
|
+
/**
|
|
13
|
+
* Public routes - accessible without authentication
|
|
14
|
+
*/
|
|
15
|
+
publicRoutes?: string[];
|
|
16
|
+
/**
|
|
17
|
+
* Redirect to login if not authenticated
|
|
18
|
+
*/
|
|
19
|
+
redirectTo?: string;
|
|
20
|
+
/**
|
|
21
|
+
* Redirect to home if authenticated (for login/register pages)
|
|
22
|
+
*/
|
|
23
|
+
redirectIfAuthenticated?: string;
|
|
24
|
+
/**
|
|
25
|
+
* API routes prefix (default: '/api/auth')
|
|
26
|
+
*/
|
|
27
|
+
apiPrefix?: string;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Create proxy middleware for authentication
|
|
31
|
+
*
|
|
32
|
+
* @example
|
|
33
|
+
* ```typescript
|
|
34
|
+
* // middleware.ts
|
|
35
|
+
* import { auth } from '@/auth'
|
|
36
|
+
* import { createProxyMiddleware } from 'mulguard/middleware/proxy'
|
|
37
|
+
*
|
|
38
|
+
* export default createProxyMiddleware({
|
|
39
|
+
* auth,
|
|
40
|
+
* protectedRoutes: ['/dashboard', '/profile'],
|
|
41
|
+
* redirectTo: '/login',
|
|
42
|
+
* })
|
|
43
|
+
*
|
|
44
|
+
* export const config = {
|
|
45
|
+
* matcher: ['/((?!_next/static|_next/image|favicon.ico).*)'],
|
|
46
|
+
* }
|
|
47
|
+
* ```
|
|
48
|
+
*/
|
|
49
|
+
export declare function createProxyMiddleware(config: ProxyMiddlewareConfig): (request: NextRequest) => Promise<NextResponse<unknown>>;
|
|
50
|
+
/**
|
|
51
|
+
* Helper to check if user has required role
|
|
52
|
+
*/
|
|
53
|
+
export declare function checkRole(auth: MulguardInstance, requiredRole: string): Promise<boolean>;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { NextResponse, NextRequest } from 'next/server';
|
|
2
|
+
/**
|
|
3
|
+
* Apply security headers to response
|
|
4
|
+
*/
|
|
5
|
+
export declare function withSecurityHeaders(_request: NextRequest, response: NextResponse): NextResponse;
|
|
6
|
+
/**
|
|
7
|
+
* Security middleware wrapper
|
|
8
|
+
*/
|
|
9
|
+
export declare function createSecurityMiddleware(): (request: NextRequest) => Promise<NextResponse>;
|
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
import { MulguardConfig, Session, AuthResult, EmailCredentials, RegisterData, Verify2FAData, User, SessionConfig } from './core/types';
|
|
2
|
+
/**
|
|
3
|
+
* Main Mulguard authentication instance interface
|
|
4
|
+
*
|
|
5
|
+
* Provides all authentication methods using Server Actions
|
|
6
|
+
* User implements custom logic in the config
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* ```typescript
|
|
10
|
+
* const auth = mulguard({
|
|
11
|
+
* actions: {
|
|
12
|
+
* signIn: {
|
|
13
|
+
* email: async (credentials) => {
|
|
14
|
+
* // Your custom sign in logic
|
|
15
|
+
* const user = await db.user.findUnique({ where: { email: credentials.email } })
|
|
16
|
+
* if (!user || !await bcrypt.compare(credentials.password, user.password)) {
|
|
17
|
+
* return { success: false, error: 'Invalid credentials' }
|
|
18
|
+
* }
|
|
19
|
+
* return { success: true, user, session: { user, expiresAt: new Date() } }
|
|
20
|
+
* }
|
|
21
|
+
* }
|
|
22
|
+
* }
|
|
23
|
+
* })
|
|
24
|
+
* ```
|
|
25
|
+
*/
|
|
26
|
+
export interface MulguardInstance {
|
|
27
|
+
/**
|
|
28
|
+
* Get current session
|
|
29
|
+
* Uses custom getSession action if provided, otherwise falls back to reading from cookie
|
|
30
|
+
*/
|
|
31
|
+
getSession(): Promise<Session | null>;
|
|
32
|
+
/**
|
|
33
|
+
* Get access token from current session
|
|
34
|
+
*/
|
|
35
|
+
getAccessToken(): Promise<string | null>;
|
|
36
|
+
/**
|
|
37
|
+
* Get refresh token from current session
|
|
38
|
+
*/
|
|
39
|
+
getRefreshToken(): Promise<string | null>;
|
|
40
|
+
/**
|
|
41
|
+
* Check if session has valid tokens
|
|
42
|
+
*/
|
|
43
|
+
hasValidTokens(): Promise<boolean>;
|
|
44
|
+
/**
|
|
45
|
+
* Set session directly (useful for Server Actions)
|
|
46
|
+
* Saves session to cookie automatically
|
|
47
|
+
*
|
|
48
|
+
* @param session - Session object to save
|
|
49
|
+
* @returns Result indicating success or failure
|
|
50
|
+
*
|
|
51
|
+
* @example
|
|
52
|
+
* ```typescript
|
|
53
|
+
* const result = await auth.setSession(newSession)
|
|
54
|
+
* if (!result.success) {
|
|
55
|
+
* console.warn('Failed to save session:', result.error)
|
|
56
|
+
* }
|
|
57
|
+
* ```
|
|
58
|
+
*/
|
|
59
|
+
setSession(session: Session): Promise<{
|
|
60
|
+
success: boolean;
|
|
61
|
+
error?: string;
|
|
62
|
+
warning?: string;
|
|
63
|
+
}>;
|
|
64
|
+
/**
|
|
65
|
+
* Internal method to get session config for Server Actions
|
|
66
|
+
* Used by verify2FAAction to save session cookie directly
|
|
67
|
+
* @internal
|
|
68
|
+
*/
|
|
69
|
+
_getSessionConfig(): {
|
|
70
|
+
cookieName: string;
|
|
71
|
+
config: SessionConfig;
|
|
72
|
+
};
|
|
73
|
+
/**
|
|
74
|
+
* Internal method to get callbacks for Server Actions
|
|
75
|
+
* Used by Server Actions to execute callbacks
|
|
76
|
+
* @internal
|
|
77
|
+
*/
|
|
78
|
+
_getCallbacks?(): import('./core/types').CallbacksConfig;
|
|
79
|
+
/**
|
|
80
|
+
* Internal method to get token refresh manager
|
|
81
|
+
* Used by HttpClient for automatic token refresh
|
|
82
|
+
* @internal
|
|
83
|
+
*/
|
|
84
|
+
_getTokenRefreshManager?(): import('./core/client/token-refresh-manager').TokenRefreshManager | undefined;
|
|
85
|
+
/**
|
|
86
|
+
* Sign in methods - uses custom actions from config
|
|
87
|
+
*/
|
|
88
|
+
signIn: {
|
|
89
|
+
/**
|
|
90
|
+
* Sign in with email and password
|
|
91
|
+
* Executes custom email action from config
|
|
92
|
+
*/
|
|
93
|
+
email(credentials: EmailCredentials): Promise<AuthResult>;
|
|
94
|
+
/**
|
|
95
|
+
* Initiate OAuth sign in flow
|
|
96
|
+
* Executes custom oauth action from config
|
|
97
|
+
*/
|
|
98
|
+
oauth?(provider: string): Promise<{
|
|
99
|
+
url: string;
|
|
100
|
+
state: string;
|
|
101
|
+
}>;
|
|
102
|
+
/**
|
|
103
|
+
* Sign in with PassKey/WebAuthn
|
|
104
|
+
* Executes custom passkey action from config
|
|
105
|
+
*/
|
|
106
|
+
passkey?(options?: {
|
|
107
|
+
userId?: string;
|
|
108
|
+
}): Promise<AuthResult>;
|
|
109
|
+
/**
|
|
110
|
+
* Sign in with OTP
|
|
111
|
+
* Executes custom otp action from config
|
|
112
|
+
*/
|
|
113
|
+
otp?(email: string, code?: string): Promise<AuthResult>;
|
|
114
|
+
};
|
|
115
|
+
/**
|
|
116
|
+
* Sign in methods - uses custom actions from config (legacy interface)
|
|
117
|
+
*/
|
|
118
|
+
signInMethods: {
|
|
119
|
+
/**
|
|
120
|
+
* Sign in with email and password
|
|
121
|
+
* Executes custom email action from config
|
|
122
|
+
*/
|
|
123
|
+
email(credentials: EmailCredentials): Promise<AuthResult>;
|
|
124
|
+
/**
|
|
125
|
+
* Initiate OAuth sign in flow
|
|
126
|
+
* Executes custom oauth action from config
|
|
127
|
+
*/
|
|
128
|
+
oauth?(provider: string): Promise<{
|
|
129
|
+
url: string;
|
|
130
|
+
state: string;
|
|
131
|
+
}>;
|
|
132
|
+
/**
|
|
133
|
+
* Sign in with PassKey/WebAuthn
|
|
134
|
+
* Executes custom passkey action from config
|
|
135
|
+
*/
|
|
136
|
+
passkey?(options?: {
|
|
137
|
+
userId?: string;
|
|
138
|
+
}): Promise<AuthResult>;
|
|
139
|
+
/**
|
|
140
|
+
* Sign in with OTP
|
|
141
|
+
* Executes custom otp action from config
|
|
142
|
+
*/
|
|
143
|
+
otp?(email: string, code?: string): Promise<AuthResult>;
|
|
144
|
+
};
|
|
145
|
+
/**
|
|
146
|
+
* Register new user
|
|
147
|
+
* Executes custom signUp action from config
|
|
148
|
+
*/
|
|
149
|
+
signUp?(data: RegisterData): Promise<AuthResult>;
|
|
150
|
+
/**
|
|
151
|
+
* Sign out current session
|
|
152
|
+
* Executes custom signOut action from config
|
|
153
|
+
*/
|
|
154
|
+
signOut(): Promise<{
|
|
155
|
+
success: boolean;
|
|
156
|
+
error?: string;
|
|
157
|
+
}>;
|
|
158
|
+
/**
|
|
159
|
+
* Request password reset
|
|
160
|
+
* Executes custom resetPassword action from config
|
|
161
|
+
*/
|
|
162
|
+
resetPassword?(email: string): Promise<{
|
|
163
|
+
success: boolean;
|
|
164
|
+
error?: string;
|
|
165
|
+
}>;
|
|
166
|
+
/**
|
|
167
|
+
* Verify email address with token
|
|
168
|
+
* Executes custom verifyEmail action from config
|
|
169
|
+
*/
|
|
170
|
+
verifyEmail?(token: string): Promise<{
|
|
171
|
+
success: boolean;
|
|
172
|
+
error?: string;
|
|
173
|
+
}>;
|
|
174
|
+
/**
|
|
175
|
+
* Refresh session
|
|
176
|
+
* Executes custom refreshSession action from config with improved error handling
|
|
177
|
+
*/
|
|
178
|
+
refreshSession?(): Promise<Session | null>;
|
|
179
|
+
/**
|
|
180
|
+
* OAuth callback handler
|
|
181
|
+
* Executes custom oauthCallback action from config
|
|
182
|
+
*/
|
|
183
|
+
oauthCallback?(provider: string, code: string, state: string): Promise<AuthResult>;
|
|
184
|
+
/**
|
|
185
|
+
* PassKey methods
|
|
186
|
+
*/
|
|
187
|
+
passkey?: {
|
|
188
|
+
register?(options?: {
|
|
189
|
+
name?: string;
|
|
190
|
+
userId?: string;
|
|
191
|
+
}): Promise<{
|
|
192
|
+
success: boolean;
|
|
193
|
+
passkeyId?: string;
|
|
194
|
+
error?: string;
|
|
195
|
+
}>;
|
|
196
|
+
authenticate?(options?: {
|
|
197
|
+
userId?: string;
|
|
198
|
+
}): Promise<AuthResult>;
|
|
199
|
+
list?(): Promise<Array<{
|
|
200
|
+
id: string;
|
|
201
|
+
name: string;
|
|
202
|
+
createdAt: Date;
|
|
203
|
+
lastUsedAt?: Date;
|
|
204
|
+
}>>;
|
|
205
|
+
remove?(passKeyId: string): Promise<{
|
|
206
|
+
success: boolean;
|
|
207
|
+
error?: string;
|
|
208
|
+
}>;
|
|
209
|
+
};
|
|
210
|
+
/**
|
|
211
|
+
* Two-Factor Authentication methods
|
|
212
|
+
*/
|
|
213
|
+
twoFactor?: {
|
|
214
|
+
enable?(): Promise<{
|
|
215
|
+
success: boolean;
|
|
216
|
+
qrCode?: string;
|
|
217
|
+
secret?: string;
|
|
218
|
+
error?: string;
|
|
219
|
+
}>;
|
|
220
|
+
verify?(code: string): Promise<{
|
|
221
|
+
success: boolean;
|
|
222
|
+
backupCodes?: string[];
|
|
223
|
+
error?: string;
|
|
224
|
+
}>;
|
|
225
|
+
disable?(): Promise<{
|
|
226
|
+
success: boolean;
|
|
227
|
+
error?: string;
|
|
228
|
+
}>;
|
|
229
|
+
generateBackupCodes?(): Promise<{
|
|
230
|
+
success: boolean;
|
|
231
|
+
backupCodes?: string[];
|
|
232
|
+
error?: string;
|
|
233
|
+
}>;
|
|
234
|
+
isEnabled?(): Promise<boolean>;
|
|
235
|
+
/**
|
|
236
|
+
* Verify 2FA code after initial sign in
|
|
237
|
+
* Used when signIn returns requires2FA: true
|
|
238
|
+
*/
|
|
239
|
+
verify2FA?(data: Verify2FAData): Promise<AuthResult>;
|
|
240
|
+
};
|
|
241
|
+
/**
|
|
242
|
+
* Verify 2FA code after initial sign in
|
|
243
|
+
* Used when signIn returns requires2FA: true
|
|
244
|
+
*
|
|
245
|
+
* @param data - 2FA verification data
|
|
246
|
+
* @param options - Optional configuration
|
|
247
|
+
* @param options.skipCookieSave - If true, skip automatic cookie saving (useful for Server Actions)
|
|
248
|
+
*/
|
|
249
|
+
verify2FA?(data: Verify2FAData, options?: {
|
|
250
|
+
skipCookieSave?: boolean;
|
|
251
|
+
}): Promise<AuthResult>;
|
|
252
|
+
/**
|
|
253
|
+
* Account Picker methods (optional)
|
|
254
|
+
* For remembering last logged-in users
|
|
255
|
+
*/
|
|
256
|
+
accountPicker?: {
|
|
257
|
+
getLastUsers(): Promise<import('./core/types').RememberedUser[]>;
|
|
258
|
+
rememberUser(user: User, provider?: 'email' | 'oauth' | 'passkey'): Promise<void>;
|
|
259
|
+
clearUser(userId: string): Promise<void>;
|
|
260
|
+
clearAll(): Promise<void>;
|
|
261
|
+
};
|
|
262
|
+
}
|
|
263
|
+
export declare function mulguard(config: MulguardConfig): MulguardInstance;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";const f=require("./actions-CExpv_dD.js"),C=require("next/navigation"),A=require("next/server");function h(e){return!e||!e.expiresAt?!1:new Date(e.expiresAt)<new Date}function v(e,t=5){if(!e||!e.expiresAt)return!1;const r=new Date(e.expiresAt),s=new Date,i=(r.getTime()-s.getTime())/(1e3*60);return i>0&&i<t}function E(e){if(!e||!e.expiresAt)return null;const t=new Date(e.expiresAt),r=new Date,s=(t.getTime()-r.getTime())/(1e3*60);return s>0?Math.floor(s):0}function _(e){return!(!e||!e.user||!e.user.id||!e.user.email||!e.user.name||h(e))}function w(e){if(!e||typeof e!="object")return!1;const t=e;if(!t.user||typeof t.user!="object")return!1;const r=t.user;if(typeof r.id!="string"||r.id.length===0||typeof r.email!="string"||r.email.length===0||typeof r.name!="string"||r.name.length===0||!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(r.email))return!1;if(t.expiresAt)if(t.expiresAt instanceof Date){if(isNaN(t.expiresAt.getTime()))return!1}else if(typeof t.expiresAt=="string"){const i=new Date(t.expiresAt);if(isNaN(i.getTime()))return!1}else return!1;return!0}function D(e,t){const r=t.cookieName||"__mulguard_session";let s=null,i=0;const g=6e4;return{async getSession(n){try{if(!await f.getCookie(r))return s=null,null;const a=Date.now();if(s&&a-i<g){if(!(n!=null&&n.skipRefresh)&&this.shouldRefreshSession(s,t)){const o=await this.refreshSession();if(o)return s=o,i=a,o}return s}const c=await e.get("/api/auth/session");if(!c.data.session)return s=null,null;const l=c.data.session;if(!w(l))return await f.deleteCookie(r),s=null,null;if(h(l)){if(!(n!=null&&n.skipRefresh)&&this.shouldRefreshSession(l,t)){const o=await this.refreshSession();if(o)return s=o,i=a,o}return await f.deleteCookie(r),s=null,null}if(!(n!=null&&n.skipRefresh)&&this.shouldRefreshSession(l,t)){const o=await this.refreshSession();if(o)return s=o,i=a,o}return s=l,i=a,l}catch{return await f.deleteCookie(r),s=null,null}},setSession(n,u){},async clearSession(n){await f.deleteCookie(r,{path:n.path}),s=null,i=0},async refreshSession(){try{const n=await e.post("/api/auth/refresh");if(!n.data.session)return s=null,null;const u=n.data.session;return w(u)?(s=u,i=Date.now(),u):(s=null,null)}catch{return s=null,null}},isSessionExpired(n){return h(n)},shouldRefreshSession(n,u){if(!n.expiresAt)return!1;const a=new Date(n.expiresAt),c=new Date,l=a.getTime()-c.getTime(),o=5*60*1e3;return l>0&&l<o}}}async function N(e,t){try{const r=await e.post("/api/auth/refresh");return r.data.session?r.data.session:null}catch{return null}}async function d(e){try{const t=await e.getSession();return!t||!w(t)||h(t)?null:t}catch(t){return console.error("Failed to get server session:",t),null}}async function S(e,t="/login"){const r=await d(e);return r||C.redirect(t),r}async function m(e,t,r="/unauthorized"){const s=await S(e);return(!s.user.roles||!s.user.roles.includes(t))&&C.redirect(r),s}async function k(e){const t=await d(e);return(t==null?void 0:t.user)??null}function q(e,t){return{getSession:()=>d(e),requireAuth:r=>S(e,r),requireRole:(r,s)=>m(e,r,s)}}function O(e){return{getSession:()=>d(e),requireAuth:t=>S(e,t),requireRole:(t,r)=>m(e,t,r),getCurrentUser:()=>k(e)}}function y(e,t=[]){return[...["/auth/login","/auth/register","/auth/forgot-password","/auth/reset-password","/auth/verify-email"],...t].some(i=>e.startsWith(i))}function x(e,t={}){const{redirectTo:r="/auth/login",requireAuth:s=!1,allowedRoles:i=[],publicRoutes:g=[]}=t;return async n=>{const{pathname:u}=n.nextUrl;if(y(u,g)||u.startsWith("/api/")||u.startsWith("/_next/")||u.startsWith("/favicon.ico")||u.match(/\.(ico|png|jpg|jpeg|svg|gif|webp|css|js|woff|woff2|ttf|eot)$/))return null;try{const a=await e.getSession();if(s&&!a){const c=new URL(r,n.url);return c.searchParams.set("redirect",u),A.NextResponse.redirect(c)}if(a&&i.length>0){const c=a.user.roles||[];if(!i.some(o=>c.includes(o)))return A.NextResponse.redirect(new URL("/unauthorized",n.url))}return a&&y(u,g)?A.NextResponse.redirect(new URL("/",n.url)):null}catch(a){return process.env.NODE_ENV==="development"&&console.error("[Mulguard Middleware] Error:",a),null}}}function U(e,t="/auth/login"){return x(e,{requireAuth:!0,redirectTo:t})}function M(e,t,r="/unauthorized"){return x(e,{requireAuth:!0,allowedRoles:t,redirectTo:r})}const R="__mulguard_oauth_state",T=10*60;async function b(e,t){try{const r=JSON.stringify({state:e,provider:t,expiresAt:Date.now()+T*1e3}),s=process.env.NODE_ENV==="production";return await f.setCookie({name:R,value:r,httpOnly:!0,secure:s,sameSite:"strict",maxAge:T,path:"/"})}catch(r){return{success:!1,error:r instanceof Error?r.message:"Failed to store OAuth state"}}}async function P(){try{const e=await f.getCookie(R);if(!e)return null;const t=JSON.parse(e);return t.expiresAt<Date.now()?(await p(),null):(await p(),{state:t.state,provider:t.provider})}catch{return await p(),null}}async function p(){await f.deleteCookie(R,{path:"/"})}exports.createAuthMiddleware=x;exports.createServerHelpers=q;exports.createServerUtils=O;exports.createSessionManager=D;exports.deleteOAuthStateCookie=p;exports.getCurrentUser=k;exports.getOAuthStateCookie=P;exports.getServerSession=d;exports.getSessionTimeUntilExpiry=E;exports.isSessionExpiredNullable=h;exports.isSessionExpiringSoon=v;exports.isSessionValid=_;exports.refreshSession=N;exports.requireAuth=S;exports.requireAuthMiddleware=U;exports.requireRole=m;exports.requireRoleMiddleware=M;exports.storeOAuthStateCookie=b;exports.validateSessionStructure=w;
|
|
@@ -0,0 +1,282 @@
|
|
|
1
|
+
import { d as f, g as R, c as E } from "./actions-DeCfLtHA.mjs";
|
|
2
|
+
import { redirect as T } from "next/navigation";
|
|
3
|
+
import { NextResponse as g } from "next/server";
|
|
4
|
+
function d(e) {
|
|
5
|
+
return !e || !e.expiresAt ? !1 : new Date(e.expiresAt) < /* @__PURE__ */ new Date();
|
|
6
|
+
}
|
|
7
|
+
function O(e, r = 5) {
|
|
8
|
+
if (!e || !e.expiresAt)
|
|
9
|
+
return !1;
|
|
10
|
+
const t = new Date(e.expiresAt), s = /* @__PURE__ */ new Date(), i = (t.getTime() - s.getTime()) / (1e3 * 60);
|
|
11
|
+
return i > 0 && i < r;
|
|
12
|
+
}
|
|
13
|
+
function U(e) {
|
|
14
|
+
if (!e || !e.expiresAt)
|
|
15
|
+
return null;
|
|
16
|
+
const r = new Date(e.expiresAt), t = /* @__PURE__ */ new Date(), s = (r.getTime() - t.getTime()) / (1e3 * 60);
|
|
17
|
+
return s > 0 ? Math.floor(s) : 0;
|
|
18
|
+
}
|
|
19
|
+
function b(e) {
|
|
20
|
+
return !(!e || !e.user || !e.user.id || !e.user.email || !e.user.name || d(e));
|
|
21
|
+
}
|
|
22
|
+
function m(e) {
|
|
23
|
+
if (!e || typeof e != "object")
|
|
24
|
+
return !1;
|
|
25
|
+
const r = e;
|
|
26
|
+
if (!r.user || typeof r.user != "object")
|
|
27
|
+
return !1;
|
|
28
|
+
const t = r.user;
|
|
29
|
+
if (typeof t.id != "string" || t.id.length === 0 || typeof t.email != "string" || t.email.length === 0 || typeof t.name != "string" || t.name.length === 0 || !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(t.email))
|
|
30
|
+
return !1;
|
|
31
|
+
if (r.expiresAt)
|
|
32
|
+
if (r.expiresAt instanceof Date) {
|
|
33
|
+
if (isNaN(r.expiresAt.getTime()))
|
|
34
|
+
return !1;
|
|
35
|
+
} else if (typeof r.expiresAt == "string") {
|
|
36
|
+
const i = new Date(r.expiresAt);
|
|
37
|
+
if (isNaN(i.getTime()))
|
|
38
|
+
return !1;
|
|
39
|
+
} else
|
|
40
|
+
return !1;
|
|
41
|
+
return !0;
|
|
42
|
+
}
|
|
43
|
+
function q(e, r) {
|
|
44
|
+
const t = r.cookieName || "__mulguard_session";
|
|
45
|
+
let s = null, i = 0;
|
|
46
|
+
const h = 6e4;
|
|
47
|
+
return {
|
|
48
|
+
/**
|
|
49
|
+
* Get current session from backend with automatic refresh
|
|
50
|
+
*/
|
|
51
|
+
async getSession(n) {
|
|
52
|
+
try {
|
|
53
|
+
if (!await R(t))
|
|
54
|
+
return s = null, null;
|
|
55
|
+
const o = Date.now();
|
|
56
|
+
if (s && o - i < h) {
|
|
57
|
+
if (!(n != null && n.skipRefresh) && this.shouldRefreshSession(s, r)) {
|
|
58
|
+
const u = await this.refreshSession();
|
|
59
|
+
if (u)
|
|
60
|
+
return s = u, i = o, u;
|
|
61
|
+
}
|
|
62
|
+
return s;
|
|
63
|
+
}
|
|
64
|
+
const c = await e.get("/api/auth/session");
|
|
65
|
+
if (!c.data.session)
|
|
66
|
+
return s = null, null;
|
|
67
|
+
const l = c.data.session;
|
|
68
|
+
if (!m(l))
|
|
69
|
+
return await f(t), s = null, null;
|
|
70
|
+
if (d(l)) {
|
|
71
|
+
if (!(n != null && n.skipRefresh) && this.shouldRefreshSession(l, r)) {
|
|
72
|
+
const u = await this.refreshSession();
|
|
73
|
+
if (u)
|
|
74
|
+
return s = u, i = o, u;
|
|
75
|
+
}
|
|
76
|
+
return await f(t), s = null, null;
|
|
77
|
+
}
|
|
78
|
+
if (!(n != null && n.skipRefresh) && this.shouldRefreshSession(l, r)) {
|
|
79
|
+
const u = await this.refreshSession();
|
|
80
|
+
if (u)
|
|
81
|
+
return s = u, i = o, u;
|
|
82
|
+
}
|
|
83
|
+
return s = l, i = o, l;
|
|
84
|
+
} catch {
|
|
85
|
+
return await f(t), s = null, null;
|
|
86
|
+
}
|
|
87
|
+
},
|
|
88
|
+
/**
|
|
89
|
+
* Set session cookie
|
|
90
|
+
*/
|
|
91
|
+
setSession(n, a) {
|
|
92
|
+
},
|
|
93
|
+
/**
|
|
94
|
+
* Clear session
|
|
95
|
+
*/
|
|
96
|
+
async clearSession(n) {
|
|
97
|
+
await f(t, {
|
|
98
|
+
path: n.path
|
|
99
|
+
}), s = null, i = 0;
|
|
100
|
+
},
|
|
101
|
+
/**
|
|
102
|
+
* Refresh session
|
|
103
|
+
*/
|
|
104
|
+
async refreshSession() {
|
|
105
|
+
try {
|
|
106
|
+
const n = await e.post("/api/auth/refresh");
|
|
107
|
+
if (!n.data.session)
|
|
108
|
+
return s = null, null;
|
|
109
|
+
const a = n.data.session;
|
|
110
|
+
return m(a) ? (s = a, i = Date.now(), a) : (s = null, null);
|
|
111
|
+
} catch {
|
|
112
|
+
return s = null, null;
|
|
113
|
+
}
|
|
114
|
+
},
|
|
115
|
+
/**
|
|
116
|
+
* Check if session is expired
|
|
117
|
+
*/
|
|
118
|
+
isSessionExpired(n) {
|
|
119
|
+
return d(n);
|
|
120
|
+
},
|
|
121
|
+
/**
|
|
122
|
+
* Check if session should be refreshed (within 5 minutes of expiration)
|
|
123
|
+
*/
|
|
124
|
+
shouldRefreshSession(n, a) {
|
|
125
|
+
if (!n.expiresAt)
|
|
126
|
+
return !1;
|
|
127
|
+
const o = new Date(n.expiresAt), c = /* @__PURE__ */ new Date(), l = o.getTime() - c.getTime(), u = 5 * 60 * 1e3;
|
|
128
|
+
return l > 0 && l < u;
|
|
129
|
+
}
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
async function M(e, r) {
|
|
133
|
+
try {
|
|
134
|
+
const t = await e.post("/api/auth/refresh");
|
|
135
|
+
return t.data.session ? t.data.session : null;
|
|
136
|
+
} catch {
|
|
137
|
+
return null;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
async function p(e) {
|
|
141
|
+
try {
|
|
142
|
+
const r = await e.getSession();
|
|
143
|
+
return !r || !m(r) || d(r) ? null : r;
|
|
144
|
+
} catch (r) {
|
|
145
|
+
return console.error("Failed to get server session:", r), null;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
async function A(e, r = "/login") {
|
|
149
|
+
const t = await p(e);
|
|
150
|
+
return t || T(r), t;
|
|
151
|
+
}
|
|
152
|
+
async function _(e, r, t = "/unauthorized") {
|
|
153
|
+
const s = await A(e);
|
|
154
|
+
return (!s.user.roles || !s.user.roles.includes(r)) && T(t), s;
|
|
155
|
+
}
|
|
156
|
+
async function k(e) {
|
|
157
|
+
const r = await p(e);
|
|
158
|
+
return (r == null ? void 0 : r.user) ?? null;
|
|
159
|
+
}
|
|
160
|
+
function P(e, r) {
|
|
161
|
+
return {
|
|
162
|
+
getSession: () => p(e),
|
|
163
|
+
requireAuth: (t) => A(e, t),
|
|
164
|
+
requireRole: (t, s) => _(e, t, s)
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
function j(e) {
|
|
168
|
+
return {
|
|
169
|
+
getSession: () => p(e),
|
|
170
|
+
requireAuth: (r) => A(e, r),
|
|
171
|
+
requireRole: (r, t) => _(e, r, t),
|
|
172
|
+
getCurrentUser: () => k(e)
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
function x(e, r = []) {
|
|
176
|
+
return [...[
|
|
177
|
+
"/auth/login",
|
|
178
|
+
"/auth/register",
|
|
179
|
+
"/auth/forgot-password",
|
|
180
|
+
"/auth/reset-password",
|
|
181
|
+
"/auth/verify-email"
|
|
182
|
+
], ...r].some((i) => e.startsWith(i));
|
|
183
|
+
}
|
|
184
|
+
function D(e, r = {}) {
|
|
185
|
+
const {
|
|
186
|
+
redirectTo: t = "/auth/login",
|
|
187
|
+
requireAuth: s = !1,
|
|
188
|
+
allowedRoles: i = [],
|
|
189
|
+
publicRoutes: h = []
|
|
190
|
+
} = r;
|
|
191
|
+
return async (n) => {
|
|
192
|
+
const { pathname: a } = n.nextUrl;
|
|
193
|
+
if (x(a, h) || a.startsWith("/api/") || a.startsWith("/_next/") || a.startsWith("/favicon.ico") || a.match(/\.(ico|png|jpg|jpeg|svg|gif|webp|css|js|woff|woff2|ttf|eot)$/))
|
|
194
|
+
return null;
|
|
195
|
+
try {
|
|
196
|
+
const o = await e.getSession();
|
|
197
|
+
if (s && !o) {
|
|
198
|
+
const c = new URL(t, n.url);
|
|
199
|
+
return c.searchParams.set("redirect", a), g.redirect(c);
|
|
200
|
+
}
|
|
201
|
+
if (o && i.length > 0) {
|
|
202
|
+
const c = o.user.roles || [];
|
|
203
|
+
if (!i.some((u) => c.includes(u)))
|
|
204
|
+
return g.redirect(new URL("/unauthorized", n.url));
|
|
205
|
+
}
|
|
206
|
+
return o && x(a, h) ? g.redirect(new URL("/", n.url)) : null;
|
|
207
|
+
} catch (o) {
|
|
208
|
+
return process.env.NODE_ENV === "development" && console.error("[Mulguard Middleware] Error:", o), null;
|
|
209
|
+
}
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
function L(e, r = "/auth/login") {
|
|
213
|
+
return D(e, {
|
|
214
|
+
requireAuth: !0,
|
|
215
|
+
redirectTo: r
|
|
216
|
+
});
|
|
217
|
+
}
|
|
218
|
+
function H(e, r, t = "/unauthorized") {
|
|
219
|
+
return D(e, {
|
|
220
|
+
requireAuth: !0,
|
|
221
|
+
allowedRoles: r,
|
|
222
|
+
redirectTo: t
|
|
223
|
+
});
|
|
224
|
+
}
|
|
225
|
+
const S = "__mulguard_oauth_state", y = 10 * 60;
|
|
226
|
+
async function V(e, r) {
|
|
227
|
+
try {
|
|
228
|
+
const t = JSON.stringify({ state: e, provider: r, expiresAt: Date.now() + y * 1e3 }), s = process.env.NODE_ENV === "production";
|
|
229
|
+
return await E({
|
|
230
|
+
name: S,
|
|
231
|
+
value: t,
|
|
232
|
+
httpOnly: !0,
|
|
233
|
+
secure: s,
|
|
234
|
+
sameSite: "strict",
|
|
235
|
+
maxAge: y,
|
|
236
|
+
path: "/"
|
|
237
|
+
});
|
|
238
|
+
} catch (t) {
|
|
239
|
+
return {
|
|
240
|
+
success: !1,
|
|
241
|
+
error: t instanceof Error ? t.message : "Failed to store OAuth state"
|
|
242
|
+
};
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
async function W() {
|
|
246
|
+
try {
|
|
247
|
+
const e = await R(S);
|
|
248
|
+
if (!e)
|
|
249
|
+
return null;
|
|
250
|
+
const r = JSON.parse(e);
|
|
251
|
+
return r.expiresAt < Date.now() ? (await w(), null) : (await w(), {
|
|
252
|
+
state: r.state,
|
|
253
|
+
provider: r.provider
|
|
254
|
+
});
|
|
255
|
+
} catch {
|
|
256
|
+
return await w(), null;
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
async function w() {
|
|
260
|
+
await f(S, { path: "/" });
|
|
261
|
+
}
|
|
262
|
+
export {
|
|
263
|
+
b as a,
|
|
264
|
+
d as b,
|
|
265
|
+
D as c,
|
|
266
|
+
H as d,
|
|
267
|
+
p as e,
|
|
268
|
+
A as f,
|
|
269
|
+
U as g,
|
|
270
|
+
_ as h,
|
|
271
|
+
O as i,
|
|
272
|
+
k as j,
|
|
273
|
+
j as k,
|
|
274
|
+
W as l,
|
|
275
|
+
w as m,
|
|
276
|
+
q as n,
|
|
277
|
+
M as o,
|
|
278
|
+
P as p,
|
|
279
|
+
L as r,
|
|
280
|
+
V as s,
|
|
281
|
+
m as v
|
|
282
|
+
};
|