A robust OAuth 2.0 authentication backend built with Spring Boot 3 and MongoDB. This server handles secure GitHub OAuth integration, JWT token generation, and session management for the Next.JS frontend.
The application implements a dual-token authentication system with short-lived access tokens and long-lived refresh tokens, both stored as httpOnly cookies to prevent XSS attacks. MongoDB provides persistent storage for refresh tokens and invalidated access tokens, ensuring secure session management and proper token revocation. CORS is configured to allow cross-origin requests from the frontend whilst maintaining security.
The application provides complete OAuth authentication functionality:
- GitHub OAuth 2.0 integration with Spring Security
- Automatic user authentication and authorisation
- Secure callback handling and token exchange
- Error handling for failed authentication attempts
Token generation and validation:
- Dual-token system with access tokens (15 minutes by default) and refresh tokens (7 days by default)
- Automatic token generation upon successful OAuth login
- Token validation on protected endpoints
- Custom JWT claims with user information (ID, username, email, avatar)
- Token expiry handling and validation
Token renewal without re-authentication:
- Automatic access token refresh using refresh tokens
- Refresh token rotation for enhanced security
- Token validation before refresh
- Persistent refresh token storage in MongoDB
Secure endpoints requiring authentication:
- User profile information retrieval
- Protected data access endpoints
- Action endpoints for authenticated operations
- Request validation using JWT tokens
Session termination:
- Access token invalidation on logout
- Refresh token revocation from database
- Automatic cleanup of expired tokens via MongoDB TTL
- Cookie deletion on logout
Health check endpoints for monitoring:
- Public health check endpoint
- Authentication status verification
- No authentication required
These are the requirements needed to run the project:
- Java 17 or higher
- MongoDB 4.4 or higher
- GitHub OAuth Application credentials (Client ID and Client Secret)
- (Optional) Next.JS frontend running
These are the main technologies used in this project:
- Java: An object-oriented programming language with strong typing and extensive libraries.
- Spring Boot: A framework for building production-ready applications with minimal configuration.
- Spring Security: Comprehensive security framework providing authentication and authorisation.
- Spring Security OAuth2 Client: OAuth 2.0 client implementation for Spring applications.
- Spring Data MongoDB: Provides integration with MongoDB for data persistence.
- JJWT: Java JWT library for creating and parsing JSON Web Tokens.
- Gradle: Build automation tool for dependency management and project building.
- MongoDB: NoSQL database for storing refresh tokens and invalidated access tokens with TTL-based expiry.
- Next.JS Frontend: A TypeScript and React-based frontend consuming the authentication APIs.
The application uses httpOnly cookies for token storage rather than localStorage. This approach prevents XSS attacks as JavaScript cannot access httpOnly cookies. Access tokens have a 15-minute lifespan whilst refresh tokens last 7 days by default. Both tokens are transmitted securely with the Secure flag in production.
MongoDB stores two collections:
refresh_tokens: Stores valid refresh tokens with username, creation time, last used time, and expiry dateinvalidated_access_tokens: Stores invalidated access tokens until their natural expiry
Both collections use MongoDB's TTL indexes to automatically delete expired documents, eliminating the need for manual cleanup.
Access tokens contain user claims (ID, login, name, email, avatar URL) and a type field set to access.
Refresh tokens contain minimal information with type set to "refresh".
All tokens are signed using HMAC-SHA256 with a secret key.
CORS is configured to accept requests from http://localhost:3000 (frontend URL) with credentials enabled. Allowed methods include GET, POST, PUT, DELETE, and OPTIONS. This enables secure cross-origin communication whilst preventing unauthorised access.
- User initiates GitHub OAuth login
- Spring Security handles OAuth callback
- Backend generates access and refresh tokens
- Tokens are set as httpOnly cookies
- User is redirected to frontend dashboard
- Subsequent requests include cookies automatically
- JWT filter validates access tokens on protected endpoints
- Frontend detects expired access token (
401response) - Frontend calls refresh endpoint with refresh token cookie
- Backend validates refresh token from database
- Backend generates new access token
- New access token is set as httpOnly cookie
- Original request is retried with new token
- User initiates logout
- Backend retrieves both tokens from cookies
- Access token is added to invalidation list in MongoDB
- Refresh token is deleted from database
- Both cookies are deleted
- User is redirected to login page
These are simple steps to run the application locally.
git clone https://github.com/mbeps/oauth-nextjs-springboot-backend.git
cd oauth-nextjs-springboot-backendEnsure MongoDB is running locally on mongodb://localhost:27017 or configure your MongoDB connection string.
The application will automatically create the required collections and indexes.
Create a GitHub OAuth application with the following settings:
- Homepage URL:
http://localhost:8080 - Authorization callback URL:
http://localhost:8080/login/oauth2/code/github
Note your Client ID and Client Secret for the next step.
For Production: Update the Homepage URL and Authorization callback URL to your production domain (e.g., https://yourdomain.com instead of http://localhost:8080).
Copy the example.application.yaml file in the project root and rename it to application.yaml:
spring:
application:
name: oauth
security:
oauth2:
client:
registration:
github:
client-id: GITHUB_CLIENT_ID_HERE
client-secret: GITHUB_CLIENT_SECRET_HERE
scope:
- user:email
- read:user
data:
mongodb:
uri: MONGODB_URI_HERE
# Alternative configuration:
# host: localhost
# port: 27017
# database: oauth_app
server:
port: 8080
# JWT Configuration
jwt:
secret: JTW_SECRET_HERE_256
expiration: 86400000 # 24 hours in milliseconds
access-token-expiration: 900000 # 15 minutes in milliseconds
refresh-token-expiration: 604800000 # 7 days in milliseconds
# Frontend URL for redirects
frontend:
url: http://localhost:3000
cookie:
secure: false # Set to true in production (requires HTTPS)
same-site: Lax # Options: Strict, Lax, Nonespring.security.oauth2.client.registration.github:
client-id: Your GitHub OAuth application Client ID obtained from GitHub Developer Settingsclient-secret: Your GitHub OAuth application Client Secret obtained from GitHub Developer Settingsscope: OAuth scopes requesting access to user profile and email information
spring.data.mongodb:
uri: MongoDB connection string specifying the database location and name (e.g.,mongodb://localhost:27017/oauth_db)
jwt:
secret: Secret key for signing JWT tokens (minimum 32 characters for HS256 algorithm)access-token-expiration: Lifespan of access tokens in milliseconds (default: 900000 = 15 minutes)refresh-token-expiration: Lifespan of refresh tokens in milliseconds (default: 604800000 = 7 days)
frontend:
url: The URL of your frontend application for CORS configuration and redirects (e.g.,http://localhost:3000)
cookie:
secure: Whether cookies should only be sent over HTTPS (set tofalsefor local development,truefor production)same-site: Cookie SameSite attribute for CSRF protection (useLaxorStrict)
app.security.refresh-token:
hashing-enabled: Stores refresh tokens as SHA-256 hashes in MongoDB whentrue(recommended for production)rotation-enabled: Issues a brand new refresh token on every refresh request and revokes the old one whentrue
For Production:
- Set
cookie.securetotrue - Update
frontend.urlto your production frontend domain - Use a strong, randomly generated
jwt.secret - Configure MongoDB with authentication and SSL/TLS
./gradlew build./gradlew bootRunThe application should now be running on http://localhost:8080
- Frontend redirects user to
/oauth2/authorization/github - User authenticates with GitHub
- GitHub redirects to callback URL with authorization code
- Backend exchanges code for access token
- Backend retrieves user information from GitHub
- Backend generates JWT tokens and sets cookies
- User is redirected to frontend dashboard
Protected endpoints require valid JWT access token in cookies:
Get User Information
GET /api/user
Cookie: jwt=<access_token>Get Protected Data
GET /api/protected/data
Cookie: jwt=<access_token>Perform Action
POST /api/protected/action
Cookie: jwt=<access_token>
Content-Type: application/json
{
"action": "refresh_data"
}When access token expires, call the refresh endpoint:
POST /api/auth/refresh
Cookie: refresh_token=<refresh_token>This returns a new access token as an httpOnly cookie.
GET /api/auth/status
Cookie: jwt=<access_token>Returns authentication status and user information if authenticated.
POST /logout
Cookie: jwt=<access_token>; refresh_token=<refresh_token>Invalidates tokens and deletes cookies.
Public health check (no authentication required):
GET /api/public/healthAll the actions above can be executed through the Next.JS frontend given it is running. The frontend app will be running on http://localhost:3000 and will require the backend server to be running correctly.
More information can be found on the Frontend for Next.JS & Spring Boot OAuth System repository.