diff --git a/docs/SignalR-Authentication-Guide.md b/docs/SignalR-Authentication-Guide.md new file mode 100644 index 0000000..433b87b --- /dev/null +++ b/docs/SignalR-Authentication-Guide.md @@ -0,0 +1,583 @@ +# Electron.NET SignalR Authentication Guide + +This guide explains the token-based authentication system implemented for SignalR mode in Electron.NET, designed to protect applications in multi-user environments. + +## Table of Contents + +1. [Overview](#overview) +2. [Threat Model](#threat-model) +3. [Authentication Architecture](#authentication-architecture) +4. [Implementation Details](#implementation-details) +5. [Security Properties](#security-properties) +6. [Troubleshooting](#troubleshooting) +7. [FAQ](#faq) + +## Overview + +Electron.NET's SignalR mode includes built-in authentication to ensure that only the Electron process spawned by a specific .NET instance can connect to that instance's HTTP and SignalR endpoints. + +**When is this important?** +- Multi-user Windows Server environments (Terminal Services, RDP) +- Shared development machines with multiple users +- Any scenario where multiple users run the same application simultaneously + +**What does it protect against?** +- User A's Electron process connecting to User B's .NET backend +- Unauthorized port scanning and connection attempts from other users +- Accidental misconfigurations causing cross-user connections + +## Threat Model + +### The Problem + +When multiple users run the same Electron.NET application on a shared server: + +1. Each .NET process binds to a TCP port (e.g., `http://localhost:58971`) +2. TCP ports are visible to **all users** on the machine +3. Without authentication, User A's Electron could connect to User B's backend + +``` +┌─────────────────────────────────────────────────┐ +│ Windows Server (Terminal Services) │ +├─────────────────────────────────────────────────┤ +│ User A Session │ +│ ├─ .NET Process (localhost:58971) │ +│ └─ Electron Process │ +│ │ +│ User B Session │ +│ ├─ .NET Process (localhost:61234) │ +│ └─ Electron Process (could connect to 58971) │ ❌ Prevent this! +└─────────────────────────────────────────────────┘ +``` + +### The Solution + +Each .NET process generates a unique authentication token when launching Electron. Only requests with the correct token are allowed to connect. + +``` +┌─────────────────────────────────────────────────┐ +│ User A Session │ +│ ├─ .NET (token: abc123...) │ +│ └─ Electron (has token abc123...) │ ✅ Authenticated +│ │ +│ User B Session │ +│ ├─ .NET (token: xyz789...) │ +│ └─ Electron (has token xyz789...) │ ✅ Authenticated +│ │ +│ ❌ User B's Electron → User A's .NET │ +│ (lacks token abc123...) │ ❌ Rejected (401) +└─────────────────────────────────────────────────┘ +``` + +## Authentication Architecture + +### Flow Diagram + +``` +┌──────────────┐ ┌─────────────────┐ +│ .NET Process │ │ Electron Process│ +└──────┬───────┘ └────────┬────────┘ + │ │ + │ 1. Generate Token (GUID) │ + │ Token: a3f8b2c1d4e5... │ + │ │ + │ 2. Launch Electron │ + │ --authtoken=a3f8b2c1d4e5... │ + │────────────────────────────────────────────────────>│ + │ │ + │ │ 3. Extract Token + │ │ global.authToken = ... + │ │ + │ │ 4. Initial Page Load + │<────────────────────────────────────────────────────│ + │ GET /?token=a3f8b2c1d4e5... │ + │ │ + │ 5. Validate Token │ + │ ✓ Valid → Set Cookie │ + │────────────────────────────────────────────────────>│ + │ HTTP 200 + Set-Cookie: ElectronAuth=... │ + │ │ + │ │ 6. SignalR Connection + │<────────────────────────────────────────────────────│ + │ GET /electron-hub?token=a3f8b2c1d4e5... │ + │ │ + │ 7. Validate Token, Set Cookie │ + │────────────────────────────────────────────────────>│ + │ HTTP 200 + Set-Cookie │ + │ │ + │ │ 8. Subsequent Requests + │<────────────────────────────────────────────────────│ + │ GET /api/data │ + │ Cookie: ElectronAuth=a3f8b2c1d4e5... │ + │ │ + │ 9. Validate Cookie │ + │────────────────────────────────────────────────────>│ + │ HTTP 200 (authenticated) │ + │ │ +``` + +### Key Steps + +1. **Token Generation**: .NET generates 128-bit cryptographic random GUID +2. **Command-Line Passing**: Token passed to Electron via `--authtoken` parameter +3. **Token Extraction**: Electron stores token in `global.authToken` +4. **URL Appending**: Token appended to initial page and SignalR connection URLs +5. **Middleware Validation**: Every HTTP request validated by middleware +6. **Cookie Setting**: Valid token results in secure HttpOnly cookie +7. **Cookie-Based Requests**: Subsequent requests use cookie (no token in URL) + +## Implementation Details + +### 1. Token Generation (.NET) + +**File**: `src/ElectronNET.AspNet/Runtime/Controllers/RuntimeControllerAspNetDotnetFirstSignalR.cs` + +```csharp +private void LaunchElectron() +{ + // Generate secure authentication token (128-bit cryptographic random GUID) + this.authenticationToken = Guid.NewGuid().ToString("N"); // 32 hex chars + + // Register token with authentication service + this.authenticationService.SetExpectedToken(this.authenticationToken); + + // Launch Electron with token + var args = $"--unpackeddotnetsignalr --electronurl={this.actualUrl} --authtoken={this.authenticationToken}"; + this.electronProcess = new ElectronProcessActive(isUnPacked, ElectronNetRuntime.ElectronExecutable, args, this.port.Value); + _ = this.electronProcess.Start(); +} +``` + +**Token Format**: 32-character hexadecimal string (e.g., `a3f8b2c1d4e5f6a7b8c9d0e1f2a3b4c5`) + +### 2. Authentication Service (.NET) + +**File**: `src/ElectronNET.AspNet/Services/ElectronAuthenticationService.cs` + +```csharp +public class ElectronAuthenticationService : IElectronAuthenticationService +{ + private string _expectedToken; + private readonly object _lock = new object(); + + public void SetExpectedToken(string token) + { + lock (_lock) + { + _expectedToken = token; + } + } + + public bool ValidateToken(string token) + { + if (string.IsNullOrEmpty(token)) + return false; + + lock (_lock) + { + if (string.IsNullOrEmpty(_expectedToken)) + return false; + + // Constant-time comparison prevents timing attacks + return ConstantTimeEquals(token, _expectedToken); + } + } + + private static bool ConstantTimeEquals(string a, string b) + { + if (a == null || b == null || a.Length != b.Length) + return false; + + var result = 0; + for (int i = 0; i < a.Length; i++) + { + result |= a[i] ^ b[i]; + } + return result == 0; + } +} +``` + +**Key Features**: +- Thread-safe with lock +- Constant-time comparison (prevents timing attacks) +- Singleton lifetime (one per .NET instance) + +### 3. Authentication Middleware (.NET) + +**File**: `src/ElectronNET.AspNet/Middleware/ElectronAuthenticationMiddleware.cs` + +```csharp +public async Task InvokeAsync(HttpContext context) +{ + // Check if authentication cookie exists + var authCookie = context.Request.Cookies["ElectronAuth"]; + + if (!string.IsNullOrEmpty(authCookie)) + { + if (_authService.ValidateToken(authCookie)) + { + await _next(context); + return; + } + else + { + _logger.LogWarning("Invalid cookie for path {Path}", context.Request.Path); + context.Response.StatusCode = 401; + await context.Response.WriteAsync("Unauthorized: Invalid authentication"); + return; + } + } + + // No cookie - check for token in query string + var token = context.Request.Query["token"].ToString(); + + if (!string.IsNullOrEmpty(token) && _authService.ValidateToken(token)) + { + // Valid token - set cookie for future requests + context.Response.Cookies.Append("ElectronAuth", token, new CookieOptions + { + HttpOnly = true, + SameSite = SameSiteMode.Strict, + Path = "/", + Secure = false, // localhost is HTTP + IsEssential = true + }); + + await _next(context); + return; + } + + // Reject - no valid cookie or token + context.Response.StatusCode = 401; + await context.Response.WriteAsync("Unauthorized: Authentication required"); +} +``` + +**Middleware Order** (IMPORTANT): +```csharp +app.UseMiddleware(); // ← FIRST +app.UseRouting(); +app.UseStaticFiles(); +app.UseAntiforgery(); +app.MapHub("/electron-hub"); +app.MapRazorComponents(); +``` + +### 4. Token Extraction (Electron) + +**File**: `src/ElectronNET.Host/main.js` + +```javascript +// Extract authentication token from command-line +if (app.commandLine.hasSwitch('authtoken')) { + global.authToken = app.commandLine.getSwitchValue('authtoken'); +} +``` + +### 5. Token in URLs (Electron) + +**Initial Page Load** (`src/ElectronNET.Host/api/browserWindows.js`): +```javascript +// Append token to window URL +if (global.authToken) { + const separator = electronUrl.includes('?') ? '&' : '?'; + electronUrl = `${electronUrl}${separator}token=${global.authToken}`; +} +window.loadURL(electronUrl); +``` + +**SignalR Connection** (`src/ElectronNET.Host/api/signalr-bridge.js`): +```javascript +async connect() { + // Append token to SignalR hub URL + const connectionUrl = this.authToken ? + `${this.hubUrl}?token=${this.authToken}` : + this.hubUrl; + + this.connection = new signalR.HubConnectionBuilder() + .withUrl(connectionUrl) + .build(); + + await this.connection.start(); +} +``` + +### 6. Service Registration (Application) + +**File**: Your application's `Program.cs` + +```csharp +var builder = WebApplication.CreateBuilder(args); + +// Register authentication service as singleton +builder.Services.AddSingleton(); + +builder.Services.AddElectron(); + +var app = builder.Build(); + +// Register middleware BEFORE UseRouting() +app.UseMiddleware(); + +app.UseRouting(); +app.MapHub("/electron-hub"); +app.Run(); +``` + +## Security Properties + +### Cookie Configuration + +| Property | Value | Purpose | +|----------|-------|---------| +| `HttpOnly` | `true` | Prevents JavaScript access (XSS protection) | +| `SameSite` | `Strict` | Prevents CSRF attacks | +| `Path` | `/` | Cookie sent with all requests | +| `Secure` | `false` | Cannot use on localhost HTTP | +| `IsEssential` | `true` | Required for app to function | +| **Lifetime** | Session | Expires when Electron closes | + +### Token Properties + +- **Entropy**: 128 bits (2^128 possible values) +- **Format**: 32 hexadecimal characters (GUID without hyphens) +- **Generation**: `Guid.NewGuid()` uses cryptographically secure RNG +- **Lifetime**: Entire application session +- **Uniqueness**: Each .NET instance generates unique token + +### What's Protected + +✅ **All HTTP endpoints**: +- Blazor Server pages (`/`, `/counter`, etc.) +- Static files (`/css/app.css`, `/js/script.js`) +- API endpoints (custom controllers) +- SignalR hub (`/electron-hub`) + +✅ **Both transport modes**: +- Initial token-based authentication (query parameter) +- Cookie-based subsequent requests + +✅ **Cross-user isolation**: +- Different users = different tokens +- Invalid token = 401 Unauthorized + +### What's NOT Protected Against + +❌ **Same-user attacks** (by design): +- Process memory inspection +- Debugger attachment +- Command-line parameter visibility + +**Rationale**: A malicious process running as the same user already has full access to: +- Process memory (can read token from RAM) +- Cookies (stored in Electron's data directory) +- All files owned by the user + +Token-based authentication focuses on **cross-user isolation**, not same-user security. + +## Troubleshooting + +### Problem: 401 Unauthorized on Initial Load + +**Symptoms**: +``` +GET / HTTP/1.1 +Response: 401 Unauthorized +``` + +**Possible Causes**: +1. Token not passed to Electron +2. Token not appended to URL +3. Middleware rejecting valid token + +**Debugging Steps**: + +1. Check Electron command-line: + ```javascript + console.log('Auth Token:', global.authToken); + ``` + Should print 32-character hex string. + +2. Check URL in browser window: + ```javascript + console.log('Loading URL:', electronUrl); + ``` + Should include `?token=` parameter. + +3. Check .NET logs: + ``` + [Warning] Authentication failed: Invalid token (prefix: a3f8b2c1...) + ``` + +4. Verify middleware registration: + ```csharp + app.UseMiddleware(); // Before UseRouting() + ``` + +### Problem: SignalR Connection Fails + +**Symptoms**: +``` +[SignalRBridge] Authentication failed: The authentication token is invalid or missing. +``` + +**Possible Causes**: +1. Token not passed to `SignalRBridge` constructor +2. Token not appended to hub URL +3. Cookie not being sent + +**Debugging Steps**: + +1. Check token passed to SignalRBridge: + ```javascript + console.log('SignalRBridge token:', this.authToken); + ``` + +2. Check connection URL: + ```javascript + console.log('Hub URL:', connectionUrl); + ``` + Should be `http://localhost:PORT/electron-hub?token=`. + +3. Enable verbose logging: + ```javascript + .configureLogging(signalR.LogLevel.Debug) + ``` + +### Problem: Cookie Not Persisting + +**Symptoms**: Every request includes token in URL, cookie never set. + +**Possible Causes**: +1. Cookie settings incompatible with browser +2. Middleware not setting cookie +3. Response headers not sent + +**Debugging Steps**: + +1. Check response headers: + ``` + Set-Cookie: ElectronAuth=a3f8b2c1...; Path=/; HttpOnly; SameSite=Strict + ``` + +2. Check subsequent requests: + ``` + Cookie: ElectronAuth=a3f8b2c1... + ``` + +3. Enable middleware logging: + ```csharp + _logger.LogInformation("Setting cookie for path {Path}", path); + ``` + +### Problem: Token Visible in Process List + +**Observation**: +```powershell +wmic process where "name='electron.exe'" get commandline +...--authtoken=a3f8b2c1d4e5f6a7b8c9d0e1f2a3b4c5... +``` + +**Is this a problem?** No, by design. + +**Explanation**: +- Command-line parameters are visible to same-user processes +- This is acceptable because: + - Same user already has access to process memory + - Same user can read cookies from Electron's data directory + - Token-based auth protects against **other users**, not same-user processes + +**If you need same-user protection**: Use OS-level access controls (file permissions, process isolation) or consider named pipes with ACLs. + +## FAQ + +### Q: Why use tokens instead of Process ID validation? + +**A**: PIDs can be recycled and reused, making validation unreliable. Additionally: +- PIDs don't provide cryptographic security +- Parent-child validation is platform-specific +- Adds complexity without meaningful security benefit + +Token-based authentication provides: +- Cryptographic randomness (128-bit entropy) +- Simple cross-platform implementation +- No race conditions or PID recycling issues + +### Q: Why not use HTTPS with certificates? + +**A**: Localhost doesn't support HTTPS certificates easily: +- Self-signed certificates trigger browser warnings +- Certificate management adds complexity +- Token-based auth provides equivalent security for localhost IPC + +### Q: Can I disable authentication? + +**A**: Not recommended, but possible by removing middleware registration: + +```csharp +// Remove this line: +// app.UseMiddleware(); +``` + +**Warning**: Only do this if: +- Application runs on single-user machines only +- No Terminal Services / RDP access +- You understand the security implications + +### Q: Does this work with hot reload? + +**A**: Yes, cookie persists across hot reload as long as Electron process keeps running. + +### Q: What about multiple Electron windows? + +**A**: All windows in the same Electron process share cookies automatically. Authentication works seamlessly across multiple windows. + +### Q: How do I test authentication? + +**Test 1 - Happy Path**: +1. Run application normally +2. Check logs for "Authentication successful" +3. Verify cookie is set (DevTools → Application → Cookies) +4. Subsequent requests should not include token in URL + +**Test 2 - Invalid Token**: +1. Modify token in browser URL: `?token=invalid` +2. Should receive 401 Unauthorized +3. Check logs for "Authentication failed: Invalid token" + +**Test 3 - No Token**: +1. Open browser manually to `http://localhost:PORT/` +2. Should receive 401 Unauthorized +3. Check logs for "Authentication failed: No cookie or token" + +**Test 4 - Multi-User** (Windows Server/Terminal Services): +1. Launch app as User A +2. In User B session, try to connect to User A's port +3. Should receive 401 Unauthorized + +### Q: What about packaged applications? + +**A**: Authentication works identically in packaged mode. The `--authtoken` parameter is included in the packaged Electron executable. + +### Q: Can I customize the cookie name? + +**A**: Yes, modify `AuthCookieName` constant in `ElectronAuthenticationMiddleware.cs`: + +```csharp +private const string AuthCookieName = "MyCustomCookieName"; +``` + +## Summary + +Electron.NET's token-based authentication provides: + +✅ **Security**: 128-bit entropy, constant-time comparison, secure cookies +✅ **Simplicity**: Automatic token generation and validation +✅ **Compatibility**: Works with Blazor, SignalR, and static files +✅ **Monitoring**: Structured logging for security events +✅ **Multi-User**: Cross-user isolation on shared servers + +The authentication system is **enabled by default** in SignalR mode and requires minimal configuration. For most applications, simply register the services and middleware - everything else happens automatically. + +For additional help or questions, see the [SignalR Implementation Summary](SignalR-Implementation-Summary.md) or open an issue on GitHub. diff --git a/docs/SignalR-Implementation-Summary.md b/docs/SignalR-Implementation-Summary.md index 27e4ec0..72c0c31 100644 --- a/docs/SignalR-Implementation-Summary.md +++ b/docs/SignalR-Implementation-Summary.md @@ -228,6 +228,156 @@ Blazor Server already uses SignalR for component communication (`/_blazor` hub). - Fixed middleware order: `UseAntiforgery()` must be between `UseRouting()` and `UseEndpoints()` - Updated scoped CSS asset reference to use lowercase name matching .NET 9+ convention +## Authentication & Security + +### Token-Based Authentication (Multi-User Protection) + +SignalR mode includes built-in authentication to prevent unauthorized connections in multi-user scenarios (e.g., Windows Server with Terminal Services/RDP). + +**Threat Model**: On shared servers, multiple users can run the same application simultaneously. Without authentication, User A's Electron process could potentially connect to User B's ASP.NET backend. + +**Solution**: Token-based authentication with secure cookies. + +### Authentication Flow + +1. **.NET generates token**: When launching Electron, `RuntimeControllerAspNetDotnetFirstSignalR` generates a cryptographically secure GUID (128-bit entropy) +2. **Token passed via command-line**: Electron receives `--authtoken=` parameter +3. **Token appended to URLs**: + - Initial page load: `http://localhost:PORT/?token=` + - SignalR connection: `http://localhost:PORT/electron-hub?token=` +4. **Middleware validates token**: `ElectronAuthenticationMiddleware` checks every HTTP request +5. **Cookie set on first request**: After successful token validation, secure HttpOnly cookie is set +6. **Subsequent requests use cookie**: No token in URLs after initial authentication + +### Security Properties + +- **Cookie Settings**: + - `HttpOnly`: true (prevents JavaScript access, XSS protection) + - `SameSite`: Strict (prevents CSRF) + - `Path`: / (applies to all routes) + - `Secure`: false (localhost is HTTP, not HTTPS) + - `IsEssential`: true (required for app to function) + - **Lifetime**: Session scope (expires when Electron closes) + +- **Token Validation**: + - Constant-time string comparison (prevents timing attacks) + - Token stored in singleton service (one per .NET instance) + - Never logged in full (only first 8 characters for debugging) + +- **Protection Scope**: + - All HTTP endpoints (Blazor pages, static files, API calls) + - SignalR hub connection (negotiate and all hub traffic) + - Both initial request and cookie-based requests validated + +### What This Protects Against + +✅ **Protected**: +- Cross-user connections (User A → User B's backend) +- Port scanning attacks from other users +- Accidental connections from misconfigured processes + +❌ **NOT Protected Against** (By Design): +- Malicious same-user processes with debugger access +- Process memory inspection tools (same privilege level) +- Command-line parameter visibility (same user can see all processes) + +**Rationale**: Same-user attacks already have full access to process memory, files, and cookies. Token-based authentication focuses on cross-user isolation, which is the primary threat in multi-user environments. + +### Implementation Components + +1. **IElectronAuthenticationService** (`src/ElectronNET.AspNet/Services/`) + - Singleton service storing expected token + - Thread-safe with lock-based validation + - Constant-time comparison to prevent timing attacks + +2. **ElectronAuthenticationMiddleware** (`src/ElectronNET.AspNet/Middleware/`) + - Validates every HTTP request before routing + - Checks cookie first, then token query parameter + - Sets cookie on first valid token + - Returns 401 for invalid/missing authentication + - Structured logging for security monitoring + +3. **Token Generation** (`RuntimeControllerAspNetDotnetFirstSignalR.cs`) + - `Guid.NewGuid().ToString("N")` = 32 hex characters + - Called in `LaunchElectron()` method + - Registered with authentication service immediately + +4. **Electron Integration** (`main.js`, `signalr-bridge.js`) + - Extracts token from `--authtoken` parameter + - Stores in `global.authToken` for module access + - Appends to browser window URL and SignalR connection URL + +### Usage in Custom Applications + +Authentication is **enabled by default** in SignalR mode. No additional configuration required beyond service registration: + +```csharp +var builder = WebApplication.CreateBuilder(args); + +// Register authentication service (singleton) +builder.Services.AddSingleton(); + +builder.Services.AddElectron(); + +var app = builder.Build(); + +// Register middleware BEFORE UseRouting() +app.UseMiddleware(); + +app.UseRouting(); +app.MapHub("/electron-hub"); +app.MapRazorComponents().AddInteractiveServerRenderMode(); +app.Run(); +``` + +The rest is automatic: +- Token generation happens when Electron launches +- Token validation happens on every request +- Cookie management is handled by the middleware + +### Logging & Monitoring + +Authentication events are logged with structured logging: + +**Successful authentication**: +``` +[Information] Authentication successful: Setting cookie for path / +``` + +**Failed authentication**: +``` +[Warning] Authentication failed: Invalid token (prefix: a3f8b2c1...) for path / from 127.0.0.1 +[Warning] Authentication failed: No cookie or token provided for path /api/data from 127.0.0.1 +[Warning] Authentication failed: Invalid cookie for path /_blazor from 127.0.0.1 +``` + +**SignalR connection failures**: +``` +[SignalRBridge] Authentication failed: The authentication token is invalid or missing. +[SignalRBridge] Please ensure the --authtoken parameter is correctly passed to Electron. +``` + +Log failed authentication attempts for security monitoring and troubleshooting. + +### Testing Multi-User Scenarios + +To test authentication in multi-user environments: + +1. **Run as different Windows users**: + ```powershell + # User A session + dotnet run + + # User B session (different RDP/Terminal Services session) + dotnet run + ``` + +2. **Verify isolation**: User A's Electron cannot access User B's backend +3. **Check logs**: Failed auth attempts should be logged +4. **Monitor tokens**: Each instance generates unique token + +For development testing on single-user machines, simulate by running multiple instances and attempting to connect with wrong/missing tokens. + ## Backward Compatibility This is a **new optional startup mode** - all existing modes continue to work unchanged: @@ -244,15 +394,19 @@ Existing applications do not need to change. SignalR mode is opt-in via command- - `src/ElectronNET.AspNet/Bridge/SignalRFacade.cs` (225 lines) - `src/ElectronNET.AspNet/Hubs/ElectronHub.cs` (108 lines) - `src/ElectronNET.AspNet/Runtime/Controllers/RuntimeControllerAspNetDotnetFirstSignalR.cs` (163 lines) +- `src/ElectronNET.AspNet/Services/IElectronAuthenticationService.cs` (20 lines) +- `src/ElectronNET.AspNet/Services/ElectronAuthenticationService.cs` (65 lines) +- `src/ElectronNET.AspNet/Middleware/ElectronAuthenticationMiddleware.cs` (105 lines) - `src/ElectronNET.Host/api/signalr-bridge.js` (125 lines) **Modified Files**: - `src/ElectronNET.AspNet/API/WebHostBuilderExtensions.cs` - Added SignalR service registration -- `src/ElectronNET.Host/main.js` - Added SignalR startup flow +- `src/ElectronNET.Host/main.js` - Added SignalR startup flow and token extraction +- `src/ElectronNET.Host/api/browserWindows.js` - Token appended to window URLs - `src/ElectronNET.Host/package.json` - Added `@microsoft/signalr` dependency -- `src/ElectronNET.Samples.BlazorSignalR/Program.cs` - Sample implementation +- `src/ElectronNET.Samples.BlazorSignalR/Program.cs` - Sample with authentication -**Total Changes**: ~1,030 lines added +**Total Changes**: ~1,220 lines added ## Testing Recommendations @@ -290,3 +444,7 @@ The implementation is considered complete and functional: - ✅ Both SignalR hubs coexist (Electron + Blazor) - ✅ Clean codebase with minimal debug logging - ✅ Comprehensive inline documentation +- ✅ Token-based authentication for multi-user scenarios +- ✅ Secure cookie-based session management +- ✅ Structured logging for security monitoring +- ✅ Protection against cross-user connection attempts