NeXuS uses JWT access tokens with httpOnly refresh token cookies for secure authentication.
ββββββββββββ POST /auth/login ββββββββββββββββ
β Browser β ββββββββββββββββββββββββββΆβ Auth Service β
β βββββββββββββββββββββββββββ β β
β β accessToken (JSON body) β PostgreSQL β
β β refresh_token (cookie) β bcrypt hash β
βββββββ¬ββββββ ββββββββββββββββ
β
β Every 13 minutes:
β POST /auth/refresh
β (cookie sent automatically)
β
βΌ
New accessToken returned
Old refresh token rotated
| Token | Storage | Lifetime | Format |
|---|---|---|---|
| Access token | Memory (useState) |
15 minutes | JWT (HS256) |
| Refresh token | httpOnly cookie | 7 days | JWT (HS256) |
The AuthProvider context manages the full auth lifecycle:
import { AuthProvider, useAuth } from '@/lib/AuthContext'
// In root layout
<AuthProvider>
{children}
</AuthProvider>
// In any component
const { user, accessToken, loading, login, logout, register } = useAuth()
AuthProvider calls POST /auth/refresh using the httpOnly cookieGET /auth/me with the token to get user profileuser and accessToken in stateAn interval refreshes the access token every 13 minutes (before the 15-minute expiry):
useEffect(() => {
if (!accessToken) return
const id = setInterval(async () => {
const token = await authApi.refresh()
if (token) setAccessToken(token)
else { setUser(null); setAccessToken(null) }
}, 13 * 60 * 1000)
return () => clearInterval(id)
}, [accessToken])
If the refresh fails, the user is logged out.
Each call to /auth/refresh:
This ensures each refresh token can only be used once.
The authApi object in lib/auth.ts handles all auth HTTP requests:
authApi.login(email, password) // β { accessToken, user }
authApi.register(username, email, password) // β void
authApi.refresh() // β accessToken | null
authApi.me(accessToken) // β User | null
authApi.logout(accessToken) // β void
All requests use credentials: 'include' to send/receive httpOnly cookies.
The ProtectedRoute component wraps dashboard pages:
loading is true/login if no user after loading completeslocalStorage (XSS protection)sameSite=strict prevents CSRF attackssecure=true ensures cookies are only sent over HTTPS