Skip to content

smereczynski/SignalR-Chat

Repository files navigation

SignalR Chat (v0.9.5)

A production-ready real-time chat application with asynchronous AI-powered translation built using ASP.NET Core 9, SignalR, Azure Cosmos DB, Redis, and Azure AI Translator (Text Translation REST API). Features multi-language chat rooms, dual authentication (Entra ID + OTP), read receipts, presence tracking, and comprehensive observability.

Status: Production-ready | License: MIT | Tests: 193 passing βœ…

Chat Application

Why This Project?

SignalR Chat demonstrates modern real-time web application patterns with production-grade security, scalability, and observability. Built for learning and as a foundation for real-time features.

What it does:

  • πŸš€ Real-time messaging with SignalR (WebSocket + Server-Sent Events fallback)
  • 🌐 Asynchronous AI-powered translation (Azure AI Translator, background workers; targets derived from room languages)
  • πŸ” Dual authentication: Microsoft Entra ID (enterprise) + OTP fallback (Argon2id hashing, rate limiting)
  • πŸ‘₯ Fixed chat rooms: General, Tech, Random, Sports (no DMs)
  • βœ“ Read receipts, typing indicators, presence tracking
  • 🌍 8 languages supported (i18n with server-side + client-side resources)
  • πŸ“Š Full observability (OpenTelemetry, Azure App Insights, health checks)
  • πŸ”’ Security headers (CSP with nonces, HSTS, frame protection)

What it doesn't do (non-goals):

  • ❌ No direct messages (DMs)
  • ❌ No message editing/deletion
  • ❌ No user registration (fixed users: alice, bob, charlie, dave, eve)
  • ❌ No file uploads or rich media

πŸš€ 5-Minute Quickstart (No Azure Required)

Run locally with in-memory storage (no external dependencies):

# Clone and build
git clone https://github.com/smereczynski/SignalR-Chat.git
cd SignalR-Chat
dotnet build ./src/Chat.sln

# Run with in-memory mode
Testing__InMemory=true dotnet run --project ./src/Chat.Web --urls=http://localhost:5099

# Open browser: http://localhost:5099
# Users: alice, bob, charlie, dave, eve
# OTP codes are printed to console (6-digit codes)

What's running:

  • In-memory OTP storage (no Redis)
  • In-memory session state (no Cosmos DB)
  • Direct SignalR connections (no Azure SignalR Service)
  • All features work except persistence across restarts

➑️ Next: Full local setup with Azure resources | Configuration guide


πŸ“š Documentation

Category Description Link
Getting Started Installation, configuration, first run docs/getting-started/
Architecture System design, diagrams, decisions docs/architecture/
Deployment Azure deployment, Bicep, CI/CD docs/deployment/
Features Authentication, presence, i18n, real-time docs/features/
Development Local setup, testing, debugging docs/development/
Operations Monitoring, diagnostics, health checks docs/operations/
Reference API, configuration, telemetry docs/reference/

Translation deep-dive: docs/architecture/translation-architecture.md

πŸ—οΈ Architecture Overview

graph TD
    Browser[Browser<br/>Razor Pages + SignalR.js]
    
    subgraph "ASP.NET Core 9"
        RazorPages[Razor Pages<br/>Login, UI]
        SignalRHub[SignalR Hub<br/>ChatHub]
        Auth[Cookie Authentication]
        
        RazorPages --> Auth
        SignalRHub --> Auth
    end
    
    Browser -->|HTTPS/WSS| RazorPages
    Browser -->|WebSocket| SignalRHub
    
    Auth --> CosmosDB[(Cosmos DB<br/>Messages, Rooms<br/>Read Status)]
    Auth --> Redis[(Redis<br/>OTP Codes<br/>Rate Limits)]
    
    SignalRHub --> CosmosDB
    SignalRHub --> Redis
    
    subgraph "Optional Azure Services"
        AzureSignalR[Azure SignalR Service<br/>Scale to 1000s]
        ACS[Azure Communication Services<br/>SMS/Email]
        AppInsights[Application Insights<br/>Telemetry]
    end
    
    SignalRHub -.->|Optional| AzureSignalR
    Auth -.->|Notifications| ACS
    ASPNETCore -.->|Metrics/Logs| AppInsights
    
    style Browser fill:#e1f5ff
    style CosmosDB fill:#ffe1e1
    style Redis fill:#ffe1e1
    style Auth fill:#fff4e1
Loading

➑️ Full architecture documentation


πŸ› οΈ Tech Stack

Layer Technology Purpose
Frontend Vanilla JS, Bootstrap 5, SignalR Client UI, real-time updates
Backend ASP.NET Core 9, SignalR Web server, WebSocket hub
Database Azure Cosmos DB (NoSQL) Messages, rooms, read receipts, translations
Cache Redis OTP storage, rate limiting, translation job queue
AI Translation Azure AI Translator (Text Translation REST API) Asynchronous message translation
Auth Cookie authentication + Entra ID/OTP Dual authentication: enterprise SSO + guest OTP
Observability OpenTelemetry, App Insights Metrics, traces, logs
Deployment Azure App Service (Linux), Bicep IaC Infrastructure as Code

πŸ“¦ Project Structure

SignalR-Chat/
β”œβ”€β”€ src/
β”‚   β”œβ”€β”€ Chat.Web/              # Main ASP.NET Core application
β”‚   β”‚   β”œβ”€β”€ Controllers/       # REST API endpoints
β”‚   β”‚   β”œβ”€β”€ Hubs/             # SignalR ChatHub
β”‚   β”‚   β”œβ”€β”€ Pages/            # Razor Pages (login, chat UI)
β”‚   β”‚   β”œβ”€β”€ Services/         # OTP, presence, notifications
β”‚   β”‚   β”œβ”€β”€ Repositories/     # Cosmos DB data access
β”‚   β”‚   └── Middleware/       # Security headers, logging
β”‚   └── Chat.sln              # Solution file
β”œβ”€β”€ tests/
β”‚   β”œβ”€β”€ Chat.Tests/           # Unit tests (included in Chat.sln)
β”‚   β”œβ”€β”€ Chat.IntegrationTests/# Integration tests (not included in Chat.sln)
β”‚   └── Chat.Web.Tests/       # Web/security tests (not included in Chat.sln)
β”œβ”€β”€ infra/
β”‚   └── bicep/                # Azure infrastructure (Bicep)
β”œβ”€β”€ docs/                     # Documentation (detailed guides)
β”œβ”€β”€ .github/
β”‚   └── workflows/            # CI/CD pipelines
└── README.md                 # This file

πŸ§ͺ Testing

193 unit tests (100% passing) in tests/Chat.Tests (this is the only test project included in src/Chat.sln):

# Run all tests
dotnet test src/Chat.sln

# Run specific test project
dotnet test tests/Chat.Tests/

# Optional: additional test projects exist under tests/, but are not referenced by Chat.sln
# dotnet test tests/Chat.IntegrationTests/
# dotnet test tests/Chat.Web.Tests/

Note: additional test projects exist under tests/, but they are not currently referenced by src/Chat.sln.

➑️ Testing guide


πŸš€ Deployment

Azure (Production)

Deploy to Azure using Bicep infrastructure as code:

cd infra/bicep

# Deploy to dev environment
az deployment sub create \
  --location westeurope \
  --template-file main.bicep \
  --parameters main.parameters.dev.bicepparam

# Deploy to production
az deployment sub create \
  --location westeurope \
  --template-file main.bicep \
  --parameters main.parameters.prod.bicepparam

Automated CI/CD: GitHub Actions workflows deploy on push to main

➑️ Deployment guide | Production checklist


πŸ”’ Security

  • βœ… Dual Authentication: Microsoft Entra ID (SSO) + OTP fallback (Argon2id hashing with pepper)
  • βœ… Multi-Tenant Support: AllowedTenants validation, UPN-based authorization
  • βœ… CORS Protection: Origin validation on SignalR hub, prevents CSRF attacks
  • βœ… CSP Headers: Nonce-based Content Security Policy, prevents XSS
  • βœ… HSTS: HTTP Strict Transport Security in production
  • βœ… Rate Limiting: Adaptive throttling for auth endpoints, 5 OTP attempts per 15 minutes
  • βœ… Input Sanitization: Log forgery prevention (CWE-117)
  • βœ… Private Endpoints: VNet integration, no public database access

CORS Configuration

The SignalR hub endpoint (/chatHub) enforces origin validation to prevent CSRF attacks. Configure allowed origins in appsettings.json:

Development (appsettings.Development.json):

{
  "Cors": {
    "AllowAllOrigins": true,
    "AllowedOrigins": [
      "http://localhost:5099",
      "https://localhost:5099"
    ]
  }
}

Production (appsettings.Production.json):

{
  "Cors": {
    "AllowAllOrigins": false,
    "AllowedOrigins": [
      "https://signalrchat-prod-plc.azurewebsites.net"
    ]
  }
}

Azure App Service (set via environment variables):

Cors__AllowedOrigins__0=https://yourdomain.com
Cors__AllowAllOrigins=false

⚠️ Security: AllowAllOrigins MUST be false in production. The application will throw an exception if set to true in non-Development environments.

➑️ Security architecture | Authentication guide


🌍 Localization

Supports 8 languages with both server-side (.resx) and client-side (API) translations:

Languages: English, Polish, German, Czech, Slovak, Ukrainian, Lithuanian, Russian

# Add new language key
# 1. Edit Resources/SharedResources.resx (English default)
# 2. Add to Resources/SharedResources.[culture].resx
# 3. Rebuild to embed resources

# Verify translations
dotnet test tests/Chat.Tests/ --filter "Category=Localization"

➑️ Localization guide


πŸ“Š Observability

Metrics, Traces, Logs via OpenTelemetry β†’ Azure Application Insights (when configured)

Custom Metrics:

  • chat.otp.requests - OTP generation count
  • chat.otp.verifications - OTP verification attempts
  • chat.messages.sent - Messages sent per room
  • chat.connections.active - Active SignalR connections

Health Checks:

  • /healthz - Liveness probe (responds 200 OK)
  • /healthz/ready - Readiness probe (checks Cosmos + Redis)
  • /healthz/metrics - Lightweight in-process metrics snapshot

➑️ Monitoring guide


🀝 Contributing

We welcome contributions! Please read CONTRIBUTING.md for:

  • Code of conduct
  • How to set up your development environment
  • Running tests and linters
  • Commit message conventions
  • Pull request process

Quick start for contributors:

  1. Fork the repository
  2. Create a feature branch: git checkout -b feature/my-feature
  3. Make changes and add tests
  4. Run tests: dotnet test src/Chat.sln
  5. Commit: git commit -m "feat: add my feature"
  6. Push and create a pull request

πŸ“ License

This project is licensed under the MIT License - see LICENSE file for details.


πŸ”— Links


⭐ Key Features Breakdown

Feature Description Implementation
Async Translation Real-time AI translation of messages Redis queue β†’ Background workers β†’ Azure AI Translator β†’ SignalR broadcast
Translation Cache Reduce API costs with 1-hour caching Redis cache with translation results
Translation Retry Automatic retry with exponential backoff Max 3 attempts, high priority requeue
Optimistic Send Messages appear instantly, confirmed async SignalR invoke + server broadcast
Read Receipts Per-user read status per message Cosmos DB ReadReceipts container
Typing Indicators "X is typing…" shown to room SignalR NotifyTyping with debounce
Presence Tracking Online/offline status Redis cache with TTL + SignalR events
Reconnection Auto-reconnect with exponential backoff SignalR.js with custom retry policy
Pagination Load 50 messages, scroll to load more Cosmos DB continuation token
Unread Badges "5 unread in Tech" notifications Redis counter + SignalR updates
Rate Limiting Prevent OTP spam, abuse ASP.NET Core rate limiter

Made with ❀️ using ASP.NET Core 9 & SignalR

About

A real-time chat application using .NET 9 and Azure

Resources

License

Contributing

Stars

Watchers

Forks

Packages

No packages published

Contributors 7