A minimal Spring Boot backend designed as a test companion for the Flutter Starter App. Provides authentication, profile management, and real-time WebSocket communication.
| Technology | Version | Purpose |
|---|---|---|
| Java | 25 | Runtime |
| Spring Boot | 4.0.1 | Framework |
| H2 Database | In-memory | Development database |
| JWT (jjwt) | 0.12.6 | Authentication tokens |
| Lombok | - | Boilerplate reduction |
| Gradle (Kotlin DSL) | - | Build system |
- Java 25+
- Gradle (or use included wrapper)
./gradlew bootRunThe server starts at http://localhost:8080
docker-compose up| Method | Endpoint | Description | Auth |
|---|---|---|---|
POST |
/register |
Register new user | ❌ |
POST |
/login |
Login user | ❌ |
POST |
/refresh |
Refresh access token | ❌ |
POST |
/logout |
Logout user | ✅ |
GET |
/me |
Get current user | ✅ |
POST |
/check-user-exists |
Check if email exists | ❌ |
curl -X POST http://localhost:8080/api/v1/auth/register \
-H "Content-Type: application/json" \
-d '{"email": "user@example.com", "password": "password123", "name": "John Doe"}'curl -X POST http://localhost:8080/api/v1/auth/login \
-H "Content-Type: application/json" \
-d '{"email": "user@example.com", "password": "password123"}'Response:
{
"user": {
"id": "uuid",
"email": "user@example.com"
},
"tokens": {
"accessToken": "eyJ...",
"refreshToken": "eyJ...",
"expiresIn": 900000
}
}curl -X POST http://localhost:8080/api/v1/auth/refresh \
-H "Content-Type: application/json" \
-d '{"refreshToken": "eyJ..."}'| Method | Endpoint | Description | Auth |
|---|---|---|---|
POST |
/ |
Create profile | ✅ |
GET |
/me |
Get my profile | ✅ |
PUT |
/me |
Update my profile | ✅ |
curl http://localhost:8080/api/v1/profiles/me \
-H "Authorization: Bearer <access_token>"Response:
{
"id": "uuid",
"userId": "uuid",
"displayName": "John Doe",
"bio": null,
"avatarUrl": null,
"createdAt": "2026-01-22T17:02:45.032773Z",
"updatedAt": "2026-01-22T17:02:45.032774Z"
}Real-time authentication state notifications.
Connect with token as query parameter:
ws://localhost:8080/ws/auth?token=<access_token>
| Event | Description | Payload |
|---|---|---|
user_authenticated |
User authenticated/updated | { "event": "user_authenticated", "data": { user } } |
user_logged_out |
Session terminated by server | { "event": "user_logged_out" } |
Example logout event:
{"event": "user_logged_out"}This event is sent when:
- Session expires
- Token is revoked
- User logged out from another device
- Admin force logout
src/main/java/com/starter/
├── StarterAppBackendApplication.java # Main entry point
├── auth/
│ ├── controller/AuthController.java
│ ├── dto/ # Request/Response DTOs
│ ├── entity/User.java
│ ├── repository/UserRepository.java
│ └── service/
│ ├── AuthService.java
│ └── JwtService.java
├── profile/
│ ├── controller/ProfileController.java
│ ├── dto/
│ ├── entity/Profile.java
│ ├── repository/ProfileRepository.java
│ └── service/ProfileService.java
├── websocket/
│ ├── config/WebSocketConfig.java
│ ├── dto/WebSocketMessage.java
│ ├── handler/AuthWebSocketHandler.java
│ └── service/WebSocketSessionService.java
├── config/ # Security, CORS, etc.
├── exception/ # Global exception handling
└── test/ # Test utilities
| Variable | Default | Description |
|---|---|---|
JWT_SECRET |
your-256-bit-secret... |
JWT signing key ( |
SERVER_PORT |
8080 |
Server port |
| Token | Duration | Milliseconds |
|---|---|---|
| Access Token | 15 minutes | 900,000 |
| Refresh Token | 7 days | 604,800,000 |
Access the in-memory database at: http://localhost:8080/h2-console
- JDBC URL:
jdbc:h2:mem:starterdb - Username:
sa - Password: (empty)
To test the WebSocket logout flow, uncomment the test code in AuthWebSocketHandler.java:
// 1. Uncomment the scheduler field
private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
// 2. Uncomment the call in afterConnectionEstablished
scheduleTestLogout(session, userId);
// 3. Uncomment the scheduleTestLogout methodThis sends a user_logged_out event 1 minute after WebSocket connection.
Caution
For development only! Before production:
- Change
JWT_SECRETto a secure 256-bit key - Replace H2 with a persistent database (PostgreSQL, MySQL)
- Configure proper CORS origins
- Enable HTTPS
MIT