This project demonstrates a secure authentication flow using Keycloak as the identity provider, with a React frontend and Node.js backend. The application implements industry best practices for authentication, including:
- OAuth 2.0 Authorization Code Flow
- JWKS token verification
- HTTP-only cookies for token storage
- Refresh token handling
- Secure token exchange via backend
The application consists of three main components:
- Keycloak Server: External identity provider (IdP) that handles user authentication
- React Frontend: Client application that initiates the authentication flow
- Node.js Backend: API server that securely exchanges the authorization code for tokens
- User clicks "Login" in the frontend
- User is redirected to Keycloak login page
- After successful authentication, Keycloak redirects back to the application with an authorization code
- The frontend sends this code to the backend
- The backend exchanges the code for tokens using client credentials
- The backend verifies the token signature using JWKS
- The backend stores the access token in an HTTP-only cookie
- The frontend receives user information and displays the dashboard
- Subsequent API requests include the cookie automatically
├── api/ # Backend Node.js API
│ ├── middleware/ # Express middleware
│ │ ├── auth.js # JWT verification middleware
│ │ └── cors.js # CORS configuration
│ ├── services/ # Business logic
│ │ ├── jwksService.js # JWKS token verification
│ │ └── tokenService.js # Token handling and exchange
│ ├── .env # Environment variables (not in repo)
│ ├── Dockerfile # Docker configuration
│ ├── package.json # Dependencies
│ └── server.js # Express server
│
├── frontend/ # React frontend
│ ├── src/
│ │ ├── components/ # React components
│ │ ├── hooks/ # Custom React hooks
│ │ │ └── useAuth.jsx # Authentication context
│ │ ├── pages/ # Application pages
│ │ │ ├── Callback.jsx # OAuth callback handler
│ │ │ ├── Dashboard.jsx # Protected page
│ │ │ └── Home.jsx # Landing page
│ │ ├── services/ # API services
│ │ │ └── authService.js # Authentication service
│ │ ├── App.jsx # Main application component
│ │ └── main.jsx # Application entry point
│ ├── Dockerfile # Docker configuration
│ ├── package.json # Dependencies
│ └── vite.config.js # Vite configuration
│
└── docker-compose.yml # Docker Compose configuration
- Docker and Docker Compose
- A Keycloak server (or use the provided configuration for the public demo server)
- Create an
.env
file in theapi
directory with the following variables:
KEYCLOAK_URL=https://keycloak.berget.ai/realms/iteam
CLIENT_ID=demo
CLIENT_SECRET=your_client_secret_here
- Configure your Keycloak client:
- Create a client with ID matching your CLIENT_ID
- Set Access Type to "confidential"
- Enable "Standard Flow"
- Add valid redirect URIs:
http://localhost:5173/callback
- Copy the client secret to your .env file
Start the application using Docker Compose:
docker-compose up
Open your browser and navigate to:
http://localhost:5173
This application implements several security best practices:
- No tokens in browser storage: Access tokens are stored in HTTP-only cookies
- Backend token exchange: The client secret is never exposed to the frontend
- JWKS verification: Tokens are cryptographically verified using Keycloak's public keys
- CORS configuration: Properly configured to prevent cross-origin attacks
- Refresh token rotation: Implemented for long-lived sessions
MIT