API Project Overview
The Relate.Smtp.Api project is the central REST API for Relate Mail. It serves as the primary interface for web, mobile, and desktop clients, and also hosts the bundled web frontend as a single-page application.
Responsibilities
- REST endpoints for email management, composition, labels, filters, preferences, and credentials
- SPA hosting -- serves the bundled React web frontend with fallback to
index.htmlfor client-side routing - SignalR hub at
/hubs/emailfor real-time email notifications - User provisioning -- JIT (just-in-time) user creation from OIDC claims on first login
- Dual authentication -- OIDC/JWT for first-party clients and API key auth for third-party integrations and protocol hosts
Startup Pipeline
The Program.cs startup configures the ASP.NET Core middleware pipeline. The order of middleware registration matters because each component processes requests in sequence.
Service Registration
- Infrastructure --
AddInfrastructure(connectionString)registers the EF Core DbContext, all repository implementations, shared services (background task queue, authentication rate limiter, delivery queue processor), and database health checks - Health checks -- API-specific checks for SignalR and the delivery queue
- Application services --
UserProvisioningService,SmtpCredentialService,EmailFilterService,SignalREmailNotificationService,PushNotificationService - Authentication -- OIDC/JWT Bearer + API key authentication (dual scheme)
- Authorization -- default policy accepts either auth scheme
- CORS -- configured with allowed origins, specific headers, and credentials support
- SignalR -- for the real-time notification hub
- Rate limiting -- three policies (see below)
- OpenTelemetry -- tracing and metrics for ASP.NET Core and HttpClient
Middleware Pipeline
After service registration, middleware is added in this order:
- Auto-migration (development only) -- runs pending EF Core migrations on startup
- OpenAPI (development only) -- serves the OpenAPI specification
- Exception handler -- maps
UnauthorizedAccessExceptionto 401, all others to 500 with generic message - HTTPS redirection
- Security headers -- custom middleware that adds protective headers to every response
- Static files + default files -- serves the bundled web frontend
- CORS
- Authentication
- Authorization
- Rate limiter
- Controllers -- maps API routes
- SignalR hub -- maps
/hubs/email - Health checks -- maps
/healthz(unauthenticated) - SPA fallback --
MapFallbackToFile("index.html")for client-side routing
Rate Limiting
Three rate limiting policies protect the API from abuse:
| Policy | Type | Limit | Window | Queue | Applied To |
|---|---|---|---|---|---|
api | Fixed window | 100 requests | 1 minute | 10 | General API endpoints |
auth | Fixed window | 10 requests | 1 minute | 2 | Authentication endpoints (SmtpCredentials) |
write | Sliding window | 30 requests | 1 minute (6 segments) | 5 | Mutating operations (create, update, delete) |
When a rate limit is exceeded, the API returns HTTP 429 Too Many Requests.
Controllers opt into rate limiting with attributes:
[EnableRateLimiting("api")] // Applied at controller level
[EnableRateLimiting("write")] // Applied on specific write endpoints
[EnableRateLimiting("auth")] // Applied on credential managementSecurity Headers
A custom middleware adds the following headers to every response:
| Header | Value | Purpose |
|---|---|---|
X-Content-Type-Options | nosniff | Prevents MIME type sniffing |
X-Frame-Options | DENY | Prevents clickjacking |
X-XSS-Protection | 1; mode=block | Legacy XSS protection |
Referrer-Policy | strict-origin-when-cross-origin | Controls referrer information |
For non-API routes (i.e., the SPA), an additional Content-Security-Policy header is set:
default-src 'self';
script-src 'self' 'unsafe-inline';
style-src 'self' 'unsafe-inline';
img-src 'self' data: blob:;
font-src 'self';
connect-src 'self' wss: ws:;
frame-ancestors 'none';This CSP allows the web frontend to function (including WebSocket connections for SignalR) while restricting external resource loading.
Auto-Migration
When running in development mode (ASPNETCORE_ENVIRONMENT=Development), the API automatically applies pending EF Core migrations on startup:
if (app.Environment.IsDevelopment())
{
using var scope = app.Services.CreateScope();
var dbContext = scope.ServiceProvider.GetRequiredService<AppDbContext>();
await dbContext.Database.MigrateAsync();
}This eliminates the need to manually run dotnet ef database update during development. In production, migrations should be applied deliberately as part of the deployment process.
Network Configuration
The API listens on port 5000 (HTTP) by default. In the Docker deployment, it sits behind a reverse proxy that handles TLS termination.
The Vite development server proxies /api requests to http://localhost:5000, so during development the web frontend and API appear to share the same origin.
Screenshot
[Screenshot placeholder: API health check response]
TODO: Add screenshot of the /healthz endpoint JSON response