IdentityShroud/IdentityShroud.Core/Services/ClientService.cs
eelke 644b005f2a Support rotation of master key.
The EncryptionService now loads a set of keys and uses the active one to encrypt and selects key based on keyid during decryption. Introduced EncryptedValue to hold keyId and encrypted data.

(There are no intermeddiate keys yet)
2026-02-24 06:32:58 +01:00

64 lines
No EOL
1.7 KiB
C#

using System.Security.Cryptography;
using IdentityShroud.Core.Contracts;
using IdentityShroud.Core.Model;
using Microsoft.EntityFrameworkCore;
namespace IdentityShroud.Core.Services;
public class ClientService(
Db db,
IEncryptionService cryptor,
IClock clock) : IClientService
{
public async Task<Result<Client>> Create(Guid realmId, ClientCreateRequest request, CancellationToken ct = default)
{
Client client = new()
{
RealmId = realmId,
ClientId = request.ClientId,
Name = request.Name,
Description = request.Description,
SignatureAlgorithm = request.SignatureAlgorithm,
AllowClientCredentialsFlow = request.AllowClientCredentialsFlow ?? false,
CreatedAt = clock.UtcNow(),
};
if (client.AllowClientCredentialsFlow)
{
client.Secrets.Add(CreateSecret());
}
await db.AddAsync(client, ct);
await db.SaveChangesAsync(ct);
return client;
}
public async Task<Client?> GetByClientId(
Guid realmId,
string clientId,
CancellationToken ct = default)
{
return await db.Clients.FirstOrDefaultAsync(c => c.ClientId == clientId && c.RealmId == realmId, ct);
}
public async Task<Client?> FindById(
Guid realmId,
int id,
CancellationToken ct = default)
{
return await db.Clients.FirstOrDefaultAsync(c => c.Id == id && c.RealmId == realmId, ct);
}
private ClientSecret CreateSecret()
{
byte[] secret = RandomNumberGenerator.GetBytes(24);
return new ClientSecret()
{
CreatedAt = clock.UtcNow(),
Secret = cryptor.Encrypt(secret),
};
}
}