@duongtrungnguyen/next-helper
Version:
Helper library for Next.js 15
353 lines (249 loc) • 8.8 kB
Markdown
### 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);
}
};
```