najm-auth 1.1.29 → 1.1.31

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.
@@ -12,8 +12,22 @@ interface AuthProviderProps {
12
12
  * Pass `null` to assert "no session" (also skips the boot fetch).
13
13
  */
14
14
  initialSession?: HydrateSession | null;
15
+ /**
16
+ * Auto-refresh the access token in the background when the hydrated
17
+ * session has a user but no access token. Default: true.
18
+ * Children always render; the refresh does not gate the tree.
19
+ */
20
+ autoRefresh?: boolean;
21
+ }
22
+ declare function AuthProvider({ client, children, initialSession, autoRefresh, }: AuthProviderProps): react_jsx_runtime.JSX.Element;
23
+
24
+ interface AuthBoundaryProps {
25
+ children: ReactNode;
26
+ fallback?: ReactNode;
27
+ /** Only gate while we truly have no identity; default false. */
28
+ requireToken?: boolean;
15
29
  }
16
- declare function AuthProvider({ client, children, initialSession }: AuthProviderProps): react_jsx_runtime.JSX.Element;
30
+ declare function AuthBoundary({ children, fallback, requireToken, }: AuthBoundaryProps): react_jsx_runtime.JSX.Element;
17
31
 
18
32
  declare function useAuthClient(): NajmAuthClient;
19
33
 
@@ -523,4 +537,4 @@ interface RedirectToLoginProps {
523
537
  */
524
538
  declare function RedirectToLogin({ to, preserveFrom }: RedirectToLoginProps): any;
525
539
 
526
- export { type AuthEventEntry, AuthGate, AuthLoading, AuthProvider, Can, IfAuth, LoginButton, PermissionList, Protected, RedirectToLogin, Role, type SessionStatus, SignOutButton, SignedIn, SignedOut, UserAvatar, UserEmail, UserName, UserRole, useAuth, useAuthClient, useAuthEvent, useAuthEvents, useChangePassword, useForgotPassword, useLogin, useLogout, usePermissions, useRegister, useResetPassword, useSession, useUser };
540
+ export { AuthBoundary, type AuthEventEntry, AuthGate, AuthLoading, AuthProvider, Can, IfAuth, LoginButton, PermissionList, Protected, RedirectToLogin, Role, type SessionStatus, SignOutButton, SignedIn, SignedOut, UserAvatar, UserEmail, UserName, UserRole, useAuth, useAuthClient, useAuthEvent, useAuthEvents, useChangePassword, useForgotPassword, useLogin, useLogout, usePermissions, useRegister, useResetPassword, useSession, useUser };
@@ -3,7 +3,7 @@ var __defProp = Object.defineProperty;
3
3
  var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
4
4
 
5
5
  // src/client/react/AuthProvider.tsx
6
- import { useRef } from "react";
6
+ import { useEffect, useRef } from "react";
7
7
 
8
8
  // src/client/react/context.ts
9
9
  import { createContext, useContext } from "react";
@@ -17,18 +17,6 @@ function useAuthClient() {
17
17
  }
18
18
  __name(useAuthClient, "useAuthClient");
19
19
 
20
- // src/client/react/AuthProvider.tsx
21
- import { jsx } from "react/jsx-runtime";
22
- function AuthProvider({ client, children, initialSession }) {
23
- const hydrated = useRef(false);
24
- if (!hydrated.current && initialSession !== void 0) {
25
- client.hydrate(initialSession);
26
- hydrated.current = true;
27
- }
28
- return /* @__PURE__ */ jsx(AuthClientContext.Provider, { value: client, children });
29
- }
30
- __name(AuthProvider, "AuthProvider");
31
-
32
20
  // src/client/react/useAuth.ts
33
21
  import { useSyncExternalStore } from "react";
34
22
  function useAuth() {
@@ -41,6 +29,55 @@ function useAuth() {
41
29
  }
42
30
  __name(useAuth, "useAuth");
43
31
 
32
+ // src/client/react/AuthProvider.tsx
33
+ import { jsx, jsxs } from "react/jsx-runtime";
34
+ function AuthProvider({
35
+ client,
36
+ children,
37
+ initialSession,
38
+ autoRefresh = true
39
+ }) {
40
+ const hydrated = useRef(false);
41
+ if (!hydrated.current && initialSession !== void 0) {
42
+ client.hydrate(initialSession);
43
+ hydrated.current = true;
44
+ }
45
+ return /* @__PURE__ */ jsxs(AuthClientContext.Provider, { value: client, children: [
46
+ autoRefresh ? /* @__PURE__ */ jsx(AutoRefresh, {}) : null,
47
+ children
48
+ ] });
49
+ }
50
+ __name(AuthProvider, "AuthProvider");
51
+ function AutoRefresh() {
52
+ const { isAuthenticated, accessToken } = useAuth();
53
+ const client = useAuthClient();
54
+ const triggered = useRef(false);
55
+ useEffect(() => {
56
+ if (triggered.current) return;
57
+ if (isAuthenticated && !accessToken) {
58
+ triggered.current = true;
59
+ client.refresh().catch(() => {
60
+ });
61
+ }
62
+ }, [isAuthenticated, accessToken, client]);
63
+ return null;
64
+ }
65
+ __name(AutoRefresh, "AutoRefresh");
66
+
67
+ // src/client/react/AuthBoundary.tsx
68
+ import { Fragment, jsx as jsx2 } from "react/jsx-runtime";
69
+ function AuthBoundary({
70
+ children,
71
+ fallback = null,
72
+ requireToken = false
73
+ }) {
74
+ const { isAuthenticated, isLoading, accessToken } = useAuth();
75
+ if (isLoading) return /* @__PURE__ */ jsx2(Fragment, { children: fallback });
76
+ if (requireToken && isAuthenticated && !accessToken) return /* @__PURE__ */ jsx2(Fragment, { children: fallback });
77
+ return /* @__PURE__ */ jsx2(Fragment, { children });
78
+ }
79
+ __name(AuthBoundary, "AuthBoundary");
80
+
44
81
  // src/client/react/useUser.ts
45
82
  function useUser() {
46
83
  const { user } = useAuth();
@@ -49,13 +86,13 @@ function useUser() {
49
86
  __name(useUser, "useUser");
50
87
 
51
88
  // src/client/react/useSession.ts
52
- import { useEffect, useRef as useRef2, useState } from "react";
89
+ import { useEffect as useEffect2, useRef as useRef2, useState } from "react";
53
90
  function useSession(opts) {
54
91
  const client = useAuthClient();
55
92
  const state = useAuth();
56
93
  const [isLoading, setIsLoading] = useState(() => !client.isHydrated());
57
94
  const didInit = useRef2(false);
58
- useEffect(() => {
95
+ useEffect2(() => {
59
96
  if (didInit.current) return;
60
97
  didInit.current = true;
61
98
  if (client.isHydrated()) {
@@ -66,7 +103,7 @@ function useSession(opts) {
66
103
  }
67
104
  client.refresh().then(() => client.fetchUser()).catch((err) => opts?.onError?.(err)).finally(() => setIsLoading(false));
68
105
  }, [client]);
69
- useEffect(() => {
106
+ useEffect2(() => {
70
107
  if (isLoading) return;
71
108
  if (!opts?.required) return;
72
109
  if (state.isAuthenticated) return;
@@ -262,12 +299,12 @@ function useChangePassword(opts) {
262
299
  __name(useChangePassword, "useChangePassword");
263
300
 
264
301
  // src/client/react/useAuthEvent.ts
265
- import { useEffect as useEffect2, useRef as useRef3 } from "react";
302
+ import { useEffect as useEffect3, useRef as useRef3 } from "react";
266
303
  function useAuthEvent(event, handler) {
267
304
  const client = useAuthClient();
268
305
  const handlerRef = useRef3(handler);
269
306
  handlerRef.current = handler;
270
- useEffect2(() => {
307
+ useEffect3(() => {
271
308
  const listener = /* @__PURE__ */ __name((data) => handlerRef.current(data), "listener");
272
309
  client.on(event, listener);
273
310
  return () => client.off(event, listener);
@@ -276,7 +313,7 @@ function useAuthEvent(event, handler) {
276
313
  __name(useAuthEvent, "useAuthEvent");
277
314
 
278
315
  // src/client/react/useAuthEvents.ts
279
- import { useEffect as useEffect3, useRef as useRef4, useState as useState8 } from "react";
316
+ import { useEffect as useEffect4, useRef as useRef4, useState as useState8 } from "react";
280
317
  var ALL_EVENTS = [
281
318
  "login",
282
319
  "logout",
@@ -292,7 +329,7 @@ function useAuthEvents(opts) {
292
329
  const [entries, setEntries] = useState8([]);
293
330
  const maxRef = useRef4(max);
294
331
  maxRef.current = max;
295
- useEffect3(() => {
332
+ useEffect4(() => {
296
333
  const cleanups = [];
297
334
  for (const event of ALL_EVENTS) {
298
335
  const handler = /* @__PURE__ */ __name(((data) => {
@@ -313,64 +350,64 @@ function useAuthEvents(opts) {
313
350
  __name(useAuthEvents, "useAuthEvents");
314
351
 
315
352
  // src/client/react/SignedIn.tsx
316
- import { Fragment, jsx as jsx2 } from "react/jsx-runtime";
353
+ import { Fragment as Fragment2, jsx as jsx3 } from "react/jsx-runtime";
317
354
  function SignedIn({ children }) {
318
355
  const { status } = useSession();
319
356
  if (status !== "authenticated") return null;
320
- return /* @__PURE__ */ jsx2(Fragment, { children });
357
+ return /* @__PURE__ */ jsx3(Fragment2, { children });
321
358
  }
322
359
  __name(SignedIn, "SignedIn");
323
360
 
324
361
  // src/client/react/SignedOut.tsx
325
- import { Fragment as Fragment2, jsx as jsx3 } from "react/jsx-runtime";
362
+ import { Fragment as Fragment3, jsx as jsx4 } from "react/jsx-runtime";
326
363
  function SignedOut({ children }) {
327
364
  const { status } = useSession();
328
365
  if (status !== "unauthenticated") return null;
329
- return /* @__PURE__ */ jsx3(Fragment2, { children });
366
+ return /* @__PURE__ */ jsx4(Fragment3, { children });
330
367
  }
331
368
  __name(SignedOut, "SignedOut");
332
369
 
333
370
  // src/client/react/AuthLoading.tsx
334
- import { Fragment as Fragment3, jsx as jsx4 } from "react/jsx-runtime";
371
+ import { Fragment as Fragment4, jsx as jsx5 } from "react/jsx-runtime";
335
372
  function AuthLoading({ children }) {
336
373
  const { status } = useSession();
337
374
  if (status !== "loading") return null;
338
- return /* @__PURE__ */ jsx4(Fragment3, { children });
375
+ return /* @__PURE__ */ jsx5(Fragment4, { children });
339
376
  }
340
377
  __name(AuthLoading, "AuthLoading");
341
378
 
342
379
  // src/client/react/Can.tsx
343
- import { Fragment as Fragment4, jsx as jsx5 } from "react/jsx-runtime";
380
+ import { Fragment as Fragment5, jsx as jsx6 } from "react/jsx-runtime";
344
381
  function Can({ permission, role, children, fallback = null }) {
345
382
  const { can, hasRole } = usePermissions();
346
383
  const allowed = permission ? can(permission) : role ? hasRole(role) : false;
347
- return /* @__PURE__ */ jsx5(Fragment4, { children: allowed ? children : fallback });
384
+ return /* @__PURE__ */ jsx6(Fragment5, { children: allowed ? children : fallback });
348
385
  }
349
386
  __name(Can, "Can");
350
387
 
351
388
  // src/client/react/Role.tsx
352
- import { Fragment as Fragment5, jsx as jsx6 } from "react/jsx-runtime";
389
+ import { Fragment as Fragment6, jsx as jsx7 } from "react/jsx-runtime";
353
390
  function Role(props) {
354
391
  const { is, any: anyRole, children, fallback = null } = props;
355
392
  const { hasRole, hasAnyRole } = usePermissions();
356
393
  const allowed = is ? hasRole(is) : anyRole ? hasAnyRole(anyRole) : false;
357
- return /* @__PURE__ */ jsx6(Fragment5, { children: allowed ? children : fallback });
394
+ return /* @__PURE__ */ jsx7(Fragment6, { children: allowed ? children : fallback });
358
395
  }
359
396
  __name(Role, "Role");
360
397
 
361
398
  // src/client/react/IfAuth.tsx
362
- import { Fragment as Fragment6, jsx as jsx7 } from "react/jsx-runtime";
399
+ import { Fragment as Fragment7, jsx as jsx8 } from "react/jsx-runtime";
363
400
  function IfAuth({ authenticated, unauthenticated, loading }) {
364
401
  const { status } = useSession();
365
402
  const user = useUser();
366
- if (status === "loading") return /* @__PURE__ */ jsx7(Fragment6, { children: loading?.() ?? null });
367
- if (status === "authenticated" && user) return /* @__PURE__ */ jsx7(Fragment6, { children: authenticated(user) });
368
- return /* @__PURE__ */ jsx7(Fragment6, { children: unauthenticated?.() ?? null });
403
+ if (status === "loading") return /* @__PURE__ */ jsx8(Fragment7, { children: loading?.() ?? null });
404
+ if (status === "authenticated" && user) return /* @__PURE__ */ jsx8(Fragment7, { children: authenticated(user) });
405
+ return /* @__PURE__ */ jsx8(Fragment7, { children: unauthenticated?.() ?? null });
369
406
  }
370
407
  __name(IfAuth, "IfAuth");
371
408
 
372
409
  // src/client/react/Protected.tsx
373
- import { Fragment as Fragment7, jsx as jsx8 } from "react/jsx-runtime";
410
+ import { Fragment as Fragment8, jsx as jsx9 } from "react/jsx-runtime";
374
411
  function Protected({
375
412
  children,
376
413
  redirectTo,
@@ -388,17 +425,17 @@ function Protected({
388
425
  onUnauthenticated
389
426
  });
390
427
  const { can, hasRole } = usePermissions();
391
- if (isLoading) return /* @__PURE__ */ jsx8(Fragment7, { children: resolvedLoading });
392
- if (!isAuthenticated) return /* @__PURE__ */ jsx8(Fragment7, { children: resolvedFallback });
393
- if (role && !hasRole(role)) return /* @__PURE__ */ jsx8(Fragment7, { children: resolvedFallback });
394
- if (permission && !can(permission)) return /* @__PURE__ */ jsx8(Fragment7, { children: resolvedFallback });
395
- return /* @__PURE__ */ jsx8(Fragment7, { children });
428
+ if (isLoading) return /* @__PURE__ */ jsx9(Fragment8, { children: resolvedLoading });
429
+ if (!isAuthenticated) return /* @__PURE__ */ jsx9(Fragment8, { children: resolvedFallback });
430
+ if (role && !hasRole(role)) return /* @__PURE__ */ jsx9(Fragment8, { children: resolvedFallback });
431
+ if (permission && !can(permission)) return /* @__PURE__ */ jsx9(Fragment8, { children: resolvedFallback });
432
+ return /* @__PURE__ */ jsx9(Fragment8, { children });
396
433
  }
397
434
  __name(Protected, "Protected");
398
435
 
399
436
  // src/client/react/AuthGate.tsx
400
- import { useEffect as useEffect4, useSyncExternalStore as useSyncExternalStore2 } from "react";
401
- import { Fragment as Fragment8, jsx as jsx9 } from "react/jsx-runtime";
437
+ import { useEffect as useEffect5, useSyncExternalStore as useSyncExternalStore2 } from "react";
438
+ import { Fragment as Fragment9, jsx as jsx10 } from "react/jsx-runtime";
402
439
  function AuthGate({
403
440
  children,
404
441
  loader = null,
@@ -415,13 +452,13 @@ function AuthGate({
415
452
  () => client.getState()
416
453
  );
417
454
  const hydrated = client.isHydrated();
418
- useEffect4(() => {
455
+ useEffect5(() => {
419
456
  if (hydrated) return;
420
457
  client.refresh().then(() => client.fetchUser()).catch(() => {
421
458
  });
422
459
  }, [client, hydrated]);
423
460
  if (!hydrated && !state.isAuthenticated) {
424
- return /* @__PURE__ */ jsx9(Fragment8, { children: loader });
461
+ return /* @__PURE__ */ jsx10(Fragment9, { children: loader });
425
462
  }
426
463
  if (!state.isAuthenticated) {
427
464
  if (onUnauthenticated) {
@@ -429,46 +466,46 @@ function AuthGate({
429
466
  } else if (redirectTo && typeof window !== "undefined") {
430
467
  window.location.href = redirectTo;
431
468
  }
432
- return /* @__PURE__ */ jsx9(Fragment8, { children: loader });
469
+ return /* @__PURE__ */ jsx10(Fragment9, { children: loader });
433
470
  }
434
471
  if (role && !client.hasRole(role)) {
435
- return /* @__PURE__ */ jsx9(Fragment8, { children: denied });
472
+ return /* @__PURE__ */ jsx10(Fragment9, { children: denied });
436
473
  }
437
474
  if (permission && !client.can(permission)) {
438
- return /* @__PURE__ */ jsx9(Fragment8, { children: denied });
475
+ return /* @__PURE__ */ jsx10(Fragment9, { children: denied });
439
476
  }
440
- return /* @__PURE__ */ jsx9(Fragment8, { children });
477
+ return /* @__PURE__ */ jsx10(Fragment9, { children });
441
478
  }
442
479
  __name(AuthGate, "AuthGate");
443
480
 
444
481
  // src/client/react/UserName.tsx
445
- import { Fragment as Fragment9, jsx as jsx10 } from "react/jsx-runtime";
482
+ import { Fragment as Fragment10, jsx as jsx11 } from "react/jsx-runtime";
446
483
  function UserName({ fallback = "" }) {
447
484
  const user = useUser();
448
- if (!user) return /* @__PURE__ */ jsx10(Fragment9, { children: fallback });
485
+ if (!user) return /* @__PURE__ */ jsx11(Fragment10, { children: fallback });
449
486
  const name = user.name ?? user.email ?? fallback;
450
- return /* @__PURE__ */ jsx10(Fragment9, { children: name });
487
+ return /* @__PURE__ */ jsx11(Fragment10, { children: name });
451
488
  }
452
489
  __name(UserName, "UserName");
453
490
 
454
491
  // src/client/react/UserEmail.tsx
455
- import { Fragment as Fragment10, jsx as jsx11 } from "react/jsx-runtime";
492
+ import { Fragment as Fragment11, jsx as jsx12 } from "react/jsx-runtime";
456
493
  function UserEmail({ fallback = "" }) {
457
494
  const user = useUser();
458
- return /* @__PURE__ */ jsx11(Fragment10, { children: user?.email ?? fallback });
495
+ return /* @__PURE__ */ jsx12(Fragment11, { children: user?.email ?? fallback });
459
496
  }
460
497
  __name(UserEmail, "UserEmail");
461
498
 
462
499
  // src/client/react/UserRole.tsx
463
- import { Fragment as Fragment11, jsx as jsx12 } from "react/jsx-runtime";
500
+ import { Fragment as Fragment12, jsx as jsx13 } from "react/jsx-runtime";
464
501
  function UserRole({ fallback = "" }) {
465
502
  const { roles } = usePermissions();
466
- return /* @__PURE__ */ jsx12(Fragment11, { children: roles[0] ?? fallback });
503
+ return /* @__PURE__ */ jsx13(Fragment12, { children: roles[0] ?? fallback });
467
504
  }
468
505
  __name(UserRole, "UserRole");
469
506
 
470
507
  // src/client/react/UserAvatar.tsx
471
- import { jsx as jsx13 } from "react/jsx-runtime";
508
+ import { jsx as jsx14 } from "react/jsx-runtime";
472
509
  function UserAvatar({ size = 32, className, style, alt }) {
473
510
  const user = useUser();
474
511
  const baseStyle = {
@@ -491,7 +528,7 @@ function UserAvatar({ size = 32, className, style, alt }) {
491
528
  const initial = label ? label[0].toUpperCase() : "?";
492
529
  const altText = alt ?? label ?? "User avatar";
493
530
  if (src) {
494
- return /* @__PURE__ */ jsx13(
531
+ return /* @__PURE__ */ jsx14(
495
532
  "img",
496
533
  {
497
534
  src,
@@ -503,7 +540,7 @@ function UserAvatar({ size = 32, className, style, alt }) {
503
540
  }
504
541
  );
505
542
  }
506
- return /* @__PURE__ */ jsx13(
543
+ return /* @__PURE__ */ jsx14(
507
544
  "span",
508
545
  {
509
546
  role: "img",
@@ -517,11 +554,11 @@ function UserAvatar({ size = 32, className, style, alt }) {
517
554
  __name(UserAvatar, "UserAvatar");
518
555
 
519
556
  // src/client/react/PermissionList.tsx
520
- import { Fragment as Fragment12, jsx as jsx14 } from "react/jsx-runtime";
557
+ import { Fragment as Fragment13, jsx as jsx15 } from "react/jsx-runtime";
521
558
  function PermissionList({ children, fallback = null }) {
522
559
  const { permissions } = usePermissions();
523
- if (permissions.length === 0) return /* @__PURE__ */ jsx14(Fragment12, { children: fallback });
524
- return /* @__PURE__ */ jsx14(Fragment12, { children: permissions.map((perm, i) => children(perm, i)) });
560
+ if (permissions.length === 0) return /* @__PURE__ */ jsx15(Fragment13, { children: fallback });
561
+ return /* @__PURE__ */ jsx15(Fragment13, { children: permissions.map((perm, i) => children(perm, i)) });
525
562
  }
526
563
  __name(PermissionList, "PermissionList");
527
564
 
@@ -560,9 +597,9 @@ function LoginButton({ href = "/login", children }) {
560
597
  __name(LoginButton, "LoginButton");
561
598
 
562
599
  // src/client/react/RedirectToLogin.tsx
563
- import { useEffect as useEffect5 } from "react";
600
+ import { useEffect as useEffect6 } from "react";
564
601
  function RedirectToLogin({ to = "/login", preserveFrom = true }) {
565
- useEffect5(() => {
602
+ useEffect6(() => {
566
603
  if (typeof window === "undefined") return;
567
604
  const target = preserveFrom ? `${to}?from=${encodeURIComponent(window.location.pathname + window.location.search)}` : to;
568
605
  window.location.href = target;
@@ -571,6 +608,7 @@ function RedirectToLogin({ to = "/login", preserveFrom = true }) {
571
608
  }
572
609
  __name(RedirectToLogin, "RedirectToLogin");
573
610
  export {
611
+ AuthBoundary,
574
612
  AuthGate,
575
613
  AuthLoading,
576
614
  AuthProvider,
@@ -74,8 +74,16 @@ interface AuthMiddlewareConfig {
74
74
  roleRoutes?: Record<string, string[]>;
75
75
  /** Refresh token cookie name (default: 'refreshToken') */
76
76
  cookieName?: string;
77
+ /** Session cookie name to clear on redirect (default: 'najm.session') */
78
+ sessionCookieName?: string;
77
79
  /** URL of the verify endpoint (default: derived from request) */
78
80
  verifyURL?: string;
81
+ /**
82
+ * When true, call the verify endpoint on EVERY protected route (not just
83
+ * roleRoutes). Redirects to loginRoute if the session is invalid. Adds one
84
+ * fetch per navigation — trade latency for stronger guarantees.
85
+ */
86
+ verifyAlways?: boolean;
79
87
  }
80
88
  /**
81
89
  * Create a Next.js middleware function that protects routes based on auth state.
@@ -230,6 +238,12 @@ interface DefineAuthConfig {
230
238
  sessionSecret?: string;
231
239
  /** Next.js middleware matcher (default: exclude _next, favicon, api) */
232
240
  matcher?: string[];
241
+ /**
242
+ * When true, call the verify endpoint on every protected route (not just
243
+ * roleRoutes). Redirects to loginRoute if the session is invalid. One extra
244
+ * fetch per navigation in exchange for guaranteed fresh auth state.
245
+ */
246
+ verifyAlways?: boolean;
233
247
  }
234
248
  interface AuthKit {
235
249
  /**
@@ -361,10 +361,22 @@ function withAuthMiddleware(config) {
361
361
  publicRoutes = [],
362
362
  loginRoute = "/login",
363
363
  roleRoutes = {},
364
- cookieName = "refreshToken"
364
+ cookieName = "refreshToken",
365
+ sessionCookieName = "najm.session",
366
+ verifyAlways = false
365
367
  } = config;
366
368
  return /* @__PURE__ */ __name(async function middleware(request) {
367
369
  const { NextResponse } = await import("next/server");
370
+ const redirectToLogin = /* @__PURE__ */ __name((pathname2, clearCookies) => {
371
+ const loginUrl = new URL(loginRoute, request.url);
372
+ loginUrl.searchParams.set("from", pathname2);
373
+ const res = NextResponse.redirect(loginUrl);
374
+ if (clearCookies) {
375
+ res.cookies.delete(cookieName);
376
+ res.cookies.delete(sessionCookieName);
377
+ }
378
+ return res;
379
+ }, "redirectToLogin");
368
380
  const url = new URL(request.url);
369
381
  const pathname = url.pathname;
370
382
  if (matchesAny(pathname, publicRoutes)) {
@@ -375,30 +387,28 @@ function withAuthMiddleware(config) {
375
387
  const cookie = request.headers.get("cookie") ?? "";
376
388
  const hasToken = cookieRegex(cookieName).test(cookie);
377
389
  if (!hasToken) {
378
- const loginUrl = new URL(loginRoute, request.url);
379
- loginUrl.searchParams.set("from", pathname);
380
- return NextResponse.redirect(loginUrl);
390
+ return redirectToLogin(pathname, true);
381
391
  }
382
392
  const requiredRoles = findMatchingRoles(pathname, roleRoutes);
383
- if (requiredRoles) {
393
+ const needsVerify = verifyAlways || !!requiredRoles;
394
+ if (needsVerify) {
384
395
  const verifyURL = config.verifyURL ?? `${url.origin}/api/auth/me`;
385
396
  try {
386
397
  const res = await fetch(verifyURL, {
387
398
  headers: { "Cookie": cookie, "Accept": "application/json" }
388
399
  });
389
400
  if (!res.ok) {
390
- const loginUrl = new URL(loginRoute, request.url);
391
- loginUrl.searchParams.set("from", pathname);
392
- return NextResponse.redirect(loginUrl);
401
+ return redirectToLogin(pathname, true);
393
402
  }
394
- const body = await res.json();
395
- const userRole = body?.data?.role;
396
- if (!userRole || !requiredRoles.includes(userRole)) {
397
- return new NextResponse(null, { status: 403 });
403
+ if (requiredRoles) {
404
+ const body = await res.json();
405
+ const userRole = body?.data?.role;
406
+ if (!userRole || !requiredRoles.includes(userRole)) {
407
+ return new NextResponse(null, { status: 403 });
408
+ }
398
409
  }
399
410
  } catch {
400
- const loginUrl = new URL(loginRoute, request.url);
401
- return NextResponse.redirect(loginUrl);
411
+ return redirectToLogin(pathname, true);
402
412
  }
403
413
  }
404
414
  return NextResponse.next();
@@ -909,6 +919,7 @@ function defineAuth(authConfig = {}) {
909
919
  sessionCookieName = "najm.session",
910
920
  sessionSecret,
911
921
  matcher = ["/((?!_next/static|_next/image|favicon.ico|api).*)"],
922
+ verifyAlways = false,
912
923
  refreshThreshold,
913
924
  tabSync,
914
925
  channelName,
@@ -967,6 +978,16 @@ function defineAuth(authConfig = {}) {
967
978
  const { NextResponse } = await import("next/server");
968
979
  const url = new URL(request.url);
969
980
  const pathname = url.pathname;
981
+ const redirectToLogin = /* @__PURE__ */ __name((clearCookies) => {
982
+ const loginUrl = new URL(loginRoute, request.url);
983
+ loginUrl.searchParams.set("from", pathname);
984
+ const res = NextResponse.redirect(loginUrl);
985
+ if (clearCookies) {
986
+ res.cookies.delete(cookieName);
987
+ res.cookies.delete(sessionCookieName);
988
+ }
989
+ return res;
990
+ }, "redirectToLogin");
970
991
  if (matchesAny2(pathname, publicRoutes)) {
971
992
  return NextResponse.next();
972
993
  }
@@ -975,31 +996,28 @@ function defineAuth(authConfig = {}) {
975
996
  const cookie = request.headers.get("cookie") ?? "";
976
997
  const hasToken = cookieRegex2(cookieName).test(cookie);
977
998
  if (!hasToken) {
978
- const loginUrl = new URL(loginRoute, request.url);
979
- loginUrl.searchParams.set("from", pathname);
980
- return NextResponse.redirect(loginUrl);
999
+ return redirectToLogin(true);
981
1000
  }
982
1001
  const requiredRoles = findMatchingRoles2(pathname, roleRoutes);
983
- if (requiredRoles) {
1002
+ const needsVerify = verifyAlways || !!requiredRoles;
1003
+ if (needsVerify) {
984
1004
  const verifyURL = `${url.origin}${apiBaseURL}${authPrefix}/me`;
985
1005
  try {
986
1006
  const res = await fetch(verifyURL, {
987
1007
  headers: { Cookie: cookie, Accept: "application/json" }
988
1008
  });
989
1009
  if (!res.ok) {
990
- const loginUrl = new URL(loginRoute, request.url);
991
- loginUrl.searchParams.set("from", pathname);
992
- return NextResponse.redirect(loginUrl);
1010
+ return redirectToLogin(true);
993
1011
  }
994
- const body = await res.json();
995
- const userRole = body?.data?.role;
996
- if (!userRole || !requiredRoles.includes(userRole)) {
997
- return new NextResponse(null, { status: 403 });
1012
+ if (requiredRoles) {
1013
+ const body = await res.json();
1014
+ const userRole = body?.data?.role;
1015
+ if (!userRole || !requiredRoles.includes(userRole)) {
1016
+ return new NextResponse(null, { status: 403 });
1017
+ }
998
1018
  }
999
1019
  } catch {
1000
- const loginUrl = new URL(loginRoute, request.url);
1001
- loginUrl.searchParams.set("from", pathname);
1002
- return NextResponse.redirect(loginUrl);
1020
+ return redirectToLogin(true);
1003
1021
  }
1004
1022
  }
1005
1023
  return NextResponse.next();
package/dist/index.js CHANGED
@@ -2314,7 +2314,7 @@ var AuthResolver = class AuthResolver2 {
2314
2314
  this.app.use("*", async (c, next) => {
2315
2315
  const raw = c.req.header("authorization") ?? "";
2316
2316
  const token = raw.replace(/^Bearer\s+/i, "").trim();
2317
- const result = token ? await this.resolve(token) : await this.resolveFromCookie();
2317
+ const result = token ? await this.resolve(token) || await this.resolveFromCookie() : await this.resolveFromCookie();
2318
2318
  if (result) {
2319
2319
  if (this.container.isActive()) {
2320
2320
  this.container.set(USER, result.user);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "najm-auth",
3
- "version": "1.1.29",
3
+ "version": "1.1.31",
4
4
  "description": "Authentication and authorization library for najm framework",
5
5
  "type": "module",
6
6
  "files": [