Skip to content

Latest commit

Β 

History

4 Commits

Folders and files

NameName
Last commit message
Last commit date
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

πŸ’¬ Chatter

A Real-Time Chat Application built with Spring Boot, React, WebSockets, Keycloak, MySQL, Flyway & Docker

Java Spring Boot React MySQL Keycloak WebSocket Flyway Docker License


πŸ“– About

Chatter is a real-time chat application built with Spring Boot and React.

The application uses Keycloak for authentication and authorization, WebSockets with STOMP for real-time communication, MySQL for persistent data, and Flyway for database migrations.

Users can authenticate through Keycloak, view other registered users, see their online/offline presence, start conversations, exchange messages in real time, receive delivery and read-status updates, and share image, audio, and video files.

The project is designed as a practical implementation of modern backend concepts including OAuth2/JWT security, WebSockets, message delivery states, presence tracking, file storage, JPA/Hibernate, database migrations, and Docker Compose.


✨ Features

  • πŸ” Keycloak-based authentication and JWT security
  • πŸ‘€ Automatic synchronization of authenticated Keycloak users into MySQL
  • πŸ‘₯ View other registered users
  • 🟒 Real-time online/offline presence tracking
  • πŸ’¬ Real-time one-to-one messaging using WebSockets and STOMP
  • πŸ“œ Persistent chat history
  • ⚑ Optimistic UI for faster message interaction
  • πŸ“€ Message delivery states: SENT, DELIVERED, SEEN
  • πŸ‘€ Read receipts for messages
  • πŸ”„ Automatic delivery of messages sent while a user was offline
  • πŸ“Ž Upload image, audio, and video files
  • πŸ”’ Authenticated file downloads restricted to chat participants
  • πŸ”‘ JWT authentication for both REST APIs and WebSocket connections
  • πŸ”„ Automatic JWT refresh before API/WebSocket operations
  • πŸ—„οΈ MySQL persistence with Spring Data JPA
  • πŸ”„ Database schema management with Flyway
  • 🐳 Containerized with Docker and Docker Compose
  • 🌐 React frontend served through Nginx in Docker

πŸ—οΈ Application Flow

Main flow

  1. User opens the Chatter frontend.
  2. User is authenticated through Keycloak.
  3. Keycloak issues a JWT access token.
  4. React sends the token with authenticated REST requests and WebSocket connections.
  5. Spring Security validates the JWT.
  6. The authenticated user is synchronized with the MySQL users table.
  7. The user can view other registered users.
  8. The frontend establishes a STOMP connection through /ws.
  9. The backend tracks the user's online presence.
  10. User selects another contact and loads the existing chat history.
  11. Messages are sent through /app/chat.send.
  12. The backend persists the message and sends it to the recipient through a user-specific queue.
  13. Message state changes from SENT to DELIVERED when the recipient is online.
  14. Messages become SEEN when the recipient reads the conversation.
  15. Presence changes are broadcast to subscribed clients.
  16. Media files are uploaded through the REST API and their URLs are sent as chat messages.

Authentication Flow

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚    React     β”‚
β”‚   Frontend   β”‚
β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”˜
       β”‚
       β”‚ Login
       β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚   Keycloak   β”‚
β”‚   chatter    β”‚
β”‚    realm     β”‚
β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”˜
       β”‚
       β”‚ JWT Access Token
       β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚    React     β”‚
β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”˜
       β”‚
       β”‚ Bearer Token
       β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Spring Boot  β”‚
β”‚  Security    β”‚
β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”˜
       β”‚
       β”‚ JWT validation
       β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚   User Sync  β”‚
β”‚    MySQL     β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜


WebSocket Message Flow

User A
  β”‚
  β”‚ STOMP SEND
  β”‚ /app/chat.send
  β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚   Spring WebSocket  β”‚
β”‚    ChatController   β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
           β”‚
           β”‚ Save message
           β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚       MySQL         β”‚
β”‚      messages       β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
           β”‚
           β”‚ User destination
           β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ /user/queue/messagesβ”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
           β”‚
           β–Ό
        User B


Presence Flow

WebSocket CONNECT
       β”‚
       β–Ό
JwtChannelInterceptor
       β”‚
       β–Ό
PresenceTracker
       β”‚
       β”œβ”€β”€ First active session
       β”‚        β”‚
       β”‚        β–Ό
       β”‚   User ONLINE
       β”‚        β”‚
       β”‚        β–Ό
       β”‚ /topic/presence.{userId}
       β”‚
       β–Ό
WebSocket DISCONNECT
       β”‚
       β–Ό
PresenceTracker
       β”‚
       β”œβ”€β”€ Last active session
       β”‚        β”‚
       β”‚        β–Ό
       β”‚   User OFFLINE
       β”‚        β”‚
       β”‚        β–Ό
       β”‚ /topic/presence.{userId}
       β”‚
       β–Ό
Update lastSeen in MySQL


πŸ“Œ Results

Login

The application uses Keycloak to authenticate users before allowing access to Chatter.


Chat Interface

Users can select another registered user and exchange messages in real time.


Online / Offline Presence

The contact list displays the current presence state of other users and updates it through WebSocket events.


Message Status

Messages support the following states:

SENT β†’ DELIVERED β†’ SEEN


Media Messages

Users can upload and send:

  • πŸ–ΌοΈ Images
  • 🎡 Audio
  • πŸŽ₯ Video

Files are stored locally by the backend and accessed through authenticated file endpoints.


πŸ› οΈ Tech Stack

Category Technology
Language Java 21
Backend Framework Spring Boot 4.1.0
Frontend React 19
Build Tool Maven
Database MySQL 8
ORM Spring Data JPA / Hibernate
Authentication Keycloak 26.5.7
Security Spring Security OAuth2 Resource Server / JWT
Real-Time Communication WebSocket + STOMP
WebSocket Client @stomp/stompjs + SockJS
Database Migration Flyway
File Storage Local File System
Frontend Build Tool Vite
Frontend Server Nginx
Containerization Docker / Docker Compose
Utilities Lombok

πŸ“‚ Project Structure

Chatter
β”œβ”€β”€ backend
β”‚   β”œβ”€β”€ src
β”‚   β”‚   β”œβ”€β”€ main
β”‚   β”‚   β”‚   β”œβ”€β”€ java
β”‚   β”‚   β”‚   β”‚   └── com.ragul.Chatter
β”‚   β”‚   β”‚   β”‚       β”œβ”€β”€ chat
β”‚   β”‚   β”‚   β”‚       β”œβ”€β”€ common
β”‚   β”‚   β”‚   β”‚       β”œβ”€β”€ config
β”‚   β”‚   β”‚   β”‚       β”œβ”€β”€ exception
β”‚   β”‚   β”‚   β”‚       β”œβ”€β”€ file
β”‚   β”‚   β”‚   β”‚       β”œβ”€β”€ message
β”‚   β”‚   β”‚   β”‚       β”œβ”€β”€ user
β”‚   β”‚   β”‚   β”‚       └── websocket
β”‚   β”‚   β”‚   β”‚           └── config
β”‚   β”‚   β”‚   β”‚
β”‚   β”‚   β”‚   └── resources
β”‚   β”‚   β”‚       β”œβ”€β”€ application.yaml
β”‚   β”‚   β”‚       └── db
β”‚   β”‚   β”‚           └── migration
β”‚   β”‚   β”‚               β”œβ”€β”€ V1__init_user_schema.sql
β”‚   β”‚   β”‚               β”œβ”€β”€ V2__init_chat_schema.sql
β”‚   β”‚   β”‚               └── V3__init_message_schema.sql
β”‚   β”‚   β”‚
β”‚   β”‚   └── test
β”‚   β”‚
β”‚   β”œβ”€β”€ keycloak
β”‚   β”‚   └── realm-export.json
β”‚   β”œβ”€β”€ Dockerfile
β”‚   └── pom.xml
β”‚
β”œβ”€β”€ Frontend
β”‚   β”œβ”€β”€ src
β”‚   β”‚   β”œβ”€β”€ api.js
β”‚   β”‚   β”œβ”€β”€ App.jsx
β”‚   β”‚   β”œβ”€β”€ ChatWindow.jsx
β”‚   β”‚   β”œβ”€β”€ ContactList.jsx
β”‚   β”‚   β”œβ”€β”€ AuthedMedia.jsx
β”‚   β”‚   β”œβ”€β”€ MessageStatus.jsx
β”‚   β”‚   β”œβ”€β”€ keycloak.js
β”‚   β”‚   β”œβ”€β”€ main.jsx
β”‚   β”‚   └── socket.js
β”‚   β”œβ”€β”€ Dockerfile
β”‚   β”œβ”€β”€ nginx.conf
β”‚   └── package.json
β”‚
β”œβ”€β”€ keycloak
β”‚   └── chatter-realm.json
β”‚
└── docker-compose.yml

🌐 REST API

All protected REST endpoints require a valid Keycloak JWT access token.

Method Endpoint Description
GET /api/users Get all users except the authenticated user
GET /api/users/{userId}/presence Get a user's current presence
GET /api/chats/{recipientId}/messages Get chat history with a user
POST /api/files/upload Upload an image, audio, or video file
GET /files/{filename} Download an uploaded file

πŸ”Œ WebSocket API

WebSocket endpoint:

/ws

SockJS is used by the React frontend for the WebSocket connection.

Application destinations

Destination Description
/app/chat.send Send a message
/app/chat.read Mark chat messages as read

User-specific destinations

Destination Description
/user/queue/messages Receive real-time messages
/user/queue/messages.status Receive message delivery/read-status updates
/user/queue/errors Receive WebSocket message errors

Presence destination

/topic/presence.{userId}

Clients subscribe to a user's presence topic to receive online/offline updates.


πŸ“€ Sample WebSocket Message

Send Message

Destination:

/app/chat.send

Payload:

{
  "recipientId": "recipient-user-id",
  "content": "Hello from Chatter!",
  "type": "TEXT"
}

Supported message types:

TEXT
IMAGE
AUDIO
VIDEO

πŸ“₯ Sample Message Response

{
  "id": 1,
  "chatId": "chat-id",
  "senderId": "sender-user-id",
  "recipientId": "recipient-user-id",
  "content": "Hello from Chatter!",
  "type": "TEXT",
  "state": "SENT",
  "createdDate": "2026-08-11T12:30:00"
}

Message state progresses through:

SENT
  ↓
DELIVERED
  ↓
SEEN

πŸ” Authentication

Chatter uses Keycloak as the identity and access management server.

The authentication flow is based on:

  • OpenID Connect
  • OAuth 2.0
  • JWT access tokens
  • Spring Security OAuth2 Resource Server

The frontend uses keycloak-js to authenticate users.

The backend validates the JWT for protected REST requests.

WebSocket connections are also authenticated using the JWT supplied through the STOMP Authorization header.

The backend synchronizes authenticated Keycloak users into the local MySQL users table.


πŸ—„οΈ Database

Flyway automatically manages the database schema.

Main Tables

users

Stores authenticated application users.

id
first_name
last_name
email
last_seen
created_date
last_modified_date

chats

Stores conversations between users.

id
sender_id
recipient_id
created_date
last_modified_date

messages

Stores messages and their delivery state.

id
content
state
type
chat_id
sender_id
recipient_id
created_date
last_modified_date

Message States

SENT
DELIVERED
SEEN

πŸ“Ž File Storage

Chatter supports uploading:

  • Images
  • Audio
  • Video

Uploaded files are stored in the configured upload directory.

Default configuration:

app:
  upload:
    dir: uploads

The Docker deployment mounts this directory to a persistent Docker volume:

/uploads

File downloads are protected by checking whether the authenticated user is a participant in the message containing the file.


βš™οΈ Configuration

Backend

The local development configuration uses:

spring:
  datasource:
    url: jdbc:mysql://localhost:3307/chatter
    username: root
    password: root

  jpa:
    hibernate:
      ddl-auto: validate

  flyway:
    enabled: true

  security:
    oauth2:
      resourceserver:
        jwt:
          issuer-uri: http://127.0.0.1:9090/realms/chatter

For Docker, the backend uses the MySQL and Keycloak Docker service names for internal communication where required.


Keycloak

Default local Keycloak address:

http://127.0.0.1:9090

Realm:

chatter

Client:

chatter-client

Keycloak is imported from the realm configuration during Docker startup.

Do not use real production credentials in the repository. Replace development credentials before deploying the application outside a local environment.


πŸš€ Running Locally

Clone Repository

git clone https://github.com/<your-username>/Chatter.git
cd Chatter

Start MySQL & Keycloak

Make sure Docker is installed and running.

Start the complete application stack:

docker compose up --build

Frontend

The React application is available at:

http://localhost:5173

Backend

The Spring Boot API is available at:

http://localhost:8080

Keycloak

Keycloak is available at:

http://127.0.0.1:9090

🐳 Docker

Chatter uses Docker Compose to run the application services.

Build and start all services

docker compose up --build

Run in detached mode

docker compose up -d

View running containers

docker compose ps

View logs

docker compose logs -f

Stop containers

docker compose down

🧩 Docker Services

Service Container Port Purpose
MySQL mysql_chatter 3307 Application database
Keycloak keycloak_chatter 9090 Authentication server
Backend backend 8080 Spring Boot API and WebSocket server
Frontend frontend 5173 React application served by Nginx

πŸ”„ Message Delivery Architecture

Chatter maintains message state using:

             β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
             β”‚   SENT    β”‚
             β””β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”˜
                   β”‚
                   β”‚ Recipient online
                   β–Ό
             β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
             β”‚ DELIVERED β”‚
             β””β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”˜
                   β”‚
                   β”‚ Recipient reads chat
                   β–Ό
             β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
             β”‚   SEEN    β”‚
             β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

If a recipient is offline, the message remains in the SENT state.

When the recipient establishes a WebSocket connection again, pending messages for that user are marked as delivered and delivery-status updates are sent to the appropriate senders.


🟒 Presence Tracking

Presence is maintained using an in-memory ConcurrentHashMap.

The tracker maintains the number of active sessions for each user.

This allows Chatter to correctly handle multiple browser tabs or devices:

User A
 β”œβ”€β”€ Browser Tab 1
 β”œβ”€β”€ Browser Tab 2
 └── Browser Tab 3

Active sessions = 3
User = ONLINE

The user becomes offline only after their last active WebSocket session disconnects.

The last-seen timestamp is persisted in MySQL when the user goes offline.


πŸ§ͺ Testing

The backend contains a Spring Boot application test setup.

Run the Maven test suite with:

./mvnw test

or:

mvn test

πŸ“ˆ Future Enhancements

  • πŸ”” Browser notifications for new messages
  • 🟒 More detailed presence and last-seen indicators
  • ✏️ Message editing
  • πŸ—‘οΈ Message deletion
  • ↩️ Message replies
  • πŸ” Message search
  • πŸ“Œ Message pinning
  • 😊 Emoji picker and reactions
  • πŸ‘₯ Group conversations
  • πŸ“ž Audio/video calling
  • ☁️ Object storage for media files
  • πŸ“Š Better test coverage
  • πŸ“š Swagger/OpenAPI documentation
  • πŸ” Production-ready Keycloak configuration
  • πŸ“ˆ Monitoring with Prometheus and Grafana
  • 🐳 Production container hardening
  • ☸️ Kubernetes deployment

🀝 Contributing

Contributions, suggestions, and improvements are welcome.

  1. Fork the repository
  2. Create a feature branch
  3. Commit your changes
  4. Push the branch
  5. Open a Pull Request

πŸ“„ License

This project is licensed under the MIT License.


⭐ If you found this project helpful, consider giving it a star!

Built with ❀️ using Spring Boot, React, WebSockets, Keycloak, MySQL, Flyway, and Docker

About

end to end secured chat application with file upload feature

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages