UNPKG

@duongtrungnguyen/next-helper

Version:
353 lines (249 loc) 8.8 kB
### Next.js JWT Authentication Library Documentation Table of Contents ----------------- 1. [Introduction](#introduction) 2. [Installation](#installation) 3. [Core Concepts](#core-concepts) 4. [Configuration](#configuration) 5. [API Reference](#api-reference) * [Client-Side API](#client-side-api) * [Server-Side API](#server-side-api) * [Middleware](#middleware) * [HTTP Utilities](#http-utilities) Introduction ------------ This authentication library provides a complete solution for JWT-based authentication in Next.js applications. It supports both client and server components, handles token refresh automatically, and provides utilities for making authenticated API requests. ### Key Features * **JWT Authentication**: Secure authentication using JSON Web Tokens * **Automatic Token Refresh**: Seamless refresh of expired tokens * **App Router Support**: Full compatibility with Next.js App Router * **TypeScript Support**: Complete type definitions for all components * **Server Components**: Authentication utilities for React Server Components * **Middleware Integration**: Route protection at the middleware level * **HTTP Utilities**: Simplified authenticated API requests Installation ------------ 1. Configure environment variables in your `.env.local` file: ```env # Auth API Configuration NEXT_PUBLIC_API_BASE_URL= # default is: http://localhost:3001 NEXT_PUBLIC_AUTH_GLOBAL_PREFIX= # default is: "" NEXT_PUBLIC_LOGIN_ENDPOINT= # default is: /sign-in NEXT_PUBLIC_LOGOUT_ENDPOINT= # default is: /sign-out NEXT_PUBLIC_REFRESH_TOKEN_PREFIX= # default is: /refresh NEXT_PUBLIC_USER_ENDPOINT= # default is: /user NEXT_PUBLIC_ACCESS_TOKEN_PREFIX= # default is:auth.access-token NEXT_PUBLIC_REFRESH_TOKEN_PREFIX= # default is:auth.refresh-token NEXT_PUBLIC_AUTH_TOKEN_TYPE= # default is:Bearer ``` 2. Initialize the authentication system in your root layout: ```ts // app/layout.tsx import { AuthProvider } from "@duongtrungnguyen/next-helper"; import "./globals.css"; export default function RootLayout({ children, }: { children: React.ReactNode }) { return ( <html lang="en"> <body> <AuthProvider>{children}</AuthProvider> </body> </html> ); } ``` Core Concepts ------------- ### Authentication Flow 1. **Login**: User provides credentials → Server validates and returns JWT tokens → Client stores tokens in cookies 2. **API Requests**: Client retrieves token from cookies → Sends in Authorization header → Server validates token 3. **Token Refresh**: When access token expires → Client sends refresh token → Server issues new tokens → Client updates cookies 4. **Logout**: Client clears tokens from cookies ### Token Storage Tokens are stored in HTTP-only cookies for security, but are sent in the Authorization header for API requests. This approach: * Protects tokens from XSS attacks (HTTP-only cookies) * Follows standard API authentication practices (Authorization header) * Maintains compatibility with backend services expecting tokens in headers ### Authentication Context The library provides a React context that exposes: * Current authentication state (user, loading, error) * Authentication methods (login, logout, refreshTokens) * Token management utilities Configuration ------------- The library can be configured through environment variables or by passing a configuration object to the `initAuth` function. API Reference ------------- ### Client-Side API #### `useAuth()` React hook that provides access to the authentication context. **Returns:** ```ts { state: { user: User | null; // Current user or null if not authenticated isLoading: boolean; // True when authentication state is loading isAuthenticated: boolean; // True when user is authenticated error: string | null; // Error message or null }; login: (credentials: LoginCredentials) => Promise<void>; // Login function logout: () => Promise<void>; // Logout function refreshTokens: () => Promise<boolean>; // Refresh tokens function } ``` **Example:** ```ts "use client"; import { useAuth } from "@duongtrungnguyen/next-helper"; export default function LoginForm() { const { login, state } = useAuth(); const handleSubmit = async (e) => { e.preventDefault(); await login({ email: "user@example.com", password: "password" }); }; return ( <form onSubmit={handleSubmit}> {/* Form fields */} <button type="submit" disabled={state.isLoading}> {state.isLoading ? "Logging in..." : "Login"} </button> {state.error && <p>{state.error}</p>} </form> ); } ``` #### `useProtectedRoute()` React hook that redirects to the login page if the user is not authenticated. **Returns:** ```ts { isLoading: boolean; // True when authentication state is loading isAuthenticated: boolean; // True when user is authenticated } ``` **Example:** ```ts "use client"; import { useProtectedRoute } from "@duongtrungnguyen/next-prepare"; export default function ProfilePage() { const { isLoading } = useProtectedRoute(); if (isLoading) { return <div>Loading...</div>; } return <div>Profile content (only visible to authenticated users)</div>; } ``` #### `AuthProvider` React context provider that must wrap your application. **Props:** ```ts { children: ReactNode; // Child components } ``` ### Server-Side API #### `getCurrentUser()` Server action that retrieves the current user based on the access token in cookies. **Returns:** ```ts Promise<User | null> // User object or null if not authenticated ``` **Example:** ```ts // app/profile/page.tsx import { getCurrentUser } from "@duongtrungnguyen/next-prepare"; export default async function ProfilePage() { const user = await getCurrentUser(); if (!user) { return <div>Please log in to view your profile</div>; } return ( <div> <h1>Welcome, {user.name}</h1> <p>Email: {user.email}</p> </div> ); } ``` #### `setAuthCookies(tokens: AuthTokens)` Server action that sets authentication cookies for access and refresh tokens. **Parameters:** * `tokens`: Object containing `accessToken` and `refreshToken` **Example:** ```ts import { setAuthCookies } from "@duongtrungnguyen/next-prepare"; // Example usage in a custom login API route export async function POST(request: Request) { const { email, password } = await request.json(); // Authenticate user (implementation depends on your backend) const tokens = await authenticateUser(email, password); if (tokens) { await setAuthCookies(tokens); return Response.json({ success: true }); } return Response.json({ success: false }, { status: 401 }); } ``` #### `clearAuthCookies()` Server action that clears authentication cookies. **Example:** ```ts import { clearAuthCookies } from "@duongtrungnguyen/next-prepare"; // Example usage in a custom logout API route export async function POST() { await clearAuthCookies(); return Response.json({ success: true }); } ``` #### `refreshAccessToken()` Server action that refreshes the access token using the refresh token in cookies. **Returns:** ```ts Promise<boolean> // True if token was successfully refreshed ``` ### Middleware #### `authMiddleware(request: NextRequest)` Middleware function that checks if the access token is about to expire and refreshes it if necessary. **Parameters:** * `request`: Next.js request object **Returns:** `Promise<NextResponse> // Next.js response object` **Example:** ```ts // middleware.ts import { NextRequest, NextResponse } from "next/server"; import { authMiddleware } from "@duongtrungnguyen/next-prepare"; export async function middleware(request: NextRequest) { return await authMiddleware(request); } export const config = { matcher: [ "/((?!_next/static|_next/image|favicon.ico|.*\\.(?:svg|png|jpg|jpeg|gif|webp)$).*)", ], }; ``` ### HTTP Utilities #### `HttpClient` Universal fetch utility class that works in both client and server components. **Methods:** * `get<T>(url: string, config?: RequestConfigs): Promise<T>` * `post<T>(url: string, data?: any, config?: RequestConfigs): Promise<T>` * `put<T>(url: string, data?: any, config?: RequestConfigs): Promise<T>` * `delete<T>(url: string, config?: RequestConfigs): Promise<T>` **Example:** ```ts import { httpClient } from "@duongtrungnguyen/next-prepare"; // In any component (client or server) const fetchData = async () => { try { const data = await httpClient.get("/api/protected-resource"); return data; } catch (error) { console.error("Error fetching data:", error); } }; ```