nextauthz 1.0.0

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/package.json ADDED
@@ -0,0 +1,26 @@
1
+ {
2
+ "name": "nextauthz",
3
+ "version": "1.0.0",
4
+ "description": "",
5
+ "main": "dist/index.js",
6
+ "module": "dist/index.mjs",
7
+ "types": "dist/index.d.ts",
8
+ "scripts": {
9
+ "build": "tsup src/index.ts --format esm,cjs --dts",
10
+ "dev": "tsup src/index.ts --watch"
11
+ },
12
+ "keywords": [],
13
+ "author": "",
14
+ "license": "ISC",
15
+ "devDependencies": {
16
+ "@types/react": "^19.2.14",
17
+ "@types/react-dom": "^19.2.3",
18
+ "typescript": "^5.9.3"
19
+ },
20
+ "dependencies": {
21
+ "next": "^16.1.6",
22
+ "react-token-manager": "^1.0.6",
23
+ "tsup": "^8.5.1",
24
+ "zustand": "^5.0.11"
25
+ }
26
+ }
package/readme.md ADDED
@@ -0,0 +1,180 @@
1
+ ## Auth System Documentation
2
+
3
+ ## Overview
4
+
5
+ This authentication system provides:
6
+
7
+ ** Global auth state
8
+
9
+ ** Token persistence
10
+
11
+ ** Typed AuthProvider & useAuth
12
+
13
+ ** Route protection
14
+
15
+ ** Role-based access
16
+
17
+ ** Configurable storage system
18
+
19
+ ## Installation
20
+
21
+ npm install nextauthz
22
+
23
+
24
+ ## AuthProvider & useAuth
25
+
26
+ Purpose
27
+
28
+ Wrap your application with AuthProvider to provide global auth state (user, loading, error) and helper functions (login, logout, setUser).
29
+
30
+ ```bash
31
+ import { createAppAuth } from 'nextauthz'
32
+
33
+ // Choose storage type
34
+ const { AuthProvider } = createAppAuth('localStorage')
35
+ // Options: 'localStorage' | 'sessionStorage' | 'cookie'
36
+
37
+
38
+ If no storage is passed, default is:
39
+
40
+ createAppAuth() // defaults to 'cookie'
41
+
42
+
43
+ function App({ children }: { children: React.ReactNode }) {
44
+ return <AuthProvider>{children}</AuthProvider>
45
+ }
46
+
47
+ export default App
48
+
49
+ ```
50
+
51
+ ## Hook: useAuth
52
+
53
+ ```bash
54
+ import { useAuth } from 'nextauthz'
55
+
56
+ const { user, login, logout, setUser, loading, error } = useAuth()
57
+ ```
58
+
59
+ Available Properties
60
+
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 |
69
+
70
+
71
+ Example: Logging in
72
+
73
+ ```bash
74
+ const handleLogin = async () => {
75
+ const tokens = {
76
+ access_token: 'abc123',
77
+ refresh_token: 'xyz789',
78
+ }
79
+
80
+ const user = {
81
+ id: 1,
82
+ name: 'John',
83
+ role: 'admin',
84
+ }
85
+
86
+ login(tokens, user)
87
+ }
88
+
89
+ ```
90
+
91
+ ## AuthGuard
92
+
93
+ Purpose
94
+
95
+ Protect routes/pages to ensure only authenticated users can access them.
96
+
97
+ Props
98
+
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
+
106
+
107
+ ## Usage Example
108
+
109
+ ```bash
110
+ import AuthGuard from 'nextauthz'
111
+
112
+ function DashboardPage() {
113
+ return (
114
+ <AuthGuard>
115
+ <h1>Dashboard</h1>
116
+ </AuthGuard>
117
+ )
118
+ }
119
+ ```
120
+ Behavior:
121
+
122
+ ** Redirects to /login if the user is not authenticated.
123
+
124
+ ** Supports token refresh with refreshToken callback.
125
+
126
+
127
+ ## RoleGuard
128
+
129
+ Purpose
130
+
131
+ Restrict access to specific roles after authentication.
132
+
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 |
138
+
139
+
140
+ Usage Example
141
+
142
+ ```bash
143
+ import RoleGuard from '@/auth/RoleGuard'
144
+ import { useAuth } from '@/auth'
145
+
146
+ function AdminPage() {
147
+ return (
148
+ <RoleGuard allowedRoles={['admin']}>
149
+ <h1>Admin Dashboard</h1>
150
+ </RoleGuard>
151
+ )
152
+ }
153
+ ```
154
+
155
+ ## Storage Options
156
+
157
+ You can configure token storage:
158
+
159
+ | Storage Type | Description |
160
+ | ---------------- | ------------------------------- |
161
+ | `localStorage` | Persists until manually cleared |
162
+ | `sessionStorage` | Clears on tab close |
163
+ | `cookie` | Cookie-based storage (default) |
164
+
165
+
166
+ ## Why NextAuthZ?
167
+
168
+ ** Zero boilerplate
169
+
170
+ ** Fully typed
171
+
172
+ ** Storage configurable
173
+
174
+ ** Clean architecture
175
+
176
+ ** Works with Next.js App Router
177
+
178
+ ** Lightweight
179
+
180
+ ** No opinionated backend
@@ -0,0 +1,67 @@
1
+ 'use client'
2
+
3
+ import React, { useEffect, useState } from 'react'
4
+ import { useRouter } from 'next/navigation'
5
+ import { useTokenManager } from 'react-token-manager'
6
+ import { useAuthStore } from '../store/useGuardStore'
7
+
8
+ type AuthGuardProps = {
9
+ children: React.ReactNode
10
+ redirectTo?: string
11
+ tokenKey?: string
12
+ refreshToken?: () => Promise<string | null>
13
+ }
14
+
15
+ const AuthGuard = ({
16
+ children,
17
+ redirectTo = '/login',
18
+ tokenKey = 'access_token',
19
+ refreshToken,
20
+ }: AuthGuardProps) => {
21
+ const manager = useTokenManager()
22
+ const router = useRouter()
23
+ const isAuthChecked = useAuthStore((state) => state.isAuthChecked)
24
+ const isAuthenticated = useAuthStore((state) => state.isAuthenticated)
25
+ const error = useAuthStore((state) => state.error)
26
+
27
+ useEffect(() => {
28
+ const checkAuth = async () => {
29
+ try {
30
+ let token = manager.getSingleToken(tokenKey)
31
+
32
+ if (!token || manager.isExpired(token)) {
33
+ if (refreshToken) {
34
+ const newToken = await refreshToken()
35
+ if (newToken) {
36
+ manager.setTokens({ [tokenKey]: newToken })
37
+ token = newToken
38
+ }
39
+ }
40
+ }
41
+
42
+ const isValid = token && !manager.isExpired(token)
43
+ useAuthStore.getState().setAuth(Boolean(isValid))
44
+
45
+ if (!isValid) {
46
+ router.replace(redirectTo)
47
+ }
48
+ } catch (err: any) {
49
+ useAuthStore.getState().setError(err instanceof Error ? err : new Error(String(err)))
50
+ useAuthStore.getState().setAuth(false)
51
+ router.replace(redirectTo)
52
+ } finally {
53
+ useAuthStore.getState().setAuthChecked(true)
54
+ }
55
+ }
56
+
57
+ checkAuth()
58
+ }, [manager, router, redirectTo, tokenKey, refreshToken])
59
+
60
+ if (!isAuthChecked) return <div>Loading...</div>
61
+ if (error) return <div>Error: {error.message}</div>
62
+ if (!isAuthenticated) return null
63
+
64
+ return <>{children}</>
65
+ }
66
+
67
+ export default AuthGuard
@@ -0,0 +1,89 @@
1
+ 'use client'
2
+
3
+ import React, { createContext, useContext, ReactNode, useState, useEffect } from 'react'
4
+ import { configureTokenManager, useTokenManager } from 'react-token-manager'
5
+ import { useAuthStore } from '../store/useGuardStore'
6
+
7
+ export type AuthContextType<UserType> = {
8
+ user: UserType | any
9
+ login: (tokens: Record<string, string>, user: UserType) => void
10
+ logout: () => void
11
+ setUser: (user: UserType) => void
12
+ loading: boolean
13
+ error: Error | null
14
+ }
15
+
16
+ export type AuthContextOptions = {
17
+ storage?: 'localStorage' | 'sessionStorage' | 'cookie'
18
+ }
19
+
20
+ /**
21
+ * Factory to create typed AuthProvider and useAuth hook
22
+ */
23
+ export function createAuthContext<UserType>(options?: AuthContextOptions) {
24
+ const storageType = options?.storage || 'cookie'
25
+ configureTokenManager({ storage: storageType })
26
+ const manager = useTokenManager()
27
+
28
+ const AuthContext = createContext<AuthContextType<UserType> | null>(null)
29
+
30
+ const AuthProvider = ({ children }: { children: ReactNode }) => {
31
+ const [loading, setLoading] = useState(true)
32
+ const user = useAuthStore((state) => state.user)
33
+ const error = useAuthStore((state) => state.error)
34
+
35
+ // Restore user on mount
36
+ useEffect(() => {
37
+ const savedUser = manager.getSingleToken('user')
38
+ if (savedUser) {
39
+ try {
40
+ const parsedUser = JSON.parse(savedUser)
41
+ useAuthStore.getState().setUser(parsedUser)
42
+ useAuthStore.getState().setError(null)
43
+ } catch (err: any) {
44
+ useAuthStore.getState().resetAuth()
45
+ useAuthStore.getState().setError(new Error('Failed to parse saved user'))
46
+ }
47
+ }
48
+ setLoading(false)
49
+ }, [])
50
+
51
+ const setUser = (userData: any) => {
52
+ useAuthStore.getState().setUser(userData)
53
+ useAuthStore.getState().setError(null)
54
+ manager.setTokens({ user: JSON.stringify(userData) })
55
+ }
56
+
57
+ const login = (tokens: Record<string, string>, userData: UserType) => {
58
+ try {
59
+ manager.setTokens(tokens)
60
+ setUser(userData)
61
+ } catch (err: any) {
62
+ useAuthStore.getState().setError(err instanceof Error ? err : new Error(String(err)))
63
+ }
64
+ }
65
+
66
+ const logout = () => {
67
+ try {
68
+ manager.clearTokens()
69
+ useAuthStore.getState().resetAuth()
70
+ } catch (err: any) {
71
+ useAuthStore.getState().setError(err instanceof Error ? err : new Error(String(err)))
72
+ }
73
+ }
74
+
75
+ return (
76
+ <AuthContext.Provider value={{ user, login, logout, setUser, loading, error }}>
77
+ {children}
78
+ </AuthContext.Provider>
79
+ )
80
+ }
81
+
82
+ const useAuth = (): AuthContextType<UserType> => {
83
+ const ctx = useContext(AuthContext)
84
+ if (!ctx) throw new Error('useAuth must be used inside AuthProvider')
85
+ return ctx
86
+ }
87
+
88
+ return { AuthProvider, useAuth }
89
+ }
@@ -0,0 +1,37 @@
1
+ 'use client'
2
+
3
+ import React, { useEffect, useState } from 'react'
4
+ import { useRouter } from 'next/navigation'
5
+ import { useAuth } from './myAuth'
6
+
7
+ type RoleGuardProps = {
8
+ children: React.ReactNode
9
+ allowedRoles: string[]
10
+ redirectTo?: string
11
+ }
12
+
13
+ const RoleGuard = ({
14
+ children,
15
+ allowedRoles,
16
+ redirectTo = '/unauthorized',
17
+ }: RoleGuardProps) => {
18
+ const { user, loading } = useAuth()
19
+ const router = useRouter()
20
+ const [isChecking, setIsChecking] = useState(true)
21
+
22
+ useEffect(() => {
23
+ if (!user) return
24
+
25
+ const hasAccess = allowedRoles.includes(user?.role)
26
+ if (!hasAccess) {
27
+ router.replace(redirectTo)
28
+ }
29
+ setIsChecking(false)
30
+ }, [user, allowedRoles, redirectTo, router])
31
+
32
+ if (loading || !user || isChecking) return <div>Loading...</div>
33
+
34
+ return <>{children}</>
35
+ }
36
+
37
+ export default RoleGuard
package/src/index.ts ADDED
@@ -0,0 +1,16 @@
1
+ import { createAuthContext } from './AuthProvider'
2
+
3
+ export type User = {
4
+ [key: string]: any
5
+ }
6
+
7
+ // Export factory instead of fixed instance
8
+ export function createAppAuth(
9
+ storage: 'localStorage' | 'sessionStorage' | 'cookie' = 'cookie'
10
+ ) {
11
+ return createAuthContext<User>({ storage })
12
+ }
13
+
14
+ export { createAuthContext } from './AuthProvider'
15
+ export { default as AuthGuard } from './AuthGuard'
16
+ export { default as RoleGuard } from './RoleGuard'
package/src/myAuth.ts ADDED
@@ -0,0 +1,7 @@
1
+ import { createAppAuth } from "."
2
+
3
+
4
+ export type User = any
5
+
6
+ // Create typed AuthProvider and useAuth hook
7
+ export const { AuthProvider, useAuth } = createAppAuth()
@@ -0,0 +1,39 @@
1
+ 'use client'
2
+
3
+ import { create } from 'zustand'
4
+
5
+ export type User = {
6
+ [key: string]: any
7
+ }
8
+
9
+ type AuthState = {
10
+ user: User | null // user can be null
11
+ isAuthenticated: boolean
12
+ isAuthChecked: boolean
13
+ error: Error | null
14
+ setUser: (user: User | null) => void
15
+ setAuth: (isAuth: boolean) => void
16
+ setAuthChecked: (checked: boolean) => void
17
+ setError: (err: Error | null) => void
18
+ resetAuth: () => void
19
+ }
20
+
21
+ export const useAuthStore = create<AuthState>((set) => ({
22
+ user: null,
23
+ isAuthenticated: false,
24
+ isAuthChecked: false,
25
+ error: null,
26
+
27
+ setUser: (user) => set({ user }),
28
+ setAuth: (isAuth) => set({ isAuthenticated: isAuth }),
29
+ setAuthChecked: (checked) => set({ isAuthChecked: checked }),
30
+ setError: (err) => set({ error: err }),
31
+
32
+ resetAuth: () =>
33
+ set({
34
+ user: null,
35
+ isAuthenticated: false,
36
+ isAuthChecked: false,
37
+ error: null,
38
+ }),
39
+ }))
package/tsconfig.json ADDED
@@ -0,0 +1,13 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "esnext",
4
+ "lib": ["dom", "dom.iterable", "esnext"],
5
+ "module": "esnext",
6
+ "jsx": "react-jsx", // important
7
+ "strict": true,
8
+ "moduleResolution": "node",
9
+ "esModuleInterop": true,
10
+ "forceConsistentCasingInFileNames": true,
11
+ "skipLibCheck": true
12
+ }
13
+ }