ec4c8fab87
Move configuration options from Core/DataAccess/DataSync/ExcelIO to dedicated Options folders within each project for better organization. Update all references and tests accordingly.
3.7 KiB
3.7 KiB
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/:
// 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
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:
- Receive base64-encoded encrypted data
- Decode and decrypt with RSA private key
- Deserialize JSON to LoginModel
- Authenticate via IAuthService
- Return LoginResultModel (unencrypted)
Client Changes
New service: ICryptoService / CryptoService
- Uses
Blazor.SubtleCryptoNuGet 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.csJdeScoping.Client/Models/LoginModel.cs
Dependencies
JdeScoping.Client.csproj:
<PackageReference Include="Blazor.SubtleCrypto" Version="..." />
DI Registration
API/Host:
services.AddSingleton<IRsaKeyService>(sp =>
new RsaKeyService(Path.Combine(appDataPath, "rsa-key.bin")));
Client:
services.AddScoped<ICryptoService, CryptoService>();
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