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.
Files changed (66) hide show
  1. package/README.md +368 -0
  2. package/dist/actions-CExpv_dD.js +1 -0
  3. package/dist/actions-DeCfLtHA.mjs +184 -0
  4. package/dist/client/hooks.d.ts +122 -0
  5. package/dist/client/index.d.ts +5 -0
  6. package/dist/client/index.js +1 -0
  7. package/dist/client/index.mjs +476 -0
  8. package/dist/client/provider.d.ts +25 -0
  9. package/dist/client/server-actions-helper.d.ts +22 -0
  10. package/dist/components/AccountPicker.d.ts +11 -0
  11. package/dist/components/OAuthButton.d.ts +11 -0
  12. package/dist/components/PassKeyButton.d.ts +11 -0
  13. package/dist/components/PassKeyRegister.d.ts +10 -0
  14. package/dist/components/TwoFactorSetup.d.ts +8 -0
  15. package/dist/components/TwoFactorVerify.d.ts +9 -0
  16. package/dist/core/account-picker/encryption.d.ts +22 -0
  17. package/dist/core/account-picker/index.d.ts +22 -0
  18. package/dist/core/auth/index.d.ts +40 -0
  19. package/dist/core/auth/oauth-providers.d.ts +69 -0
  20. package/dist/core/auth/oauth-state-store.d.ts +44 -0
  21. package/dist/core/auth/oauth.d.ts +20 -0
  22. package/dist/core/auth/passkey.d.ts +35 -0
  23. package/dist/core/auth/password.d.ts +22 -0
  24. package/dist/core/auth/signin-unified.d.ts +33 -0
  25. package/dist/core/auth/two-factor.d.ts +28 -0
  26. package/dist/core/client/index.d.ts +132 -0
  27. package/dist/core/client/token-refresh-manager.d.ts +48 -0
  28. package/dist/core/index.d.ts +10 -0
  29. package/dist/core/security/csrf.d.ts +46 -0
  30. package/dist/core/security/headers.d.ts +24 -0
  31. package/dist/core/security/index.d.ts +28 -0
  32. package/dist/core/security/rate-limit.d.ts +39 -0
  33. package/dist/core/security/validation.d.ts +53 -0
  34. package/dist/core/security/xss.d.ts +20 -0
  35. package/dist/core/session/index.d.ts +35 -0
  36. package/dist/core/types/auth.d.ts +131 -0
  37. package/dist/core/types/errors.d.ts +44 -0
  38. package/dist/core/types/index.d.ts +369 -0
  39. package/dist/core/utils/auth-helpers.d.ts +136 -0
  40. package/dist/core/utils/logger.d.ts +17 -0
  41. package/dist/handlers/api.d.ts +10 -0
  42. package/dist/handlers/route.d.ts +22 -0
  43. package/dist/index/index.js +1 -0
  44. package/dist/index/index.mjs +1633 -0
  45. package/dist/index.d.ts +21 -0
  46. package/dist/middleware/index.d.ts +28 -0
  47. package/dist/middleware/proxy.d.ts +53 -0
  48. package/dist/middleware/security.d.ts +9 -0
  49. package/dist/mulguard.d.ts +263 -0
  50. package/dist/oauth-state-CzIWQq3s.js +1 -0
  51. package/dist/oauth-state-LE-qeq-K.mjs +282 -0
  52. package/dist/server/actions.d.ts +86 -0
  53. package/dist/server/auth.d.ts +65 -0
  54. package/dist/server/cookies.d.ts +42 -0
  55. package/dist/server/helpers.d.ts +10 -0
  56. package/dist/server/index.d.ts +14 -0
  57. package/dist/server/index.js +1 -0
  58. package/dist/server/index.mjs +31 -0
  59. package/dist/server/middleware.d.ts +39 -0
  60. package/dist/server/oauth-state.d.ts +24 -0
  61. package/dist/server/session-helpers.d.ts +26 -0
  62. package/dist/server/session.d.ts +28 -0
  63. package/dist/server/utils.d.ts +10 -0
  64. package/dist/signin-unified-BS2gxaG1.mjs +30 -0
  65. package/dist/signin-unified-Cw41EFkc.js +1 -0
  66. package/package.json +73 -0
@@ -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
+ };