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.
- package/dist/client/react/index.d.ts +16 -2
- package/dist/client/react/index.js +101 -63
- package/dist/client/server/index.d.ts +14 -0
- package/dist/client/server/index.js +46 -28
- package/dist/index.js +1 -1
- package/package.json +1 -1
|
@@ -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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
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__ */
|
|
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
|
|
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__ */
|
|
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
|
|
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__ */
|
|
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
|
|
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__ */
|
|
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
|
|
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__ */
|
|
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
|
|
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__ */
|
|
367
|
-
if (status === "authenticated" && user) return /* @__PURE__ */
|
|
368
|
-
return /* @__PURE__ */
|
|
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
|
|
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__ */
|
|
392
|
-
if (!isAuthenticated) return /* @__PURE__ */
|
|
393
|
-
if (role && !hasRole(role)) return /* @__PURE__ */
|
|
394
|
-
if (permission && !can(permission)) return /* @__PURE__ */
|
|
395
|
-
return /* @__PURE__ */
|
|
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
|
|
401
|
-
import { Fragment as
|
|
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
|
-
|
|
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__ */
|
|
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__ */
|
|
469
|
+
return /* @__PURE__ */ jsx10(Fragment9, { children: loader });
|
|
433
470
|
}
|
|
434
471
|
if (role && !client.hasRole(role)) {
|
|
435
|
-
return /* @__PURE__ */
|
|
472
|
+
return /* @__PURE__ */ jsx10(Fragment9, { children: denied });
|
|
436
473
|
}
|
|
437
474
|
if (permission && !client.can(permission)) {
|
|
438
|
-
return /* @__PURE__ */
|
|
475
|
+
return /* @__PURE__ */ jsx10(Fragment9, { children: denied });
|
|
439
476
|
}
|
|
440
|
-
return /* @__PURE__ */
|
|
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
|
|
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__ */
|
|
485
|
+
if (!user) return /* @__PURE__ */ jsx11(Fragment10, { children: fallback });
|
|
449
486
|
const name = user.name ?? user.email ?? fallback;
|
|
450
|
-
return /* @__PURE__ */
|
|
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
|
|
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__ */
|
|
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
|
|
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__ */
|
|
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
|
|
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__ */
|
|
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__ */
|
|
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
|
|
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__ */
|
|
524
|
-
return /* @__PURE__ */
|
|
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
|
|
600
|
+
import { useEffect as useEffect6 } from "react";
|
|
564
601
|
function RedirectToLogin({ to = "/login", preserveFrom = true }) {
|
|
565
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
391
|
-
loginUrl.searchParams.set("from", pathname);
|
|
392
|
-
return NextResponse.redirect(loginUrl);
|
|
401
|
+
return redirectToLogin(pathname, true);
|
|
393
402
|
}
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
991
|
-
loginUrl.searchParams.set("from", pathname);
|
|
992
|
-
return NextResponse.redirect(loginUrl);
|
|
1010
|
+
return redirectToLogin(true);
|
|
993
1011
|
}
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
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
|
-
|
|
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);
|