using ScadaLink.Commons.Types.InboundApi; namespace ScadaLink.Commons.Entities.InboundApi; /// /// An inbound-API bearer credential. Per ConfigurationDatabase-012 the plaintext key /// is never persisted: the entity stores only , a deterministic /// keyed hash of the key (HMAC-SHA256 with a server-side pepper). The plaintext is /// generated at creation, shown to the operator exactly once, and then discarded. /// public class ApiKey { /// Database primary key. public int Id { get; set; } /// Display name for the API key. public string Name { get; set; } /// /// Deterministic keyed hash of the API key value. This is the only form of the /// credential persisted; the plaintext key is never stored. Authentication hashes /// the presented candidate with the same scheme and compares against this value. /// public string KeyHash { get; set; } /// When false, the key is rejected even if the hash matches. public bool IsEnabled { get; set; } /// /// Creates an API key from a plaintext value, immediately hashing it with the /// unpeppered default hasher () so the entity /// never holds the plaintext. Production code paths that have a configured pepper /// should use with a peppered hash instead. /// /// Display name for the API key. /// Plaintext key value; hashed immediately and never stored. public ApiKey(string name, string keyValue) { Name = name ?? throw new ArgumentNullException(nameof(name)); if (keyValue is null) throw new ArgumentNullException(nameof(keyValue)); KeyHash = ApiKeyHasher.Default.Hash(keyValue); } /// /// Parameterless constructor for the EF Core materializer. Application code uses /// or . /// private ApiKey() { Name = string.Empty; KeyHash = string.Empty; } /// /// Creates an API key from an already-computed key hash. Used by the creation /// path, which generates a random key, hashes it with the configured (peppered) /// , and stores only the resulting hash. /// /// Display name for the API key. /// Pre-computed keyed hash of the API key value. public static ApiKey FromHash(string name, string keyHash) { return new ApiKey { Name = name ?? throw new ArgumentNullException(nameof(name)), KeyHash = keyHash ?? throw new ArgumentNullException(nameof(keyHash)), }; } }