A security-focused 1-to-1 chat application built with Next.js, Firebase, Firestore, Tailwind CSS, and the Web Crypto API. Messages are encrypted in the browser before they are written to Firestore, and the app uses a device-based session architecture that is materially stronger than the original single-key browser chat model.
- 🔐 Firebase Authentication - Sign up and sign in with persistent email/password sessions.
- 👤 Username Onboarding - Create an identity with a unique username and color-based avatar.
- 💬 Realtime 1-to-1 Messaging - Search users, create chats instantly, and receive live updates with Firestore listeners.
- 🛡️ Browser-Side Encryption - Encrypt message content on the client before it is stored remotely.
- 📱 Device Security Center - Review device state, trusted contacts, recovery metadata, and local session status.
- ✅ Safety Number Verification - Compare fingerprints manually and mark trusted contacts as verified.
- 🚨 Key Change Warnings - Surface visible warnings when a contact device identity changes.
- 🟢 Presence and Typing Signals - Show online status, typing indicators, and inactive chat alerts.
- 🌗 Responsive Interface - Works across desktop and mobile with theme support.
- 🗝️ Device-Scoped Identity Keys - Each browser creates its own identity instead of sharing one long-lived account-wide key.
- 💾 IndexedDB Key Storage - Private device material stays in browser storage backed by IndexedDB instead of
localStorage. - ✍️ Signed Prekeys - Remote device prekeys are signed and verified before secure session establishment.
- 🤝 Session-Based Encryption - New chats use derived conversation session keys instead of static direct key wrapping for every message.
- 🔄 Automatic Session Rotation - Session material rotates after message-count and age thresholds, and when remote device state changes.
- 🧾 Ciphertext Integrity Signatures - Messages are signed so tampered ciphertext or metadata can be detected instead of silently rendered.
- 🔍 Contact Verification - Safety numbers let users confirm identity material out of band.
- 🚫 No Plaintext in Firestore - Firestore stores encrypted payloads and metadata, not raw message text.
Unlike traditional browser chat demos that rely on static encryption models, chatr introduces:
- Device-scoped cryptographic identities
- Signed prekey verification
- Session-derived encryption keys
- Automatic session rotation
- Ciphertext integrity validation
- Local trust verification workflows
- Key-change detection warnings
- IndexedDB-backed secure key persistence
This creates a significantly stronger browser-based secure messaging architecture than typical realtime Firebase chat applications.
Each browser creates its own device bundle with:
- ECDSA P-256 identity keypair
- ECDH P-256 signed prekey
- device metadata such as
deviceId,deviceName,createdAt, andlastActive
Public device material is stored in Firestore under:
users/{uid}/devices/{deviceId}
Private device keys stay in local IndexedDB as CryptoKey objects and are never uploaded to Firebase.
When a secure chat session is created:
- The sender verifies the recipient device's signed prekey signature.
- The sender generates an ephemeral ECDH keypair.
- A shared secret is derived with the recipient's signed prekey.
- HKDF derives a conversation session AES-GCM key.
- Session metadata is signed by the sender's device identity key.
- The session key is stored only in local IndexedDB.
Session state is written to:
chats/{chatId}/sessions/{sessionId}
New messages are encrypted with the active conversation session key:
- AES-GCM encrypts the plaintext
- authenticated additional data binds
chatId,sessionId, sender and recipient ids, device ids, key version, encryption version, and client timestamp - ciphertext metadata is signed with the sender's identity key
Firestore stores only encrypted payloads and metadata such as:
encryptedTextivsignatureaadsessionId- sender and recipient metadata
- version metadata
Firestore does not store plaintext message content.
- Continuous rotation of cryptographic session material during active encrypted conversations
- Session renewal triggered by conversation activity, session aging, and remote device identity changes
- Reduces long-lived key exposure across secure messaging sessions
- Improves isolation between encrypted communication states
- Previously rotated session material cannot decrypt newly established secure sessions
- Strengthens forward-security characteristics compared to traditional static-key browser chat architectures.
For each contact, the app derives a deterministic safety number from both devices' identity public keys.
Users can:
- compare the safety number with a contact
- mark a contact as verified
- see verified or unverified state in chat
- receive warnings when a remote device key changes
Trust records are stored locally per browser in IndexedDB, not in Firestore.
Each secure message includes a signature over ciphertext metadata. If ciphertext or bound metadata is modified, verification fails and the UI surfaces an integrity warning instead of quietly treating the message as trustworthy.
chatr is engineered to reduce risks associated with:
- plaintext exposure in Firestore
- passive database compromise revealing message bodies
- ciphertext tampering without detection
- over-reliance on one long-lived encryption key for future traffic
- silent remote key replacement going unnoticed in the interface
These protections are reinforced by IndexedDB-backed private key persistence, device identities, signed prekeys, session rotation, safety-number verification, key-change detection, and ciphertext integrity checks throughout the chat flow.
- Next.js 16 App Router
- React 19
- TypeScript
- Tailwind CSS v4
- Firebase Authentication
- Cloud Firestore
- Web Crypto API
- IndexedDB
- pnpm
app/- App Router routescomponents/- Auth, chat, settings, and shared UIcontext/- Auth and device/session bootstrap statehooks/- Auth access, presence, notifications, and typing statelib/firebase.ts- Firebase initializationlib/auth.ts- Auth flows, profile creation, and secure identity rotationlib/encryption.ts- Legacy RSA compatibility helperslib/security.ts- Device identities, signed prekeys, session derivation, verification, and trust statelib/secure-storage.ts- IndexedDB persistence for secure local records and keyslib/chat.ts- Chat creation, secure session handling, and message decryption helpers
users/{uid}users/{uid}/devices/{deviceId}usernames/{usernameLower}chats/{chatId}chats/{chatId}/sessions/{sessionId}messages/{chatId}/items/{messageId}typing/{chatId}
- Copy the sample environment file:
cp .env.local.example .env.local
- Add your Firebase web app values to
.env.local:NEXT_PUBLIC_FIREBASE_API_KEY=your-api-key NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN=your-project-id.firebaseapp.com NEXT_PUBLIC_FIREBASE_PROJECT_ID=your-project-id NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET=your-project-id.firebasestorage.app NEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID=your-messaging-sender-id NEXT_PUBLIC_FIREBASE_APP_ID=your-app-id NEXT_PUBLIC_FIREBASE_MEASUREMENT_ID=your-measurement-id
- Create a Firebase project and register a web app.
- Enable Email/Password authentication in Firebase Auth.
- Create a Cloud Firestore database.
- Paste the rules from:
firestore.rules - Publish the Firestore rules before using the app.
- Clone the repository:
git clone https://github.com/Danindu05/encrypted-chat-app.git
- Move into the project directory:
cd encrypted-chat-app - Install dependencies:
pnpm install
- Start the development server:
pnpm dev
- Open in your browser:
http://localhost:3000