nextauthz 1.3.34 → 1.3.36

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/index.d.mts CHANGED
@@ -1,5 +1,5 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
- import React, { ReactNode } from 'react';
2
+ import { ReactNode } from 'react';
3
3
 
4
4
  type User = {
5
5
  [key: string]: any;
@@ -24,22 +24,19 @@ declare function createAuthContext<UserType extends User = User>(option?: {
24
24
  children: ReactNode;
25
25
  }) => react_jsx_runtime.JSX.Element;
26
26
  useAuth: () => AuthContextType<UserType>;
27
- tokenKey: string;
28
27
  };
29
28
 
30
- type AuthGuardProps = {
29
+ declare const AuthGuard: ({ children, redirectTo, fallback, }: {
31
30
  children: React.ReactNode;
32
31
  redirectTo?: string;
33
32
  fallback?: React.ReactNode;
34
- };
35
- declare const AuthGuard: ({ children, redirectTo, fallback, }: AuthGuardProps) => react_jsx_runtime.JSX.Element | null;
33
+ }) => react_jsx_runtime.JSX.Element | null;
36
34
 
37
- type RoleGuardProps = {
35
+ declare const RoleGuard: ({ children, allowedRoles, redirectTo, fallback, }: {
38
36
  children: React.ReactNode;
39
37
  allowedRoles: string[];
40
38
  redirectTo?: string;
41
39
  fallback?: React.ReactNode;
42
- };
43
- declare const RoleGuard: ({ children, allowedRoles, redirectTo, fallback, }: RoleGuardProps) => react_jsx_runtime.JSX.Element | null;
40
+ }) => react_jsx_runtime.JSX.Element | null;
44
41
 
45
42
  export { AuthGuard, RoleGuard, createAuthContext };
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
- import React, { ReactNode } from 'react';
2
+ import { ReactNode } from 'react';
3
3
 
4
4
  type User = {
5
5
  [key: string]: any;
@@ -24,22 +24,19 @@ declare function createAuthContext<UserType extends User = User>(option?: {
24
24
  children: ReactNode;
25
25
  }) => react_jsx_runtime.JSX.Element;
26
26
  useAuth: () => AuthContextType<UserType>;
27
- tokenKey: string;
28
27
  };
29
28
 
30
- type AuthGuardProps = {
29
+ declare const AuthGuard: ({ children, redirectTo, fallback, }: {
31
30
  children: React.ReactNode;
32
31
  redirectTo?: string;
33
32
  fallback?: React.ReactNode;
34
- };
35
- declare const AuthGuard: ({ children, redirectTo, fallback, }: AuthGuardProps) => react_jsx_runtime.JSX.Element | null;
33
+ }) => react_jsx_runtime.JSX.Element | null;
36
34
 
37
- type RoleGuardProps = {
35
+ declare const RoleGuard: ({ children, allowedRoles, redirectTo, fallback, }: {
38
36
  children: React.ReactNode;
39
37
  allowedRoles: string[];
40
38
  redirectTo?: string;
41
39
  fallback?: React.ReactNode;
42
- };
43
- declare const RoleGuard: ({ children, allowedRoles, redirectTo, fallback, }: RoleGuardProps) => react_jsx_runtime.JSX.Element | null;
40
+ }) => react_jsx_runtime.JSX.Element | null;
44
41
 
45
42
  export { AuthGuard, RoleGuard, createAuthContext };
package/dist/index.js CHANGED
@@ -65,87 +65,82 @@ function createAuthContext(option) {
65
65
  const storage = option?.storage ?? "cookie";
66
66
  const tokenKey = option?.tokenKey ?? "access_token";
67
67
  const rolePath = option?.rolePath ?? "role";
68
+ const manager = (0, import_react_token_manager.useTokenManager)();
69
+ const user = useAuthStore((s) => s.user);
70
+ const role = useAuthStore((s) => s.role);
71
+ const isAuthenticated = useAuthStore((s) => s.isAuthenticated);
72
+ const isAuthChecked = useAuthStore((s) => s.isAuthChecked);
73
+ const setUser = useAuthStore((s) => s.setUser);
74
+ const setRole = useAuthStore((s) => s.setRole);
75
+ const setAuth = useAuthStore((s) => s.setAuth);
76
+ const setAuthChecked = useAuthStore((s) => s.setAuthChecked);
77
+ const resetAuth = useAuthStore((s) => s.resetAuth);
68
78
  (0, import_react.useEffect)(() => {
69
79
  (0, import_react_token_manager.configureTokenManager)({ storage });
70
80
  }, [storage]);
71
- const manager = (0, import_react_token_manager.useTokenManager)();
72
- const {
73
- user,
74
- role,
75
- setUser,
76
- setRole,
77
- resetAuth,
78
- isAuthChecked,
79
- isAuthenticated,
80
- setAuth,
81
- setAuthChecked
82
- } = useAuthStore();
83
81
  const extractRole = (userObj) => {
84
- if (!userObj || !rolePath) return null;
82
+ if (!userObj) return null;
85
83
  return rolePath.split(".").reduce((acc, key) => acc?.[key], userObj) ?? null;
86
84
  };
87
85
  (0, import_react.useEffect)(() => {
88
86
  const storedUser = manager.getSingleToken("user");
89
87
  const token = manager.getSingleToken(tokenKey);
90
88
  if (token && !manager.isExpired(token)) {
91
- try {
92
- setAuth(true);
93
- if (storedUser) {
94
- const parsedUser = JSON.parse(storedUser);
95
- setUser(parsedUser);
96
- setRole(extractRole(parsedUser));
89
+ setAuth(true);
90
+ if (storedUser) {
91
+ try {
92
+ const parsed = JSON.parse(storedUser);
93
+ setUser(parsed);
94
+ setRole(extractRole(parsed));
95
+ } catch {
96
+ resetAuth();
97
97
  }
98
- } catch {
99
- resetAuth();
100
98
  }
101
99
  } else {
102
100
  resetAuth();
103
101
  }
104
102
  setAuthChecked(true);
105
103
  }, [tokenKey]);
106
- const login = (tokens, userData, role2) => {
107
- const tokenValue = tokens[tokenKey] ?? tokens["access_token"] ?? Object.values(tokens)[0];
108
- manager.setTokens({
109
- ...tokens,
110
- [tokenKey]: tokenValue,
111
- user: JSON.stringify(userData ?? null)
112
- });
113
- if (userData) setUser(userData);
114
- setRole(role2 ?? extractRole(userData));
115
- setAuth(true);
116
- setAuthChecked(true);
117
- };
118
- const logout = () => {
104
+ const login = (0, import_react.useCallback)(
105
+ (tokens, userData, role2) => {
106
+ const tokenValue = tokens[tokenKey] ?? tokens["access_token"] ?? Object.values(tokens)[0];
107
+ manager.setTokens({
108
+ ...tokens,
109
+ [tokenKey]: tokenValue,
110
+ user: JSON.stringify(userData ?? null)
111
+ });
112
+ if (userData) setUser(userData);
113
+ setRole(role2 ?? extractRole(userData));
114
+ setAuth(true);
115
+ setAuthChecked(true);
116
+ },
117
+ [tokenKey]
118
+ );
119
+ const logout = (0, import_react.useCallback)(() => {
119
120
  manager.clearTokens();
120
121
  resetAuth();
121
- };
122
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
123
- AuthContext.Provider,
124
- {
125
- value: {
126
- user,
127
- role,
128
- isAuthenticated,
129
- isAuthChecked,
130
- login,
131
- logout,
132
- setUser: (u) => setUser(u),
133
- tokenKey
134
- },
135
- children
136
- }
122
+ }, []);
123
+ const value = (0, import_react.useMemo)(
124
+ () => ({
125
+ user,
126
+ role,
127
+ isAuthenticated,
128
+ isAuthChecked,
129
+ login,
130
+ logout,
131
+ setUser,
132
+ tokenKey
133
+ }),
134
+ [user, role, isAuthenticated, isAuthChecked, login, logout, tokenKey]
137
135
  );
136
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(AuthContext.Provider, { value, children });
138
137
  };
139
138
  const useAuth = () => {
140
139
  const context = (0, import_react.useContext)(AuthContext);
141
140
  if (!context) throw new Error("useAuth must be used within AuthProvider");
142
141
  return context;
143
142
  };
144
- return {
145
- AuthProvider,
146
- useAuth,
147
- tokenKey: option?.tokenKey ?? "access_token"
148
- };
143
+ return { AuthProvider, useAuth };
149
144
  }
150
145
 
151
146
  // src/AuthGuard.tsx
@@ -158,18 +153,16 @@ var AuthGuard = ({
158
153
  fallback = null
159
154
  }) => {
160
155
  const router = (0, import_navigation.useRouter)();
161
- const { isAuthenticated, isAuthChecked } = useAuthStore();
156
+ const isAuthenticated = useAuthStore((s) => s.isAuthenticated);
157
+ const isAuthChecked = useAuthStore((s) => s.isAuthChecked);
162
158
  (0, import_react2.useEffect)(() => {
163
159
  if (!isAuthChecked) return;
164
160
  if (!isAuthenticated) {
165
161
  router.replace(redirectTo);
166
162
  }
167
- }, [isAuthenticated, isAuthChecked, redirectTo, router]);
163
+ }, [isAuthenticated, isAuthChecked]);
168
164
  if (!isAuthChecked) return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_jsx_runtime2.Fragment, { children: fallback });
169
- if (!isAuthenticated) {
170
- router.replace(redirectTo);
171
- return null;
172
- }
165
+ if (!isAuthenticated) return null;
173
166
  return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_jsx_runtime2.Fragment, { children });
174
167
  };
175
168
  var AuthGuard_default = AuthGuard;
@@ -185,18 +178,17 @@ var RoleGuard = ({
185
178
  fallback = null
186
179
  }) => {
187
180
  const router = (0, import_navigation2.useRouter)();
188
- const { role, isAuthChecked, isAuthenticated } = useAuthStore();
181
+ const role = useAuthStore((s) => s.role);
182
+ const isAuthenticated = useAuthStore((s) => s.isAuthenticated);
183
+ const isAuthChecked = useAuthStore((s) => s.isAuthChecked);
189
184
  (0, import_react3.useEffect)(() => {
190
- if (!isAuthChecked) return;
185
+ if (!isAuthChecked || !isAuthenticated) return;
191
186
  if (!role || !allowedRoles.includes(role)) {
192
187
  router.replace(redirectTo);
193
188
  }
194
- }, [role, isAuthChecked, allowedRoles, redirectTo, router]);
189
+ }, [role, isAuthenticated, isAuthChecked]);
195
190
  if (!isAuthChecked) return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_jsx_runtime3.Fragment, { children: fallback });
196
- if (!isAuthenticated) {
197
- router.replace(redirectTo);
198
- return null;
199
- }
191
+ if (!isAuthenticated) return null;
200
192
  if (!role || !allowedRoles.includes(role)) return null;
201
193
  return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_jsx_runtime3.Fragment, { children });
202
194
  };
package/dist/index.mjs CHANGED
@@ -1,7 +1,13 @@
1
1
  "use client";
2
2
 
3
3
  // src/AuthProvider.tsx
4
- import { createContext, useContext, useEffect } from "react";
4
+ import {
5
+ createContext,
6
+ useContext,
7
+ useEffect,
8
+ useMemo,
9
+ useCallback
10
+ } from "react";
5
11
  import { configureTokenManager, useTokenManager } from "react-token-manager";
6
12
 
7
13
  // store/useGuardStore.ts
@@ -38,87 +44,82 @@ function createAuthContext(option) {
38
44
  const storage = option?.storage ?? "cookie";
39
45
  const tokenKey = option?.tokenKey ?? "access_token";
40
46
  const rolePath = option?.rolePath ?? "role";
47
+ const manager = useTokenManager();
48
+ const user = useAuthStore((s) => s.user);
49
+ const role = useAuthStore((s) => s.role);
50
+ const isAuthenticated = useAuthStore((s) => s.isAuthenticated);
51
+ const isAuthChecked = useAuthStore((s) => s.isAuthChecked);
52
+ const setUser = useAuthStore((s) => s.setUser);
53
+ const setRole = useAuthStore((s) => s.setRole);
54
+ const setAuth = useAuthStore((s) => s.setAuth);
55
+ const setAuthChecked = useAuthStore((s) => s.setAuthChecked);
56
+ const resetAuth = useAuthStore((s) => s.resetAuth);
41
57
  useEffect(() => {
42
58
  configureTokenManager({ storage });
43
59
  }, [storage]);
44
- const manager = useTokenManager();
45
- const {
46
- user,
47
- role,
48
- setUser,
49
- setRole,
50
- resetAuth,
51
- isAuthChecked,
52
- isAuthenticated,
53
- setAuth,
54
- setAuthChecked
55
- } = useAuthStore();
56
60
  const extractRole = (userObj) => {
57
- if (!userObj || !rolePath) return null;
61
+ if (!userObj) return null;
58
62
  return rolePath.split(".").reduce((acc, key) => acc?.[key], userObj) ?? null;
59
63
  };
60
64
  useEffect(() => {
61
65
  const storedUser = manager.getSingleToken("user");
62
66
  const token = manager.getSingleToken(tokenKey);
63
67
  if (token && !manager.isExpired(token)) {
64
- try {
65
- setAuth(true);
66
- if (storedUser) {
67
- const parsedUser = JSON.parse(storedUser);
68
- setUser(parsedUser);
69
- setRole(extractRole(parsedUser));
68
+ setAuth(true);
69
+ if (storedUser) {
70
+ try {
71
+ const parsed = JSON.parse(storedUser);
72
+ setUser(parsed);
73
+ setRole(extractRole(parsed));
74
+ } catch {
75
+ resetAuth();
70
76
  }
71
- } catch {
72
- resetAuth();
73
77
  }
74
78
  } else {
75
79
  resetAuth();
76
80
  }
77
81
  setAuthChecked(true);
78
82
  }, [tokenKey]);
79
- const login = (tokens, userData, role2) => {
80
- const tokenValue = tokens[tokenKey] ?? tokens["access_token"] ?? Object.values(tokens)[0];
81
- manager.setTokens({
82
- ...tokens,
83
- [tokenKey]: tokenValue,
84
- user: JSON.stringify(userData ?? null)
85
- });
86
- if (userData) setUser(userData);
87
- setRole(role2 ?? extractRole(userData));
88
- setAuth(true);
89
- setAuthChecked(true);
90
- };
91
- const logout = () => {
83
+ const login = useCallback(
84
+ (tokens, userData, role2) => {
85
+ const tokenValue = tokens[tokenKey] ?? tokens["access_token"] ?? Object.values(tokens)[0];
86
+ manager.setTokens({
87
+ ...tokens,
88
+ [tokenKey]: tokenValue,
89
+ user: JSON.stringify(userData ?? null)
90
+ });
91
+ if (userData) setUser(userData);
92
+ setRole(role2 ?? extractRole(userData));
93
+ setAuth(true);
94
+ setAuthChecked(true);
95
+ },
96
+ [tokenKey]
97
+ );
98
+ const logout = useCallback(() => {
92
99
  manager.clearTokens();
93
100
  resetAuth();
94
- };
95
- return /* @__PURE__ */ jsx(
96
- AuthContext.Provider,
97
- {
98
- value: {
99
- user,
100
- role,
101
- isAuthenticated,
102
- isAuthChecked,
103
- login,
104
- logout,
105
- setUser: (u) => setUser(u),
106
- tokenKey
107
- },
108
- children
109
- }
101
+ }, []);
102
+ const value = useMemo(
103
+ () => ({
104
+ user,
105
+ role,
106
+ isAuthenticated,
107
+ isAuthChecked,
108
+ login,
109
+ logout,
110
+ setUser,
111
+ tokenKey
112
+ }),
113
+ [user, role, isAuthenticated, isAuthChecked, login, logout, tokenKey]
110
114
  );
115
+ return /* @__PURE__ */ jsx(AuthContext.Provider, { value, children });
111
116
  };
112
117
  const useAuth = () => {
113
118
  const context = useContext(AuthContext);
114
119
  if (!context) throw new Error("useAuth must be used within AuthProvider");
115
120
  return context;
116
121
  };
117
- return {
118
- AuthProvider,
119
- useAuth,
120
- tokenKey: option?.tokenKey ?? "access_token"
121
- };
122
+ return { AuthProvider, useAuth };
122
123
  }
123
124
 
124
125
  // src/AuthGuard.tsx
@@ -131,18 +132,16 @@ var AuthGuard = ({
131
132
  fallback = null
132
133
  }) => {
133
134
  const router = useRouter();
134
- const { isAuthenticated, isAuthChecked } = useAuthStore();
135
+ const isAuthenticated = useAuthStore((s) => s.isAuthenticated);
136
+ const isAuthChecked = useAuthStore((s) => s.isAuthChecked);
135
137
  useEffect2(() => {
136
138
  if (!isAuthChecked) return;
137
139
  if (!isAuthenticated) {
138
140
  router.replace(redirectTo);
139
141
  }
140
- }, [isAuthenticated, isAuthChecked, redirectTo, router]);
142
+ }, [isAuthenticated, isAuthChecked]);
141
143
  if (!isAuthChecked) return /* @__PURE__ */ jsx2(Fragment, { children: fallback });
142
- if (!isAuthenticated) {
143
- router.replace(redirectTo);
144
- return null;
145
- }
144
+ if (!isAuthenticated) return null;
146
145
  return /* @__PURE__ */ jsx2(Fragment, { children });
147
146
  };
148
147
  var AuthGuard_default = AuthGuard;
@@ -158,18 +157,17 @@ var RoleGuard = ({
158
157
  fallback = null
159
158
  }) => {
160
159
  const router = useRouter2();
161
- const { role, isAuthChecked, isAuthenticated } = useAuthStore();
160
+ const role = useAuthStore((s) => s.role);
161
+ const isAuthenticated = useAuthStore((s) => s.isAuthenticated);
162
+ const isAuthChecked = useAuthStore((s) => s.isAuthChecked);
162
163
  useEffect3(() => {
163
- if (!isAuthChecked) return;
164
+ if (!isAuthChecked || !isAuthenticated) return;
164
165
  if (!role || !allowedRoles.includes(role)) {
165
166
  router.replace(redirectTo);
166
167
  }
167
- }, [role, isAuthChecked, allowedRoles, redirectTo, router]);
168
+ }, [role, isAuthenticated, isAuthChecked]);
168
169
  if (!isAuthChecked) return /* @__PURE__ */ jsx3(Fragment2, { children: fallback });
169
- if (!isAuthenticated) {
170
- router.replace(redirectTo);
171
- return null;
172
- }
170
+ if (!isAuthenticated) return null;
173
171
  if (!role || !allowedRoles.includes(role)) return null;
174
172
  return /* @__PURE__ */ jsx3(Fragment2, { children });
175
173
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nextauthz",
3
- "version": "1.3.34",
3
+ "version": "1.3.36",
4
4
  "description": "",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",
package/readme.md CHANGED
@@ -153,7 +153,7 @@ Props
153
153
  ## Usage Example
154
154
 
155
155
  ```bash
156
- import AuthGuard from 'nextauthz'
156
+ import { AuthGuard } from 'nextauthz'
157
157
 
158
158
  function DashboardPage() {
159
159
  return (
package/src/AuthGuard.tsx CHANGED
@@ -1,41 +1,34 @@
1
1
  'use client'
2
2
 
3
- import React, { useEffect } from 'react'
3
+ import { useEffect } from 'react'
4
4
  import { useRouter } from 'next/navigation'
5
5
  import { useAuthStore } from '../store/useGuardStore'
6
6
 
7
- type AuthGuardProps = {
8
- children: React.ReactNode
9
- redirectTo?: string
10
- fallback?: React.ReactNode
11
- }
12
-
13
7
  const AuthGuard = ({
14
8
  children,
15
9
  redirectTo = '/login',
16
10
  fallback = null,
17
- }: AuthGuardProps) => {
11
+ }: {
12
+ children: React.ReactNode
13
+ redirectTo?: string
14
+ fallback?: React.ReactNode
15
+ }) => {
18
16
  const router = useRouter()
19
17
 
20
- const { isAuthenticated, isAuthChecked } = useAuthStore()
18
+ const isAuthenticated = useAuthStore((s) => s.isAuthenticated)
19
+ const isAuthChecked = useAuthStore((s) => s.isAuthChecked)
21
20
 
22
21
  useEffect(() => {
23
22
  if (!isAuthChecked) return
24
-
25
23
  if (!isAuthenticated) {
26
24
  router.replace(redirectTo)
27
25
  }
28
- }, [isAuthenticated, isAuthChecked, redirectTo, router])
26
+ }, [isAuthenticated, isAuthChecked])
29
27
 
30
28
  if (!isAuthChecked) return <>{fallback}</>
31
-
32
- if (!isAuthenticated) {
33
- router.replace(redirectTo)
34
- return null
35
- }
36
-
29
+ if (!isAuthenticated) return null
37
30
 
38
31
  return <>{children}</>
39
32
  }
40
33
 
41
- export default AuthGuard
34
+ export default AuthGuard
@@ -1,13 +1,16 @@
1
1
  'use client'
2
2
 
3
- import { createContext, useContext, ReactNode, useEffect } from 'react'
3
+ import {
4
+ createContext,
5
+ useContext,
6
+ ReactNode,
7
+ useEffect,
8
+ useMemo,
9
+ useCallback,
10
+ } from 'react'
4
11
  import { configureTokenManager, useTokenManager } from 'react-token-manager'
5
12
  import { useAuthStore, User } from '../store/useGuardStore'
6
13
 
7
- /* ---------------------------------- */
8
- /* Types */
9
- /* ---------------------------------- */
10
-
11
14
  export type AuthContextType<UserType extends User = User> = {
12
15
  user: UserType | null
13
16
  role: string | null
@@ -19,14 +22,10 @@ export type AuthContextType<UserType extends User = User> = {
19
22
  tokenKey: string
20
23
  }
21
24
 
22
- /* ---------------------------------- */
23
- /* Factory */
24
- /* ---------------------------------- */
25
-
26
25
  export function createAuthContext<UserType extends User = User>(option?: {
27
26
  storage?: 'localStorage' | 'sessionStorage' | 'cookie'
28
27
  tokenKey?: string
29
- rolePath?: string // e.g. 'role' or 'profile.role'
28
+ rolePath?: string
30
29
  }) {
31
30
  const AuthContext = createContext<AuthContextType<UserType> | undefined>(undefined)
32
31
 
@@ -35,109 +34,94 @@ export function createAuthContext<UserType extends User = User>(option?: {
35
34
  const tokenKey = option?.tokenKey ?? 'access_token'
36
35
  const rolePath = option?.rolePath ?? 'role'
37
36
 
37
+ const manager = useTokenManager()
38
+
39
+ // ✅ Selectors (IMPORTANT)
40
+ const user = useAuthStore((s) => s.user)
41
+ const role = useAuthStore((s) => s.role)
42
+ const isAuthenticated = useAuthStore((s) => s.isAuthenticated)
43
+ const isAuthChecked = useAuthStore((s) => s.isAuthChecked)
44
+
45
+ const setUser = useAuthStore((s) => s.setUser)
46
+ const setRole = useAuthStore((s) => s.setRole)
47
+ const setAuth = useAuthStore((s) => s.setAuth)
48
+ const setAuthChecked = useAuthStore((s) => s.setAuthChecked)
49
+ const resetAuth = useAuthStore((s) => s.resetAuth)
50
+
38
51
  useEffect(() => {
39
52
  configureTokenManager({ storage })
40
53
  }, [storage])
41
54
 
42
- const manager = useTokenManager()
43
- const {
44
- user,
45
- role,
46
- setUser,
47
- setRole,
48
- resetAuth,
49
- isAuthChecked,
50
- isAuthenticated,
51
- setAuth,
52
- setAuthChecked,
53
- } = useAuthStore()
54
-
55
- /* ---------------------------------- */
56
- /* Helper: get role from path */
57
- /* ---------------------------------- */
58
55
  const extractRole = (userObj: any) => {
59
- if (!userObj || !rolePath) return null
56
+ if (!userObj) return null
60
57
  return rolePath.split('.').reduce((acc: any, key: string) => acc?.[key], userObj) ?? null
61
58
  }
62
-
63
-
64
- /* ---------------------------------- */
65
- /* Hydrate user from storage */
66
- /* ---------------------------------- */
67
59
 
60
+ // ✅ Hydrate once
68
61
  useEffect(() => {
69
62
  const storedUser = manager.getSingleToken('user')
70
63
  const token = manager.getSingleToken(tokenKey)
71
64
 
72
65
  if (token && !manager.isExpired(token)) {
73
- try {
74
- setAuth(true)
75
-
76
- if (storedUser) {
77
- const parsedUser = JSON.parse(storedUser) as UserType
78
- setUser(parsedUser)
79
- setRole(extractRole(parsedUser))
66
+ setAuth(true)
67
+
68
+ if (storedUser) {
69
+ try {
70
+ const parsed = JSON.parse(storedUser)
71
+ setUser(parsed)
72
+ setRole(extractRole(parsed))
73
+ } catch {
74
+ resetAuth()
80
75
  }
81
- } catch {
82
- resetAuth()
83
76
  }
84
77
  } else {
85
78
  resetAuth()
86
79
  }
87
80
 
88
81
  setAuthChecked(true)
89
- // eslint-disable-next-line react-hooks/exhaustive-deps
90
82
  }, [tokenKey])
91
83
 
92
- /* ---------------------------------- */
93
- /* Login */
94
- /* ---------------------------------- */
95
-
96
- const login = (tokens: Record<string, string>, userData?: UserType, role?: string) => {
97
- const tokenValue = tokens[tokenKey] ?? tokens['access_token'] ?? Object.values(tokens)[0]
98
-
99
- manager.setTokens({
100
- ...tokens,
101
- [tokenKey]: tokenValue,
102
- user: JSON.stringify(userData ?? null),
103
- })
104
-
105
- if (userData) setUser(userData)
106
- setRole(role ?? extractRole(userData))
107
- setAuth(true)
108
- setAuthChecked(true)
109
- }
110
-
111
- /* ---------------------------------- */
112
- /* Logout */
113
- /* ---------------------------------- */
84
+ const login = useCallback(
85
+ (tokens: Record<string, string>, userData?: UserType, role?: string) => {
86
+ const tokenValue =
87
+ tokens[tokenKey] ?? tokens['access_token'] ?? Object.values(tokens)[0]
88
+
89
+ manager.setTokens({
90
+ ...tokens,
91
+ [tokenKey]: tokenValue,
92
+ user: JSON.stringify(userData ?? null),
93
+ })
94
+
95
+ if (userData) setUser(userData)
96
+ setRole(role ?? extractRole(userData))
97
+ setAuth(true)
98
+ setAuthChecked(true)
99
+ },
100
+ [tokenKey]
101
+ )
114
102
 
115
- const logout = () => {
103
+ const logout = useCallback(() => {
116
104
  manager.clearTokens()
117
105
  resetAuth()
118
- }
119
-
120
- return (
121
- <AuthContext.Provider
122
- value={{
123
- user: user as UserType | null,
124
- role,
125
- isAuthenticated,
126
- isAuthChecked,
127
- login,
128
- logout,
129
- setUser: (u) => setUser(u),
130
- tokenKey,
131
- }}
132
- >
133
- {children}
134
- </AuthContext.Provider>
106
+ }, [])
107
+
108
+ // ✅ MEMOIZED CONTEXT VALUE
109
+ const value = useMemo(
110
+ () => ({
111
+ user: user as UserType | null,
112
+ role,
113
+ isAuthenticated,
114
+ isAuthChecked,
115
+ login,
116
+ logout,
117
+ setUser,
118
+ tokenKey,
119
+ }),
120
+ [user, role, isAuthenticated, isAuthChecked, login, logout, tokenKey]
135
121
  )
136
- }
137
122
 
138
- /* ---------------------------------- */
139
- /* Hook */
140
- /* ---------------------------------- */
123
+ return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>
124
+ }
141
125
 
142
126
  const useAuth = () => {
143
127
  const context = useContext(AuthContext)
@@ -145,9 +129,5 @@ export function createAuthContext<UserType extends User = User>(option?: {
145
129
  return context
146
130
  }
147
131
 
148
- return {
149
- AuthProvider,
150
- useAuth,
151
- tokenKey: option?.tokenKey ?? 'access_token',
152
- }
153
- }
132
+ return { AuthProvider, useAuth }
133
+ }
package/src/RoleGuard.tsx CHANGED
@@ -1,45 +1,39 @@
1
1
  'use client'
2
2
 
3
- import React, { useEffect } from 'react'
3
+ import { useEffect } from 'react'
4
4
  import { useRouter } from 'next/navigation'
5
5
  import { useAuthStore } from '../store/useGuardStore'
6
6
 
7
- type RoleGuardProps = {
8
- children: React.ReactNode
9
- allowedRoles: string[]
10
- redirectTo?: string
11
- fallback?: React.ReactNode
12
- }
13
-
14
7
  const RoleGuard = ({
15
8
  children,
16
9
  allowedRoles,
17
10
  redirectTo = '/unauthorized',
18
11
  fallback = null,
19
- }: RoleGuardProps) => {
12
+ }: {
13
+ children: React.ReactNode
14
+ allowedRoles: string[]
15
+ redirectTo?: string
16
+ fallback?: React.ReactNode
17
+ }) => {
20
18
  const router = useRouter()
21
- const { role, isAuthChecked, isAuthenticated } = useAuthStore()
19
+
20
+ const role = useAuthStore((s) => s.role)
21
+ const isAuthenticated = useAuthStore((s) => s.isAuthenticated)
22
+ const isAuthChecked = useAuthStore((s) => s.isAuthChecked)
22
23
 
23
24
  useEffect(() => {
24
- if (!isAuthChecked) return
25
+ if (!isAuthChecked || !isAuthenticated) return
25
26
 
26
- // If role not allowed, redirect
27
27
  if (!role || !allowedRoles.includes(role)) {
28
28
  router.replace(redirectTo)
29
29
  }
30
- }, [role, isAuthChecked, allowedRoles, redirectTo, router])
30
+ }, [role, isAuthenticated, isAuthChecked])
31
31
 
32
32
  if (!isAuthChecked) return <>{fallback}</>
33
-
34
- if (!isAuthenticated) {
35
- router.replace(redirectTo)
36
- return null
37
- }
38
-
39
- // Block rendering if role is not allowed
33
+ if (!isAuthenticated) return null
40
34
  if (!role || !allowedRoles.includes(role)) return null
41
35
 
42
36
  return <>{children}</>
43
37
  }
44
38
 
45
- export default RoleGuard
39
+ export default RoleGuard