# Encrypted Login Design ## Overview Consolidate login models into Core project and implement RSA-OAEP encryption for login credentials between Blazor WASM client and API. ## Decisions | Decision | Choice | Rationale | |----------|--------|-----------| | Encryption algorithm | RSA-OAEP (SHA-256) | Simple direct encryption, suitable for small payloads | | Key size | 2048-bit | Industry standard, ~20 year security horizon | | Key distribution | Fetch at runtime | Allows key rotation without client redeployment | | Key storage | Auto-generate, persist to file | Self-bootstrapping, no manual key management | | Key expiration | None | Internal enterprise app, manual rotation if needed | ## New Models (Core) All in `JdeScoping.Core/Models/Auth/`: ```csharp // LoginModel.cs - shared form/request model public class LoginModel { [Required(ErrorMessage = "Username is required")] public string Username { get; set; } = string.Empty; [Required(ErrorMessage = "Password is required")] public string Password { get; set; } = string.Empty; } // LoginResultModel.cs - API response (unencrypted) public record LoginResultModel( bool Success, string? ErrorMessage, UserInfo? User); // EncryptedLoginRequest.cs - encrypted payload wrapper public record EncryptedLoginRequest(string EncryptedData); // PublicKeyResponse.cs - public key endpoint response public record PublicKeyResponse(string PublicKeyPem); ``` ## RSA Key Service (Infrastructure) **Interface:** `JdeScoping.Core/Interfaces/IRsaKeyService.cs` ```csharp public interface IRsaKeyService { string GetPublicKeyPem(); byte[] Decrypt(byte[] ciphertext); } ``` **Implementation:** `JdeScoping.Infrastructure/Security/RsaKeyService.cs` - Auto-generates 2048-bit RSA key on first startup if not exists - Persists private key to configurable file path - Exports public key as PEM format - Decrypts using RSA-OAEP with SHA-256 padding ## API Changes **New endpoint:** ``` GET /api/auth/public-key → PublicKeyResponse ``` **Modified endpoint:** ``` POST /api/auth/login Body: EncryptedLoginRequest { EncryptedData: base64 } Response: LoginResultModel ``` Flow: 1. Receive base64-encoded encrypted data 2. Decode and decrypt with RSA private key 3. Deserialize JSON to LoginModel 4. Authenticate via IAuthService 5. Return LoginResultModel (unencrypted) ## Client Changes **New service:** `ICryptoService` / `CryptoService` - Uses `Blazor.SubtleCrypto` NuGet package - Fetches and caches server public key - Encrypts LoginModel JSON with RSA-OAEP - Returns base64-encoded ciphertext **Modified AuthService:** - Injects ICryptoService - Encrypts LoginModel before sending - Sends EncryptedLoginRequest to API ## Files to Delete - `JdeScoping.Api/Models/LoginRequest.cs` - `JdeScoping.Client/Models/LoginModel.cs` ## Dependencies **JdeScoping.Client.csproj:** ```xml ``` ## DI Registration **API/Host:** ```csharp services.AddSingleton(sp => new RsaKeyService(Path.Combine(appDataPath, "rsa-key.bin"))); ``` **Client:** ```csharp services.AddScoped(); ``` ## Test Coverage ### RsaKeyServiceTests (Api.Tests) - GetPublicKeyPem returns valid PEM format - Decrypt with valid ciphertext returns plaintext - Constructor loads existing key from file - Constructor generates and persists new key if missing ### CryptoServiceTests (Client.Tests) - EncryptLoginAsync returns valid base64 - EncryptLoginAsync caches public key (single HTTP call) ### Integration (optional) - Full roundtrip: encrypt on client → decrypt on API → authenticate