Skip to content

Module Recap

Module 09 added authentication to the Bulletin frontend. Users can register, log in, and log out. The app knows who they are, protects private routes, and handles token expiry automatically.

JWT storage has trade-offs. localStorage is simple and persistent but vulnerable to XSS. Memory is secure but lost on reload. httpOnly cookies are most secure but complex for cross-domain APIs. Bulletin uses localStorage with documented trade-offs.

AuthContext distributes auth state app-wide. AuthProvider wraps the app; useAuth reads the state anywhere. Lazy useState initializers read localStorage once — no logged-out flash on reload. login() stores the token; logout() clears it.

Register and Login forms follow the standard pattern. Controlled inputs + submit handler + API call + call login() + navigate. The same error-display and submitting-state pattern applies to all forms.

ProtectedRoute redirects unauthenticated users. Wrap routes with <ProtectedRoute>. Pass state={{ from: location }} to enable post-login redirect. Redirect logged-in users away from auth pages.

The 401 interceptor handles token expiry globally. Register a logout callback with the axios interceptor — any 401 response automatically logs the user out. No per-component handling needed.

Anonymous user visits /create
→ ProtectedRoute: isAuthenticated = false
→ Navigate to /login?from=/create
User fills in credentials and submits
→ authApi.login({ username, password })
→ API returns { token, userId, username }
→ login(token, { userId, username })
→ localStorage updated, React state updated
→ navigate('/create') — back where they were
User is now on /create
→ ProtectedRoute: isAuthenticated = true
→ Page renders, form shown
→ axios interceptor attaches Authorization header
24 hours later, token expires
→ User makes a request
→ API returns 401
→ Response interceptor calls logout()
→ React state cleared, localStorage cleared
→ User redirected to /login
TermWhat it means
AuthProviderContext provider wrapping the app — holds user/token state
useAuth()Hook that reads user, token, login, logout, isAuthenticated
Lazy initializeruseState(() => localStorage.getItem(...)) — runs once on mount
ProtectedRouteRedirects unauthenticated users to /login
state={{ from: location }}Passes intended destination through navigation for post-login redirect
401 interceptorGlobal axios response handler — calls logout() on expired tokens

Module 10 — The Feed and Posts →

Module 10 builds the core UI of Bulletin: the post feed, create post form, post detail page, delete functionality, and optimistic upvote updates. This is where the backend API and frontend auth system come together to produce real app features.