Add complete SignalR infrastructure for real-time project collaboration and notifications with multi-tenant isolation and JWT authentication. Changes: - Created BaseHub with multi-tenant isolation and JWT authentication helpers - Created ProjectHub for real-time project collaboration (join/leave, typing indicators) - Created NotificationHub for user-level notifications - Implemented IRealtimeNotificationService for application layer integration - Configured SignalR in Program.cs with CORS and JWT query string support - Added SignalRTestController for connection testing - Documented hub endpoints, client events, and integration examples Features: - Multi-tenant isolation via automatic tenant group membership - JWT authentication (Bearer header + query string for WebSocket) - Hub endpoints: /hubs/project, /hubs/notification - Project-level events: IssueCreated, IssueUpdated, IssueStatusChanged, etc. - User-level notifications with tenant-wide broadcasting - Test endpoints for validation 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
315 lines
10 KiB
Markdown
315 lines
10 KiB
Markdown
# 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<T>`.
|
|
|
|
### 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<ProjectHub>("/hubs/project");
|
|
app.MapHub<NotificationHub>("/hubs/notification");
|
|
```
|
|
|
|
**Service Registration:**
|
|
```csharp
|
|
builder.Services.AddScoped<IRealtimeNotificationService, RealtimeNotificationService>();
|
|
```
|
|
|
|
### 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<IssueCreatedEvent>
|
|
{
|
|
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
|