Files
jdescopingtool/PLANS/2026-01-03-encrypted-login-design.md
T
Joseph Doherty ec4c8fab87 refactor: relocate options classes to dedicated Options folders
Move configuration options from Core/DataAccess/DataSync/ExcelIO to
dedicated Options folders within each project for better organization.
Update all references and tests accordingly.
2026-01-03 08:55:08 -05:00

135 lines
3.7 KiB
Markdown

# 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
<PackageReference Include="Blazor.SubtleCrypto" Version="..." />
```
## DI Registration
**API/Host:**
```csharp
services.AddSingleton<IRsaKeyService>(sp =>
new RsaKeyService(Path.Combine(appDataPath, "rsa-key.bin")));
```
**Client:**
```csharp
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