Production-ready JWT authentication scaffold with refresh token rotation. Clone it, swap in your business logic, and ship.
Use this as a starting point for any full-stack app that needs auth — the boring (and easy to mess up) parts are already done: secure token storage, automatic refresh, route guards, breach detection, and logout.
For a deep dive into how JWT auth works, see docs/JWT_GUIDE.md.
- Backend: Spring Boot 3.5.2, Java 21, Gradle (Kotlin DSL)
- Auth: JWT (jjwt) with access + refresh token rotation
- Database: PostgreSQL 17
- Frontend: React 19, Vite 7, Tailwind CSS v4, shadcn/ui, React Router 7, Zustand, Axios
- Docker (for PostgreSQL)
- Node.js 20+ (for frontend)
- pnpm (
npm install -g pnpm) - Java 21 (auto-downloaded by Gradle if missing)
docker compose up -dThis starts a Postgres container with:
- Database:
auth_db - User:
auth_user - Password:
auth_pass - Port:
5432
cd server
./gradlew bootRunFirst run downloads dependencies + Java 21 (if needed) — takes ~90 seconds. Subsequent starts take ~8 seconds.
Server runs at http://localhost:8443
cd client
pnpm install
pnpm devFrontend runs at http://localhost:5173
- Go to http://localhost:5173 — you'll be redirected to
/login - Click Register — create an account — you'll land on the dashboard
- Refresh the page — session persists (refresh token restores it)
- Click Logout — you're back at
/login - Log in with the same credentials — back on the dashboard
# Register
curl -X POST http://localhost:8443/api/auth/register \
-H "Content-Type: application/json" \
-d '{"email":"test@test.com","password":"password123","firstName":"John","lastName":"Doe"}'
# Login
curl -X POST http://localhost:8443/api/auth/login \
-H "Content-Type: application/json" \
-d '{"email":"test@test.com","password":"password123"}'| Method | Endpoint | Auth | Description |
|---|---|---|---|
| POST | /api/auth/register |
No | Register a new user |
| POST | /api/auth/login |
No | Login with email/password |
| POST | /api/auth/refresh |
Cookie | Rotate refresh token, get new access token |
| POST | /api/auth/logout |
Cookie | Revoke refresh token |
Register/Login → Backend returns access token (JSON) + refresh token (HttpOnly cookie)
↓
Access token stored in memory (JS variable, never localStorage)
↓
Axios interceptor attaches Bearer token to every request
↓
On 401 → interceptor calls /refresh → retries with new token
↓
On page refresh → initAuth() calls /refresh to restore session
spring-react-jwt-auth/
├── docker-compose.yml # PostgreSQL container
├── server/ # Spring Boot backend
│ ├── build.gradle.kts # Dependencies & build config
│ ├── settings.gradle.kts # Project name + Java toolchain
│ ├── gradlew # Gradle wrapper (Unix)
│ ├── INITIAL_SETUP.md # Backend setup guide (learning resource)
│ └── src/main/
│ ├── resources/
│ │ └── application.yml # Server config (port, DB, JWT)
│ └── java/com/springauth/
│ ├── AuthApplication.java
│ ├── config/ # Security & JWT filter
│ ├── controller/ # REST endpoints
│ ├── dto/ # Request/response records
│ ├── entity/ # JPA entities (User, RefreshToken)
│ ├── exception/ # Error handling
│ ├── repository/ # Data access
│ └── service/ # Business logic (Auth, JWT, Token)
├── client/ # React frontend
│ ├── package.json
│ ├── vite.config.ts # Vite + Tailwind + path aliases
│ ├── components.json # shadcn/ui config
│ ├── INITIAL_SETUP.md # Frontend setup guide (learning resource)
│ └── src/
│ ├── index.css # Tailwind + shadcn theme
│ ├── App.tsx # Routes + initAuth on mount
│ ├── api/
│ │ ├── axios.ts # Axios instance + interceptors
│ │ └── auth.ts # API functions + TypeScript types
│ ├── stores/
│ │ └── authStore.ts # Zustand auth state + actions
│ ├── components/
│ │ ├── ProtectedRoute.tsx # Auth guard (redirects to /login)
│ │ └── ui/ # shadcn components
│ ├── lib/utils.ts # shadcn utility
│ └── pages/ # Login, Register, Dashboard, etc.
# Stop the Spring Boot server
Ctrl+C
# Stop the React dev server
Ctrl+C
# Stop PostgreSQL
docker compose down
# Stop PostgreSQL AND delete all data
docker compose down -vdocs/JWT_GUIDE.md— Complete JWT guide: how tokens work, sessions vs JWT, refresh token rotation, breach detection, and security considerationsserver/INITIAL_SETUP.md— Backend from-scratch guide (Spring Boot, JPA, Security, JWT)client/INITIAL_SETUP.md— Frontend from-scratch guide (Vite, Tailwind, shadcn, React Router)