# SignalR Real-time Communication Implementation ## Overview This document describes the SignalR real-time communication infrastructure implemented for ColaFlow. The implementation provides real-time updates for project collaboration, issue tracking, and user notifications with multi-tenant isolation and JWT authentication. ## Implementation Date 2025-11-04 ## Components Implemented ### 1. Hub Infrastructure #### BaseHub (`src/ColaFlow.API/Hubs/BaseHub.cs`) Base class for all SignalR hubs with: - **JWT Authentication**: All hubs require authentication via `[Authorize]` attribute - **Multi-tenant Isolation**: Automatically adds users to tenant-specific groups on connection - **User/Tenant Extraction**: Helper methods to extract user ID and tenant ID from JWT claims - **Connection Lifecycle**: Logging for connect/disconnect events **Key Features:** - `GetCurrentUserId()`: Extracts user ID from JWT token (sub or user_id claim) - `GetCurrentTenantId()`: Extracts tenant ID from JWT token (tenant_id claim) - `GetTenantGroupName(Guid tenantId)`: Returns standardized tenant group name - Automatic group membership on connection - Error handling with connection abort on authentication failures #### ProjectHub (`src/ColaFlow.API/Hubs/ProjectHub.cs`) Hub for project-level real-time collaboration: **Methods:** - `JoinProject(Guid projectId)`: Join a project room to receive updates - `LeaveProject(Guid projectId)`: Leave a project room - `SendTypingIndicator(Guid projectId, Guid issueId, bool isTyping)`: Send typing indicators **Client Events:** - `UserJoinedProject`: Notifies when a user joins a project - `UserLeftProject`: Notifies when a user leaves a project - `TypingIndicator`: Real-time typing indicators for issue editing - `ProjectUpdated`: General project updates - `IssueCreated`: New issue created - `IssueUpdated`: Issue updated - `IssueDeleted`: Issue deleted - `IssueStatusChanged`: Issue status changed #### NotificationHub (`src/ColaFlow.API/Hubs/NotificationHub.cs`) Hub for user-level notifications: **Methods:** - `MarkAsRead(Guid notificationId)`: Mark a notification as read **Client Events:** - `Notification`: General notifications - `NotificationRead`: Confirmation of read status ### 2. Realtime Notification Service #### IRealtimeNotificationService (`src/ColaFlow.API/Services/IRealtimeNotificationService.cs`) Service interface for sending real-time notifications from application layer. **Project-level Methods:** - `NotifyProjectUpdate(Guid tenantId, Guid projectId, object data)` - `NotifyIssueCreated(Guid tenantId, Guid projectId, object issue)` - `NotifyIssueUpdated(Guid tenantId, Guid projectId, object issue)` - `NotifyIssueDeleted(Guid tenantId, Guid projectId, Guid issueId)` - `NotifyIssueStatusChanged(Guid tenantId, Guid projectId, Guid issueId, string oldStatus, string newStatus)` **User-level Methods:** - `NotifyUser(Guid userId, string message, string type = "info")` - `NotifyUsersInTenant(Guid tenantId, string message, string type = "info")` #### RealtimeNotificationService (`src/ColaFlow.API/Services/RealtimeNotificationService.cs`) Implementation of the notification service using `IHubContext`. ### 3. Configuration #### Program.cs Updates **SignalR Configuration:** ```csharp builder.Services.AddSignalR(options => { options.EnableDetailedErrors = builder.Environment.IsDevelopment(); options.ClientTimeoutInterval = TimeSpan.FromSeconds(60); options.HandshakeTimeout = TimeSpan.FromSeconds(15); options.KeepAliveInterval = TimeSpan.FromSeconds(15); }); ``` **CORS Configuration (SignalR-compatible):** ```csharp builder.Services.AddCors(options => { options.AddPolicy("AllowFrontend", policy => { policy.WithOrigins("http://localhost:3000", "https://localhost:3000") .AllowAnyHeader() .AllowAnyMethod() .AllowCredentials(); // Required for SignalR }); }); ``` **JWT Authentication for SignalR (Query String Support):** ```csharp options.Events = new JwtBearerEvents { OnMessageReceived = context => { var accessToken = context.Request.Query["access_token"]; var path = context.HttpContext.Request.Path; if (!string.IsNullOrEmpty(accessToken) && path.StartsWithSegments("/hubs")) { context.Token = accessToken; } return Task.CompletedTask; } }; ``` **Hub Endpoints:** ```csharp app.MapHub("/hubs/project"); app.MapHub("/hubs/notification"); ``` **Service Registration:** ```csharp builder.Services.AddScoped(); ``` ### 4. Test Controller #### SignalRTestController (`src/ColaFlow.API/Controllers/SignalRTestController.cs`) Controller for testing SignalR functionality: **Endpoints:** - `POST /api/SignalRTest/test-user-notification`: Send notification to current user - `POST /api/SignalRTest/test-tenant-notification`: Send notification to entire tenant - `POST /api/SignalRTest/test-project-update`: Send project update notification - `POST /api/SignalRTest/test-issue-status-change`: Send issue status change notification - `GET /api/SignalRTest/connection-info`: Get connection information for debugging ## SignalR Hub Endpoints | Hub | Endpoint | Description | |-----|----------|-------------| | ProjectHub | `/hubs/project` | Project-level real-time collaboration | | NotificationHub | `/hubs/notification` | User-level notifications | ## Authentication SignalR hubs use JWT authentication with two methods: 1. **Authorization Header**: Standard `Bearer {token}` in HTTP headers 2. **Query String**: `?access_token={token}` for WebSocket upgrade requests All hubs are protected with `[Authorize]` attribute and require valid JWT tokens. ## Multi-Tenant Isolation Users are automatically added to their tenant group (`tenant-{tenantId}`) on connection. This ensures: - Notifications are only sent within tenant boundaries - Cross-tenant data leakage is prevented - Group-based broadcasting is efficient ## Client Connection Example ### JavaScript/TypeScript (SignalR Client) ```typescript import * as signalR from "@microsoft/signalr"; const connection = new signalR.HubConnectionBuilder() .withUrl("https://localhost:5001/hubs/project", { accessTokenFactory: () => getAccessToken() // Your JWT token }) .withAutomaticReconnect() .build(); // Listen for events connection.on("IssueCreated", (issue) => { console.log("New issue:", issue); }); connection.on("IssueStatusChanged", (data) => { console.log("Issue status changed:", data); }); // Start connection await connection.start(); // Join project await connection.invoke("JoinProject", projectId); // Send typing indicator await connection.invoke("SendTypingIndicator", projectId, issueId, true); ``` ## Integration with Domain Events To send SignalR notifications from application layer: ```csharp public class IssueCreatedEventHandler : INotificationHandler { private readonly IRealtimeNotificationService _realtimeNotification; public async Task Handle(IssueCreatedEvent notification, CancellationToken cancellationToken) { await _realtimeNotification.NotifyIssueCreated( notification.TenantId, notification.ProjectId, new { Id = notification.IssueId, Title = notification.Title, Status = notification.Status } ); } } ``` ## Testing ### Test Endpoints 1. **Get Connection Info**: ```bash curl -X GET https://localhost:5001/api/SignalRTest/connection-info \ -H "Authorization: Bearer {your-jwt-token}" ``` 2. **Test User Notification**: ```bash curl -X POST https://localhost:5001/api/SignalRTest/test-user-notification \ -H "Authorization: Bearer {your-jwt-token}" \ -H "Content-Type: application/json" \ -d "\"Test notification message\"" ``` 3. **Test Tenant Notification**: ```bash curl -X POST https://localhost:5001/api/SignalRTest/test-tenant-notification \ -H "Authorization: Bearer {your-jwt-token}" \ -H "Content-Type: application/json" \ -d "\"Test tenant message\"" ``` 4. **Test Project Update**: ```bash curl -X POST https://localhost:5001/api/SignalRTest/test-project-update \ -H "Authorization: Bearer {your-jwt-token}" \ -H "Content-Type: application/json" \ -d '{"projectId":"00000000-0000-0000-0000-000000000000","message":"Test update"}' ``` ### Build Status ✅ Build successful with no errors or warnings ```bash cd colaflow-api dotnet build src/ColaFlow.API/ColaFlow.API.csproj ``` ## Success Criteria Checklist - [x] SignalR infrastructure added (built-in .NET 9 SignalR) - [x] Created BaseHub, ProjectHub, NotificationHub - [x] Configured SignalR in Program.cs with CORS and JWT - [x] Implemented IRealtimeNotificationService - [x] Hub supports multi-tenant isolation (automatic tenant group membership) - [x] Hub supports JWT authentication (Bearer + query string) - [x] Created test controller (SignalRTestController) - [x] Compilation successful with no errors ## Files Created/Modified **Created:** - `src/ColaFlow.API/Hubs/BaseHub.cs` - `src/ColaFlow.API/Hubs/ProjectHub.cs` - `src/ColaFlow.API/Hubs/NotificationHub.cs` - `src/ColaFlow.API/Services/IRealtimeNotificationService.cs` - `src/ColaFlow.API/Services/RealtimeNotificationService.cs` - `src/ColaFlow.API/Controllers/SignalRTestController.cs` **Modified:** - `src/ColaFlow.API/Program.cs` (SignalR configuration, CORS, JWT, hub endpoints) ## Next Steps 1. **Frontend Integration**: Implement SignalR client in Next.js frontend 2. **Domain Event Integration**: Wire up notification service to domain events 3. **Permission Validation**: Add authorization checks in ProjectHub.JoinProject() 4. **User Connection Mapping**: Implement user-to-connection tracking for targeted notifications 5. **Scalability**: Consider Redis backplane for multi-server deployments 6. **Monitoring**: Add SignalR performance metrics and connection monitoring ## Notes - SignalR is built-in to .NET 9.0 ASP.NET Core, no separate NuGet package required - CORS policy updated to include `AllowCredentials()` for SignalR compatibility - JWT authentication supports both HTTP Authorization header and query string for WebSocket upgrade - All hubs automatically enforce tenant isolation via BaseHub - Notification service can be injected into any application service or event handler