nextauthz 1.2.1 → 1.2.4

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
@@ -6,35 +6,41 @@ type User$1 = {
6
6
  [key: string]: any;
7
7
  };
8
8
 
9
- type AuthContextType<UserType extends User$1> = {
9
+ type AuthContextType<UserType extends User$1 = User$1> = {
10
10
  user: UserType | null;
11
- login: (tokens: Record<string, string>, userData: UserType) => void;
11
+ role: string | null;
12
+ isAuthenticated: boolean;
13
+ login: (tokens: Record<string, string>, userData: UserType, role: string) => void;
12
14
  logout: () => void;
13
- setUser: (user: UserType) => void;
14
- loading: boolean;
15
- error: Error | null;
15
+ setUser: (user: UserType | null) => void;
16
+ tokenKey: string;
16
17
  };
17
18
 
18
19
  type AuthGuardProps = {
19
20
  children: React__default.ReactNode;
20
21
  redirectTo?: string;
21
- tokenKey?: string;
22
+ fallback?: React__default.ReactNode;
22
23
  };
23
- declare const AuthGuard: ({ children, redirectTo, tokenKey, }: AuthGuardProps) => react_jsx_runtime.JSX.Element;
24
+ declare const AuthGuard: ({ children, redirectTo, fallback, }: AuthGuardProps) => react_jsx_runtime.JSX.Element | null;
24
25
 
25
26
  type RoleGuardProps = {
26
27
  children: React__default.ReactNode;
27
28
  allowedRoles: string[];
28
29
  redirectTo?: string;
30
+ fallback?: React__default.ReactNode;
29
31
  };
30
- declare const RoleGuard: ({ children, allowedRoles, redirectTo, }: RoleGuardProps) => react_jsx_runtime.JSX.Element | null;
32
+ declare const RoleGuard: ({ children, allowedRoles, redirectTo, fallback, }: RoleGuardProps) => react_jsx_runtime.JSX.Element | null;
31
33
 
32
- type User = any;
33
- declare function createAppAuth(storage?: 'localStorage' | 'sessionStorage' | 'cookie'): {
34
+ type User = Record<string, any>;
35
+ /**
36
+ * Factory to create isolated auth instance
37
+ */
38
+ declare function createAppAuth(storage?: 'localStorage' | 'sessionStorage' | 'cookie', tokenKey?: string): {
34
39
  AuthProvider: ({ children }: {
35
40
  children: React.ReactNode;
36
41
  }) => react_jsx_runtime.JSX.Element;
37
42
  useAuth: () => AuthContextType<User$1>;
43
+ tokenKey: string;
38
44
  };
39
45
 
40
46
  export { AuthGuard, RoleGuard, type User, createAppAuth };
package/dist/index.d.ts CHANGED
@@ -6,35 +6,41 @@ type User$1 = {
6
6
  [key: string]: any;
7
7
  };
8
8
 
9
- type AuthContextType<UserType extends User$1> = {
9
+ type AuthContextType<UserType extends User$1 = User$1> = {
10
10
  user: UserType | null;
11
- login: (tokens: Record<string, string>, userData: UserType) => void;
11
+ role: string | null;
12
+ isAuthenticated: boolean;
13
+ login: (tokens: Record<string, string>, userData: UserType, role: string) => void;
12
14
  logout: () => void;
13
- setUser: (user: UserType) => void;
14
- loading: boolean;
15
- error: Error | null;
15
+ setUser: (user: UserType | null) => void;
16
+ tokenKey: string;
16
17
  };
17
18
 
18
19
  type AuthGuardProps = {
19
20
  children: React__default.ReactNode;
20
21
  redirectTo?: string;
21
- tokenKey?: string;
22
+ fallback?: React__default.ReactNode;
22
23
  };
23
- declare const AuthGuard: ({ children, redirectTo, tokenKey, }: AuthGuardProps) => react_jsx_runtime.JSX.Element;
24
+ declare const AuthGuard: ({ children, redirectTo, fallback, }: AuthGuardProps) => react_jsx_runtime.JSX.Element | null;
24
25
 
25
26
  type RoleGuardProps = {
26
27
  children: React__default.ReactNode;
27
28
  allowedRoles: string[];
28
29
  redirectTo?: string;
30
+ fallback?: React__default.ReactNode;
29
31
  };
30
- declare const RoleGuard: ({ children, allowedRoles, redirectTo, }: RoleGuardProps) => react_jsx_runtime.JSX.Element | null;
32
+ declare const RoleGuard: ({ children, allowedRoles, redirectTo, fallback, }: RoleGuardProps) => react_jsx_runtime.JSX.Element | null;
31
33
 
32
- type User = any;
33
- declare function createAppAuth(storage?: 'localStorage' | 'sessionStorage' | 'cookie'): {
34
+ type User = Record<string, any>;
35
+ /**
36
+ * Factory to create isolated auth instance
37
+ */
38
+ declare function createAppAuth(storage?: 'localStorage' | 'sessionStorage' | 'cookie', tokenKey?: string): {
34
39
  AuthProvider: ({ children }: {
35
40
  children: React.ReactNode;
36
41
  }) => react_jsx_runtime.JSX.Element;
37
42
  useAuth: () => AuthContextType<User$1>;
43
+ tokenKey: string;
38
44
  };
39
45
 
40
46
  export { AuthGuard, RoleGuard, type User, createAppAuth };
package/dist/index.js CHANGED
@@ -35,18 +35,20 @@ var import_react_token_manager = require("react-token-manager");
35
35
  var import_zustand = require("zustand");
36
36
  var useAuthStore = (0, import_zustand.create)((set) => ({
37
37
  user: null,
38
+ role: null,
38
39
  isAuthenticated: false,
39
40
  isAuthChecked: false,
40
41
  loading: true,
41
- // start as loading
42
42
  error: null,
43
43
  setUser: (user) => set({ user }),
44
+ setRole: (role) => set({ role }),
44
45
  setAuth: (isAuth) => set({ isAuthenticated: isAuth }),
45
46
  setAuthChecked: (checked) => set({ isAuthChecked: checked }),
46
47
  setLoading: (loading) => set({ loading }),
47
48
  setError: (err) => set({ error: err }),
48
49
  resetAuth: () => set({
49
50
  user: null,
51
+ role: null,
50
52
  isAuthenticated: false,
51
53
  isAuthChecked: false,
52
54
  loading: false,
@@ -56,99 +58,104 @@ var useAuthStore = (0, import_zustand.create)((set) => ({
56
58
 
57
59
  // src/AuthProvider.tsx
58
60
  var import_jsx_runtime = require("react/jsx-runtime");
59
- function createAuthContext(options = {}) {
60
- const { storage = "cookie" } = options;
61
- const AuthContext = (0, import_react.createContext)(
62
- void 0
63
- );
61
+ function createAuthContext(option) {
62
+ const AuthContext = (0, import_react.createContext)(void 0);
64
63
  const AuthProvider = ({ children }) => {
65
- const configuredRef = (0, import_react.useRef)(false);
66
- const manager = (0, import_react_token_manager.useTokenManager)();
67
- const { user, loading, error, setUser, setLoading, setError, resetAuth } = useAuthStore();
64
+ const storage = option?.storage ?? "cookie";
65
+ const tokenKey = option?.tokenKey ?? "access_token";
68
66
  (0, import_react.useEffect)(() => {
69
- if (!configuredRef.current) {
70
- (0, import_react_token_manager.configureTokenManager)({ storage });
71
- configuredRef.current = true;
72
- }
67
+ (0, import_react_token_manager.configureTokenManager)({ storage });
73
68
  }, [storage]);
69
+ const manager = (0, import_react_token_manager.useTokenManager)();
70
+ const {
71
+ user,
72
+ role,
73
+ setUser,
74
+ setRole,
75
+ resetAuth,
76
+ isAuthenticated,
77
+ setAuth,
78
+ setAuthChecked
79
+ } = useAuthStore();
74
80
  (0, import_react.useEffect)(() => {
75
- try {
76
- const savedUser = manager.getSingleToken("user");
77
- if (savedUser) {
78
- setUser(JSON.parse(savedUser));
81
+ const storedUser = manager.getSingleToken("user");
82
+ const token = manager.getSingleToken(tokenKey);
83
+ if (storedUser && token && !manager.isExpired(token)) {
84
+ try {
85
+ const parsedUser = JSON.parse(storedUser);
86
+ setUser(parsedUser);
87
+ setRole(parsedUser?.role ?? null);
88
+ setAuth(true);
89
+ } catch {
90
+ resetAuth();
79
91
  }
80
- } catch (err) {
81
- setError(err instanceof Error ? err : new Error(String(err)));
82
- } finally {
83
- setLoading(false);
84
- }
85
- }, [manager, setUser, setLoading, setError]);
86
- const login = (tokens, userData) => {
87
- try {
88
- manager.setTokens({ ...tokens, user: JSON.stringify(userData) });
89
- setUser(userData);
90
- } catch (err) {
91
- setError(err instanceof Error ? err : new Error(String(err)));
92
+ } else {
93
+ resetAuth();
92
94
  }
95
+ setAuthChecked(true);
96
+ }, [tokenKey]);
97
+ const login = (tokens, userData, role2) => {
98
+ const tokenValue = tokens[tokenKey] ?? tokens["access_token"] ?? Object.values(tokens)[0];
99
+ manager.setTokens({
100
+ ...tokens,
101
+ [tokenKey]: tokenValue,
102
+ user: JSON.stringify(userData)
103
+ });
104
+ setUser(userData);
105
+ setRole(role2);
106
+ setAuth(true);
93
107
  };
94
108
  const logout = () => {
95
- try {
96
- manager.clearTokens();
97
- resetAuth();
98
- } catch (err) {
99
- setError(err instanceof Error ? err : new Error(String(err)));
100
- }
109
+ manager.clearTokens();
110
+ resetAuth();
101
111
  };
102
112
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
103
113
  AuthContext.Provider,
104
114
  {
105
115
  value: {
106
116
  user,
117
+ role,
118
+ isAuthenticated,
107
119
  login,
108
120
  logout,
109
121
  setUser: (u) => setUser(u),
110
- loading,
111
- error
122
+ tokenKey
112
123
  },
113
124
  children
114
125
  }
115
126
  );
116
127
  };
117
128
  const useAuth = () => {
118
- const ctx = (0, import_react.useContext)(AuthContext);
119
- if (!ctx) throw new Error("useAuth must be used inside AuthProvider");
120
- return ctx;
129
+ const context = (0, import_react.useContext)(AuthContext);
130
+ if (!context) throw new Error("useAuth must be used within AuthProvider");
131
+ return context;
132
+ };
133
+ return {
134
+ AuthProvider,
135
+ useAuth,
136
+ tokenKey: option?.tokenKey ?? "access_token"
121
137
  };
122
- return { AuthProvider, useAuth };
123
138
  }
124
139
 
125
140
  // src/AuthGuard.tsx
126
141
  var import_react2 = require("react");
127
142
  var import_navigation = require("next/navigation");
128
- var import_react_token_manager2 = require("react-token-manager");
129
143
  var import_jsx_runtime2 = require("react/jsx-runtime");
130
144
  var AuthGuard = ({
131
145
  children,
132
146
  redirectTo = "/login",
133
- tokenKey = "access_token"
147
+ fallback = null
134
148
  }) => {
135
149
  const router = (0, import_navigation.useRouter)();
136
- const manager = (0, import_react_token_manager2.useTokenManager)();
137
- const setAuth = useAuthStore((state) => state.setAuth);
138
- const setAuthChecked = useAuthStore((state) => state.setAuthChecked);
139
- const [loading, setLoading] = (0, import_react2.useState)(true);
150
+ const { isAuthenticated, isAuthChecked } = useAuthStore();
140
151
  (0, import_react2.useEffect)(() => {
141
- const token = manager.getSingleToken(tokenKey);
142
- const isValid = Boolean(token) && !manager.isExpired(token);
143
- setAuth(Boolean(isValid));
144
- setAuthChecked(true);
145
- if (!isValid) {
152
+ if (!isAuthChecked) return;
153
+ if (!isAuthenticated) {
146
154
  router.replace(redirectTo);
147
- } else {
148
- setLoading(false);
149
155
  }
150
- }, [manager, tokenKey, redirectTo, router, setAuth, setAuthChecked]);
151
- if (loading) return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { children: "Loading..." });
156
+ }, [isAuthenticated, isAuthChecked, redirectTo, router]);
157
+ if (!isAuthChecked) return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_jsx_runtime2.Fragment, { children: fallback });
158
+ if (!isAuthenticated) return null;
152
159
  return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_jsx_runtime2.Fragment, { children });
153
160
  };
154
161
  var AuthGuard_default = AuthGuard;
@@ -160,32 +167,26 @@ var import_jsx_runtime3 = require("react/jsx-runtime");
160
167
  var RoleGuard = ({
161
168
  children,
162
169
  allowedRoles,
163
- redirectTo = "/unauthorized"
170
+ redirectTo = "/unauthorized",
171
+ fallback = null
164
172
  }) => {
165
173
  const router = (0, import_navigation2.useRouter)();
166
- const { user, isAuthChecked, isAuthenticated } = useAuthStore();
174
+ const { role, isAuthChecked } = useAuthStore();
167
175
  (0, import_react3.useEffect)(() => {
168
176
  if (!isAuthChecked) return;
169
- if (!isAuthenticated || !user) {
170
- router.replace("/login");
171
- return;
172
- }
173
- const role2 = user?.role;
174
- if (!allowedRoles.includes(role2)) {
177
+ if (!role || !allowedRoles.includes(role)) {
175
178
  router.replace(redirectTo);
176
179
  }
177
- }, [isAuthChecked, isAuthenticated, user, allowedRoles, redirectTo, router]);
178
- if (!isAuthChecked) return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { children: "Loading..." });
179
- if (!isAuthenticated || !user) return null;
180
- const role = user?.role;
181
- if (!allowedRoles.includes(role)) return null;
180
+ }, [role, isAuthChecked, allowedRoles, redirectTo, router]);
181
+ if (!isAuthChecked) return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_jsx_runtime3.Fragment, { children: fallback });
182
+ if (!role || !allowedRoles.includes(role)) return null;
182
183
  return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_jsx_runtime3.Fragment, { children });
183
184
  };
184
185
  var RoleGuard_default = RoleGuard;
185
186
 
186
187
  // src/index.ts
187
- function createAppAuth(storage = "cookie") {
188
- return createAuthContext({ storage });
188
+ function createAppAuth(storage = "cookie", tokenKey = "access_token") {
189
+ return createAuthContext({ storage, tokenKey });
189
190
  }
190
191
  // Annotate the CommonJS export names for ESM import in node:
191
192
  0 && (module.exports = {
package/dist/index.mjs CHANGED
@@ -1,30 +1,27 @@
1
1
  "use client";
2
2
 
3
3
  // src/AuthProvider.tsx
4
- import {
5
- createContext,
6
- useContext,
7
- useEffect,
8
- useRef
9
- } from "react";
4
+ import { createContext, useContext, useEffect } from "react";
10
5
  import { configureTokenManager, useTokenManager } from "react-token-manager";
11
6
 
12
7
  // store/useGuardStore.ts
13
8
  import { create } from "zustand";
14
9
  var useAuthStore = create((set) => ({
15
10
  user: null,
11
+ role: null,
16
12
  isAuthenticated: false,
17
13
  isAuthChecked: false,
18
14
  loading: true,
19
- // start as loading
20
15
  error: null,
21
16
  setUser: (user) => set({ user }),
17
+ setRole: (role) => set({ role }),
22
18
  setAuth: (isAuth) => set({ isAuthenticated: isAuth }),
23
19
  setAuthChecked: (checked) => set({ isAuthChecked: checked }),
24
20
  setLoading: (loading) => set({ loading }),
25
21
  setError: (err) => set({ error: err }),
26
22
  resetAuth: () => set({
27
23
  user: null,
24
+ role: null,
28
25
  isAuthenticated: false,
29
26
  isAuthChecked: false,
30
27
  loading: false,
@@ -34,99 +31,104 @@ var useAuthStore = create((set) => ({
34
31
 
35
32
  // src/AuthProvider.tsx
36
33
  import { jsx } from "react/jsx-runtime";
37
- function createAuthContext(options = {}) {
38
- const { storage = "cookie" } = options;
39
- const AuthContext = createContext(
40
- void 0
41
- );
34
+ function createAuthContext(option) {
35
+ const AuthContext = createContext(void 0);
42
36
  const AuthProvider = ({ children }) => {
43
- const configuredRef = useRef(false);
44
- const manager = useTokenManager();
45
- const { user, loading, error, setUser, setLoading, setError, resetAuth } = useAuthStore();
37
+ const storage = option?.storage ?? "cookie";
38
+ const tokenKey = option?.tokenKey ?? "access_token";
46
39
  useEffect(() => {
47
- if (!configuredRef.current) {
48
- configureTokenManager({ storage });
49
- configuredRef.current = true;
50
- }
40
+ configureTokenManager({ storage });
51
41
  }, [storage]);
42
+ const manager = useTokenManager();
43
+ const {
44
+ user,
45
+ role,
46
+ setUser,
47
+ setRole,
48
+ resetAuth,
49
+ isAuthenticated,
50
+ setAuth,
51
+ setAuthChecked
52
+ } = useAuthStore();
52
53
  useEffect(() => {
53
- try {
54
- const savedUser = manager.getSingleToken("user");
55
- if (savedUser) {
56
- setUser(JSON.parse(savedUser));
54
+ const storedUser = manager.getSingleToken("user");
55
+ const token = manager.getSingleToken(tokenKey);
56
+ if (storedUser && token && !manager.isExpired(token)) {
57
+ try {
58
+ const parsedUser = JSON.parse(storedUser);
59
+ setUser(parsedUser);
60
+ setRole(parsedUser?.role ?? null);
61
+ setAuth(true);
62
+ } catch {
63
+ resetAuth();
57
64
  }
58
- } catch (err) {
59
- setError(err instanceof Error ? err : new Error(String(err)));
60
- } finally {
61
- setLoading(false);
62
- }
63
- }, [manager, setUser, setLoading, setError]);
64
- const login = (tokens, userData) => {
65
- try {
66
- manager.setTokens({ ...tokens, user: JSON.stringify(userData) });
67
- setUser(userData);
68
- } catch (err) {
69
- setError(err instanceof Error ? err : new Error(String(err)));
65
+ } else {
66
+ resetAuth();
70
67
  }
68
+ setAuthChecked(true);
69
+ }, [tokenKey]);
70
+ const login = (tokens, userData, role2) => {
71
+ const tokenValue = tokens[tokenKey] ?? tokens["access_token"] ?? Object.values(tokens)[0];
72
+ manager.setTokens({
73
+ ...tokens,
74
+ [tokenKey]: tokenValue,
75
+ user: JSON.stringify(userData)
76
+ });
77
+ setUser(userData);
78
+ setRole(role2);
79
+ setAuth(true);
71
80
  };
72
81
  const logout = () => {
73
- try {
74
- manager.clearTokens();
75
- resetAuth();
76
- } catch (err) {
77
- setError(err instanceof Error ? err : new Error(String(err)));
78
- }
82
+ manager.clearTokens();
83
+ resetAuth();
79
84
  };
80
85
  return /* @__PURE__ */ jsx(
81
86
  AuthContext.Provider,
82
87
  {
83
88
  value: {
84
89
  user,
90
+ role,
91
+ isAuthenticated,
85
92
  login,
86
93
  logout,
87
94
  setUser: (u) => setUser(u),
88
- loading,
89
- error
95
+ tokenKey
90
96
  },
91
97
  children
92
98
  }
93
99
  );
94
100
  };
95
101
  const useAuth = () => {
96
- const ctx = useContext(AuthContext);
97
- if (!ctx) throw new Error("useAuth must be used inside AuthProvider");
98
- return ctx;
102
+ const context = useContext(AuthContext);
103
+ if (!context) throw new Error("useAuth must be used within AuthProvider");
104
+ return context;
105
+ };
106
+ return {
107
+ AuthProvider,
108
+ useAuth,
109
+ tokenKey: option?.tokenKey ?? "access_token"
99
110
  };
100
- return { AuthProvider, useAuth };
101
111
  }
102
112
 
103
113
  // src/AuthGuard.tsx
104
- import { useEffect as useEffect2, useState } from "react";
114
+ import { useEffect as useEffect2 } from "react";
105
115
  import { useRouter } from "next/navigation";
106
- import { useTokenManager as useTokenManager2 } from "react-token-manager";
107
116
  import { Fragment, jsx as jsx2 } from "react/jsx-runtime";
108
117
  var AuthGuard = ({
109
118
  children,
110
119
  redirectTo = "/login",
111
- tokenKey = "access_token"
120
+ fallback = null
112
121
  }) => {
113
122
  const router = useRouter();
114
- const manager = useTokenManager2();
115
- const setAuth = useAuthStore((state) => state.setAuth);
116
- const setAuthChecked = useAuthStore((state) => state.setAuthChecked);
117
- const [loading, setLoading] = useState(true);
123
+ const { isAuthenticated, isAuthChecked } = useAuthStore();
118
124
  useEffect2(() => {
119
- const token = manager.getSingleToken(tokenKey);
120
- const isValid = Boolean(token) && !manager.isExpired(token);
121
- setAuth(Boolean(isValid));
122
- setAuthChecked(true);
123
- if (!isValid) {
125
+ if (!isAuthChecked) return;
126
+ if (!isAuthenticated) {
124
127
  router.replace(redirectTo);
125
- } else {
126
- setLoading(false);
127
128
  }
128
- }, [manager, tokenKey, redirectTo, router, setAuth, setAuthChecked]);
129
- if (loading) return /* @__PURE__ */ jsx2("div", { children: "Loading..." });
129
+ }, [isAuthenticated, isAuthChecked, redirectTo, router]);
130
+ if (!isAuthChecked) return /* @__PURE__ */ jsx2(Fragment, { children: fallback });
131
+ if (!isAuthenticated) return null;
130
132
  return /* @__PURE__ */ jsx2(Fragment, { children });
131
133
  };
132
134
  var AuthGuard_default = AuthGuard;
@@ -138,32 +140,26 @@ import { Fragment as Fragment2, jsx as jsx3 } from "react/jsx-runtime";
138
140
  var RoleGuard = ({
139
141
  children,
140
142
  allowedRoles,
141
- redirectTo = "/unauthorized"
143
+ redirectTo = "/unauthorized",
144
+ fallback = null
142
145
  }) => {
143
146
  const router = useRouter2();
144
- const { user, isAuthChecked, isAuthenticated } = useAuthStore();
147
+ const { role, isAuthChecked } = useAuthStore();
145
148
  useEffect3(() => {
146
149
  if (!isAuthChecked) return;
147
- if (!isAuthenticated || !user) {
148
- router.replace("/login");
149
- return;
150
- }
151
- const role2 = user?.role;
152
- if (!allowedRoles.includes(role2)) {
150
+ if (!role || !allowedRoles.includes(role)) {
153
151
  router.replace(redirectTo);
154
152
  }
155
- }, [isAuthChecked, isAuthenticated, user, allowedRoles, redirectTo, router]);
156
- if (!isAuthChecked) return /* @__PURE__ */ jsx3("div", { children: "Loading..." });
157
- if (!isAuthenticated || !user) return null;
158
- const role = user?.role;
159
- if (!allowedRoles.includes(role)) return null;
153
+ }, [role, isAuthChecked, allowedRoles, redirectTo, router]);
154
+ if (!isAuthChecked) return /* @__PURE__ */ jsx3(Fragment2, { children: fallback });
155
+ if (!role || !allowedRoles.includes(role)) return null;
160
156
  return /* @__PURE__ */ jsx3(Fragment2, { children });
161
157
  };
162
158
  var RoleGuard_default = RoleGuard;
163
159
 
164
160
  // src/index.ts
165
- function createAppAuth(storage = "cookie") {
166
- return createAuthContext({ storage });
161
+ function createAppAuth(storage = "cookie", tokenKey = "access_token") {
162
+ return createAuthContext({ storage, tokenKey });
167
163
  }
168
164
  export {
169
165
  AuthGuard_default as AuthGuard,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nextauthz",
3
- "version": "1.2.1",
3
+ "version": "1.2.4",
4
4
  "description": "",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",
package/readme.md CHANGED
@@ -31,21 +31,17 @@ Wrap your application with AuthProvider to provide global auth state (user, load
31
31
  import { createAppAuth } from 'nextauthz'
32
32
 
33
33
  // Choose storage type
34
- const { AuthProvider } = createAppAuth('localStorage')
35
- // Options: 'localStorage' | 'sessionStorage' | 'cookie'
36
-
34
+ const { AuthProvider, useAuth } = createAppAuth('cookie', 'access_token')
37
35
 
38
- If no storage is passed, default is:
39
-
40
- createAppAuth() // defaults to 'cookie'
41
36
 
37
+ // Options: 'localStorage' | 'sessionStorage' | 'cookie'
38
+ // Defaults to 'cookie' if no storage is passed
42
39
 
43
40
  function App({ children }: { children: React.ReactNode }) {
44
41
  return <AuthProvider>{children}</AuthProvider>
45
42
  }
46
43
 
47
44
  export default App
48
-
49
45
  ```
50
46
 
51
47
  ## Hook: useAuth
@@ -53,25 +49,25 @@ export default App
53
49
  ```bash
54
50
  import { useAuth } from 'nextauthz'
55
51
 
56
- const { user, login, logout, setUser, loading, error } = useAuth()
52
+ const { user, role, login, logout, setUser, isAuthenticated } = useAuth()
57
53
  ```
58
54
 
59
55
  Available Properties
60
56
 
61
- | Name | Type | Description | |
62
- | --------- | ------------------------ | -------------------------------------------- | -------------------------- |
63
- | `user` | `User | null` | Current authenticated user |
64
- | `login` | `(tokens, user) => void` | Login and save tokens + user | |
65
- | `logout` | `() => void` | Clear auth tokens and reset state | |
66
- | `setUser` | `(user) => void` | Update user state manually | |
67
- | `loading` | `boolean` | True while restoring user from token storage | |
68
- | `error` | `Error | null` | Last auth error |
57
+ | Name | Type | Description | |
58
+ | ----------------- | ---------------------------------- | -------------------------------------- | -------------------------- |
59
+ | `user` | `User | null` | Current authenticated user |
60
+ | `role` | `string | null` | User role |
61
+ | `isAuthenticated` | `boolean` | True if the user is logged in | |
62
+ | `login` | `(tokens, userData, role) => void` | Logs in a user and saves tokens + role | |
63
+ | `logout` | `() => void` | Clears auth tokens and resets state | |
64
+ | `setUser` | `(user: User | null) => void` | Update user state manually |
69
65
 
70
66
 
71
67
  Example: Logging in
72
68
 
73
69
  ```bash
74
- const handleLogin = async () => {
70
+ const handleLogin = () => {
75
71
  const tokens = {
76
72
  access_token: 'abc123',
77
73
  refresh_token: 'xyz789',
@@ -83,9 +79,8 @@ const handleLogin = async () => {
83
79
  role: 'admin',
84
80
  }
85
81
 
86
- login(tokens, user)
82
+ login(tokens, user, user.role)
87
83
  }
88
-
89
84
  ```
90
85
 
91
86
  ## AuthGuard
@@ -96,13 +91,11 @@ Protect routes/pages to ensure only authenticated users can access them.
96
91
 
97
92
  Props
98
93
 
99
- | Name | Type | Default | Description |
100
- | -------------- | ----------------------- | -------------- | -------------------------------------- |
101
- | `children` | `ReactNode` | required | Components to render if authenticated |
102
- | `redirectTo` | `string` | `/login` | Page to redirect unauthenticated users |
103
- | `tokenKey` | `string` | `access_token` | Token key to validate |
104
- | `refreshToken` | `() => Promise<string>` | optional | Function to refresh expired token |
105
-
94
+ | Name | Type | Default | Description |
95
+ | ------------ | ----------- | -------- | -------------------------------------- |
96
+ | `children` | `ReactNode` | required | Components to render if authenticated |
97
+ | `redirectTo` | `string` | `/login` | Page to redirect unauthenticated users |
98
+ | `fallback` | `ReactNode` | `null` | Optional loading or fallback UI |
106
99
 
107
100
  ## Usage Example
108
101
 
@@ -111,7 +104,7 @@ import AuthGuard from 'nextauthz'
111
104
 
112
105
  function DashboardPage() {
113
106
  return (
114
- <AuthGuard>
107
+ <AuthGuard fallback={<p>Loading...</p>}>
115
108
  <h1>Dashboard</h1>
116
109
  </AuthGuard>
117
110
  )
@@ -121,37 +114,45 @@ Behavior:
121
114
 
122
115
  ** Redirects to /login if the user is not authenticated.
123
116
 
124
- ** Supports token refresh with refreshToken callback.
125
-
126
117
 
127
118
  ## RoleGuard
128
119
 
129
120
  Purpose
130
121
 
131
- Restrict access to specific roles after authentication.
122
+ RoleGuard restricts access to certain pages or components based on a user’s role. It assumes authentication is already handled (e.g., via AuthGuard or useAuth) and only checks whether the user’s role is allowed to access the content.
132
123
 
133
- | Name | Type | Default | Description |
134
- | -------------- | ----------- | --------------- | -------------------------------------------- |
135
- | `children` | `ReactNode` | required | Components to render if user role is allowed |
136
- | `allowedRoles` | `string[]` | required | Roles allowed to access this page |
137
- | `redirectTo` | `string` | `/unauthorized` | Redirect page for unauthorized roles |
124
+ | Name | Type | Default | Description |
125
+ | -------------- | ----------- | --------------- | ------------------------------------------------------------- |
126
+ | `children` | `ReactNode` | required | Components to render if the user role is allowed |
127
+ | `allowedRoles` | `string[]` | required | List of roles allowed to access this page |
128
+ | `redirectTo` | `string` | `/unauthorized` | Redirect page for unauthorized roles |
129
+ | `fallback` | `ReactNode` | `null` | Optional loading or fallback UI displayed while checking role |
138
130
 
139
131
 
140
132
  Usage Example
141
133
 
142
134
  ```bash
143
- import RoleGuard from '@/auth/RoleGuard'
144
- import { useAuth } from '@/auth'
135
+ import RoleGuard from 'nextauthz'
136
+ import { useAuth } from 'nextauthz'
145
137
 
146
138
  function AdminPage() {
139
+ const { user } = useAuth()
140
+
147
141
  return (
148
- <RoleGuard allowedRoles={['admin']}>
142
+ <RoleGuard allowedRoles={['admin']} fallback={<p>Checking permissions...</p>}>
149
143
  <h1>Admin Dashboard</h1>
144
+ <p>Welcome, {user?.name}</p>
150
145
  </RoleGuard>
151
146
  )
152
147
  }
153
148
  ```
154
149
 
150
+ Behavior:
151
+
152
+ ** RoleGuard will show the fallback UI while the authentication state is loading (isAuthChecked is false).
153
+ **
154
+
155
+
155
156
  ## Storage Options
156
157
 
157
158
  You can configure token storage:
package/src/AuthGuard.tsx CHANGED
@@ -1,42 +1,35 @@
1
1
  'use client'
2
2
 
3
- import React, { useEffect, useState } from 'react'
3
+ import React, { useEffect } from 'react'
4
4
  import { useRouter } from 'next/navigation'
5
- import { useTokenManager } from 'react-token-manager'
6
5
  import { useAuthStore } from '../store/useGuardStore'
7
6
 
8
7
  type AuthGuardProps = {
9
8
  children: React.ReactNode
10
9
  redirectTo?: string
11
- tokenKey?: string
10
+ fallback?: React.ReactNode
12
11
  }
13
12
 
14
13
  const AuthGuard = ({
15
14
  children,
16
15
  redirectTo = '/login',
17
- tokenKey = 'access_token',
16
+ fallback = null,
18
17
  }: AuthGuardProps) => {
19
18
  const router = useRouter()
20
- const manager = useTokenManager()
21
- const setAuth = useAuthStore((state) => state.setAuth)
22
- const setAuthChecked = useAuthStore((state) => state.setAuthChecked)
23
- const [loading, setLoading] = useState(true)
24
19
 
25
- useEffect(() => {
26
- const token = manager.getSingleToken(tokenKey)
27
- const isValid = Boolean(token) && !manager.isExpired(token as any)
20
+ const { isAuthenticated, isAuthChecked } = useAuthStore()
28
21
 
29
- setAuth(Boolean(isValid))
30
- setAuthChecked(true)
22
+ useEffect(() => {
23
+ if (!isAuthChecked) return
31
24
 
32
- if (!isValid) {
25
+ if (!isAuthenticated) {
33
26
  router.replace(redirectTo)
34
- } else {
35
- setLoading(false)
36
27
  }
37
- }, [manager, tokenKey, redirectTo, router, setAuth, setAuthChecked])
28
+ }, [isAuthenticated, isAuthChecked, redirectTo, router])
29
+
30
+ if (!isAuthChecked) return <>{fallback}</>
38
31
 
39
- if (loading) return <div>Loading...</div>
32
+ if (!isAuthenticated) return null
40
33
 
41
34
  return <>{children}</>
42
35
  }
@@ -1,93 +1,117 @@
1
1
  'use client'
2
2
 
3
- import React, {
4
- createContext,
5
- useContext,
6
- ReactNode,
7
- useEffect,
8
- useRef,
9
- } from 'react'
3
+ import React, { createContext, useContext, ReactNode, useEffect } from 'react'
10
4
  import { configureTokenManager, useTokenManager } from 'react-token-manager'
11
5
  import { useAuthStore, User } from '../store/useGuardStore'
12
6
 
13
- export type AuthContextType<UserType extends User> = {
7
+ /* ---------------------------------- */
8
+ /* Types */
9
+ /* ---------------------------------- */
10
+
11
+ export type AuthContextType<UserType extends User = User> = {
14
12
  user: UserType | null
15
- login: (tokens: Record<string, string>, userData: UserType) => void
13
+ role: string | null
14
+ isAuthenticated: boolean
15
+ login: (tokens: Record<string, string>, userData: UserType, role: string) => void
16
16
  logout: () => void
17
- setUser: (user: UserType) => void
18
- loading: boolean
19
- error: Error | null
17
+ setUser: (user: UserType | null) => void
18
+ tokenKey: string
20
19
  }
21
20
 
22
- type CreateAuthOptions = {
21
+ /* ---------------------------------- */
22
+ /* Factory */
23
+ /* ---------------------------------- */
24
+
25
+ export function createAuthContext<UserType extends User = User>(option?: {
23
26
  storage?: 'localStorage' | 'sessionStorage' | 'cookie'
24
- }
27
+ tokenKey?: string
28
+ }) {
29
+ const AuthContext = createContext<AuthContextType<UserType> | undefined>(undefined)
25
30
 
26
- export function createAuthContext<UserType extends User = User>(
27
- options: CreateAuthOptions = {}
28
- ) {
29
- const { storage = 'cookie' } = options
31
+ const AuthProvider = ({ children }: { children: ReactNode }) => {
32
+ const storage = option?.storage ?? 'cookie'
33
+ const tokenKey = option?.tokenKey ?? 'access_token'
30
34
 
31
- const AuthContext = createContext<AuthContextType<UserType> | undefined>(
32
- undefined
33
- )
35
+ useEffect(() => {
36
+ configureTokenManager({ storage })
37
+ }, [storage])
34
38
 
35
- const AuthProvider = ({ children }: { children: ReactNode }) => {
36
- const configuredRef = useRef(false)
37
39
  const manager = useTokenManager()
38
40
 
39
- const { user, loading, error, setUser, setLoading, setError, resetAuth } =
40
- useAuthStore()
41
+ const {
42
+ user,
43
+ role,
44
+ setUser,
45
+ setRole,
46
+ resetAuth,
47
+ isAuthenticated,
48
+ setAuth,
49
+ setAuthChecked,
50
+ } = useAuthStore()
41
51
 
42
- // Configure token manager once
43
- useEffect(() => {
44
- if (!configuredRef.current) {
45
- configureTokenManager({ storage })
46
- configuredRef.current = true
47
- }
48
- }, [storage])
52
+ /* ---------------------------------- */
53
+ /* Hydrate user from storage */
54
+ /* ---------------------------------- */
49
55
 
50
- // Restore user from token storage
51
56
  useEffect(() => {
52
- try {
53
- const savedUser = manager.getSingleToken('user')
54
- if (savedUser) {
55
- setUser(JSON.parse(savedUser) as UserType)
57
+ const storedUser = manager.getSingleToken('user')
58
+ const token = manager.getSingleToken(tokenKey)
59
+
60
+ if (storedUser && token && !manager.isExpired(token)) {
61
+ try {
62
+ const parsedUser = JSON.parse(storedUser) as UserType
63
+ setUser(parsedUser)
64
+ setRole((parsedUser as any)?.role ?? null)
65
+ setAuth(true)
66
+ } catch {
67
+ resetAuth()
56
68
  }
57
- } catch (err) {
58
- setError(err instanceof Error ? err : new Error(String(err)))
59
- } finally {
60
- setLoading(false)
61
- }
62
- }, [manager, setUser, setLoading, setError])
63
-
64
- const login = (tokens: Record<string, string>, userData: UserType) => {
65
- try {
66
- manager.setTokens({ ...tokens, user: JSON.stringify(userData) })
67
- setUser(userData)
68
- } catch (err) {
69
- setError(err instanceof Error ? err : new Error(String(err)))
69
+ } else {
70
+ resetAuth()
70
71
  }
72
+
73
+ setAuthChecked(true)
74
+ // eslint-disable-next-line react-hooks/exhaustive-deps
75
+ }, [tokenKey])
76
+
77
+ /* ---------------------------------- */
78
+ /* Login */
79
+ /* ---------------------------------- */
80
+
81
+ const login = (tokens: Record<string, string>, userData: UserType, role: string) => {
82
+ // Use dynamic token key
83
+ const tokenValue = tokens[tokenKey] ?? tokens['access_token'] ?? Object.values(tokens)[0]
84
+
85
+ manager.setTokens({
86
+ ...tokens,
87
+ [tokenKey]: tokenValue,
88
+ user: JSON.stringify(userData),
89
+ })
90
+
91
+ setUser(userData)
92
+ setRole(role)
93
+ setAuth(true)
71
94
  }
72
95
 
96
+ /* ---------------------------------- */
97
+ /* Logout */
98
+ /* ---------------------------------- */
99
+
73
100
  const logout = () => {
74
- try {
75
- manager.clearTokens()
76
- resetAuth()
77
- } catch (err) {
78
- setError(err instanceof Error ? err : new Error(String(err)))
79
- }
101
+ manager.clearTokens()
102
+ resetAuth()
80
103
  }
81
104
 
82
105
  return (
83
106
  <AuthContext.Provider
84
107
  value={{
85
108
  user: user as UserType | null,
109
+ role,
110
+ isAuthenticated,
86
111
  login,
87
112
  logout,
88
- setUser: (u: UserType) => setUser(u),
89
- loading,
90
- error,
113
+ setUser: (u) => setUser(u),
114
+ tokenKey,
91
115
  }}
92
116
  >
93
117
  {children}
@@ -95,11 +119,19 @@ export function createAuthContext<UserType extends User = User>(
95
119
  )
96
120
  }
97
121
 
98
- const useAuth = (): AuthContextType<UserType> => {
99
- const ctx = useContext(AuthContext)
100
- if (!ctx) throw new Error('useAuth must be used inside AuthProvider')
101
- return ctx
122
+ /* ---------------------------------- */
123
+ /* Hook */
124
+ /* ---------------------------------- */
125
+
126
+ const useAuth = () => {
127
+ const context = useContext(AuthContext)
128
+ if (!context) throw new Error('useAuth must be used within AuthProvider')
129
+ return context
102
130
  }
103
131
 
104
- return { AuthProvider, useAuth }
132
+ return {
133
+ AuthProvider,
134
+ useAuth,
135
+ tokenKey: option?.tokenKey ?? 'access_token',
136
+ }
105
137
  }
package/src/RoleGuard.tsx CHANGED
@@ -8,44 +8,32 @@ type RoleGuardProps = {
8
8
  children: React.ReactNode
9
9
  allowedRoles: string[]
10
10
  redirectTo?: string
11
+ fallback?: React.ReactNode
11
12
  }
12
13
 
13
14
  const RoleGuard = ({
14
15
  children,
15
16
  allowedRoles,
16
17
  redirectTo = '/unauthorized',
18
+ fallback = null,
17
19
  }: RoleGuardProps) => {
18
20
  const router = useRouter()
19
-
20
- // Get auth state from Zustand
21
- const { user, isAuthChecked, isAuthenticated } = useAuthStore()
21
+ const { role, isAuthChecked } = useAuthStore()
22
22
 
23
23
  useEffect(() => {
24
- // Wait until auth is checked
25
24
  if (!isAuthChecked) return
26
25
 
27
- // Not authenticated
28
- if (!isAuthenticated || !user) {
29
- router.replace('/login')
30
- return
31
- }
32
-
33
- // Role check
34
- const role = user?.role
35
- if (!allowedRoles.includes(role)) {
26
+ // If role not allowed, redirect
27
+ if (!role || !allowedRoles.includes(role)) {
36
28
  router.replace(redirectTo)
37
29
  }
38
- }, [isAuthChecked, isAuthenticated, user, allowedRoles, redirectTo, router])
39
-
40
- // Show loading until auth is checked
41
- if (!isAuthChecked) return <div>Loading...</div>
30
+ }, [role, isAuthChecked, allowedRoles, redirectTo, router])
42
31
 
43
- // Not authenticated
44
- if (!isAuthenticated || !user) return null
32
+ // Show fallback while loading
33
+ if (!isAuthChecked) return <>{fallback}</>
45
34
 
46
- // Role not allowed
47
- const role = (user as any)?.role
48
- if (!allowedRoles.includes(role)) return null
35
+ // Block rendering if role is not allowed
36
+ if (!role || !allowedRoles.includes(role)) return null
49
37
 
50
38
  return <>{children}</>
51
39
  }
package/src/index.ts CHANGED
@@ -5,10 +5,14 @@ import { createAuthContext } from './AuthProvider'
5
5
  export { default as AuthGuard } from './AuthGuard'
6
6
  export { default as RoleGuard } from './RoleGuard'
7
7
 
8
- export type User = any
8
+ export type User = Record<string, any>
9
9
 
10
+ /**
11
+ * Factory to create isolated auth instance
12
+ */
10
13
  export function createAppAuth(
11
- storage: 'localStorage' | 'sessionStorage' | 'cookie' = 'cookie'
14
+ storage: 'localStorage' | 'sessionStorage' | 'cookie' = 'cookie',
15
+ tokenKey: string = 'access_token' // <-- default token key
12
16
  ) {
13
- return createAuthContext({ storage })
17
+ return createAuthContext({ storage, tokenKey })
14
18
  }
@@ -7,12 +7,16 @@ export type User = {
7
7
  }
8
8
 
9
9
  type AuthState = {
10
- user: User | null // user can be null
10
+ user: User | null
11
+ role: string | null // ✅ explicit role
12
+
11
13
  isAuthenticated: boolean
12
14
  isAuthChecked: boolean
13
- loading: boolean // new loading state
15
+ loading: boolean
14
16
  error: Error | null
17
+
15
18
  setUser: (user: User | null) => void
19
+ setRole: (role: string | null) => void // ✅ new setter
16
20
  setAuth: (isAuth: boolean) => void
17
21
  setAuthChecked: (checked: boolean) => void
18
22
  setLoading: (loading: boolean) => void
@@ -22,12 +26,16 @@ type AuthState = {
22
26
 
23
27
  export const useAuthStore = create<AuthState>((set) => ({
24
28
  user: null,
29
+ role: null,
30
+
25
31
  isAuthenticated: false,
26
32
  isAuthChecked: false,
27
- loading: true, // start as loading
33
+ loading: true,
28
34
  error: null,
29
35
 
30
36
  setUser: (user) => set({ user }),
37
+ setRole: (role) => set({ role }),
38
+
31
39
  setAuth: (isAuth) => set({ isAuthenticated: isAuth }),
32
40
  setAuthChecked: (checked) => set({ isAuthChecked: checked }),
33
41
  setLoading: (loading) => set({ loading }),
@@ -36,6 +44,7 @@ export const useAuthStore = create<AuthState>((set) => ({
36
44
  resetAuth: () =>
37
45
  set({
38
46
  user: null,
47
+ role: null,
39
48
  isAuthenticated: false,
40
49
  isAuthChecked: false,
41
50
  loading: false,