use async_trait::async_trait;
use serde::{Deserialize, Serialize};
use super::error::AuthError;
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct ApiKeyRow {
pub id: String,
pub tenant_id: String,
pub key_hash: String,
pub name: String,
pub scopes: Vec<String>,
}
#[async_trait]
pub trait TenantApiKeyStore: Send + Sync + 'static {
async fn get_by_hash(&self, hash: &str) -> Result<ApiKeyRow, AuthError>;
}
pub fn hash_api_key(key: &str) -> String {
use sha2::{Digest, Sha256};
let mut hasher = Sha256::new();
hasher.update(key.as_bytes());
hex::encode(hasher.finalize())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn hash_api_key_is_deterministic_and_hex() {
let h1 = hash_api_key("my-secret-key");
let h2 = hash_api_key("my-secret-key");
assert_eq!(h1, h2);
assert_eq!(h1.len(), 64);
assert!(h1.chars().all(|c| c.is_ascii_hexdigit()));
}
#[test]
fn hash_api_key_differs_for_different_keys() {
let h1 = hash_api_key("key-one");
let h2 = hash_api_key("key-two");
assert_ne!(h1, h2);
}
}