using System.Net.Http.Json;
using System.Text.Json;
using JdeScoping.Core.Models.Auth;
using Microsoft.JSInterop;
namespace JdeScoping.Client.Services;
///
/// Encrypts login credentials using Web Crypto API via JavaScript interop.
/// Uses RSA-OAEP with SHA-256 to encrypt credentials before transmission.
///
public class CryptoService : ICryptoService
{
private readonly HttpClient _httpClient;
private readonly IJSRuntime _jsRuntime;
private string? _cachedPublicKeyPem;
private readonly SemaphoreSlim _keyLock = new(1, 1);
public CryptoService(HttpClient httpClient, IJSRuntime jsRuntime)
{
_httpClient = httpClient;
_jsRuntime = jsRuntime;
}
///
public async Task EncryptLoginAsync(LoginModel model)
{
var publicKeyPem = await GetOrFetchPublicKeyAsync();
var json = JsonSerializer.Serialize(model);
// Use JavaScript interop to encrypt with RSA-OAEP via browser's SubtleCrypto API
var encryptedBase64 = await _jsRuntime.InvokeAsync(
"jdeScopingInterop.rsaEncrypt",
publicKeyPem,
json);
return encryptedBase64;
}
private async Task GetOrFetchPublicKeyAsync()
{
if (_cachedPublicKeyPem is not null)
return _cachedPublicKeyPem;
await _keyLock.WaitAsync();
try
{
if (_cachedPublicKeyPem is not null)
return _cachedPublicKeyPem;
var response = await _httpClient.GetFromJsonAsync("api/auth/public-key")
?? throw new InvalidOperationException("Failed to fetch public key from server");
_cachedPublicKeyPem = response.PublicKeyPem;
return _cachedPublicKeyPem;
}
finally
{
_keyLock.Release();
}
}
}