Skip to main content

uvb_storage_api/
secret.rs

1use async_trait::async_trait;
2use serde::{Deserialize, Serialize};
3use std::time::SystemTime;
4use thiserror::Error;
5use uvb_core::TenantId;
6
7#[derive(Debug, Error)]
8pub enum SecretError {
9    #[error("secret not found")]
10    NotFound,
11    #[error("storage error: {0}")]
12    Storage(String),
13    #[error("encryption error: {0}")]
14    Encryption(String),
15}
16
17/// Represents a factor secret (TOTP key, WebAuthn credential, etc.)
18#[derive(Clone, Debug, Serialize, Deserialize)]
19pub struct SecretRecord {
20    pub id: String,
21    pub user_id: String,
22    pub tenant_id: TenantId,
23    pub factor_id: String,
24    pub secret_data: Vec<u8>, // Encrypted at rest
25    pub metadata: serde_json::Value,
26    pub created_at: SystemTime,
27    pub updated_at: SystemTime,
28}
29
30/// Trait for pluggable secret storage
31///
32/// Critical: Implementations MUST encrypt secrets at rest
33/// Consider using:
34/// - HashiCorp Vault
35/// - AWS Secrets Manager
36/// - Azure Key Vault
37/// - GCP Secret Manager
38/// - Database with application-level encryption
39#[async_trait]
40pub trait SecretStore: Send + Sync {
41    /// Store a secret (implementation handles encryption)
42    async fn set(
43        &self,
44        user_id: &str,
45        tenant_id: &TenantId,
46        factor_id: &str,
47        secret_data: &[u8],
48        metadata: serde_json::Value,
49    ) -> Result<String, SecretError>;
50
51    /// Retrieve a secret (implementation handles decryption)
52    async fn get(
53        &self,
54        user_id: &str,
55        tenant_id: &TenantId,
56        factor_id: &str,
57    ) -> Result<Option<SecretRecord>, SecretError>;
58
59    /// Get a specific secret by ID
60    async fn get_by_id(&self, id: &str) -> Result<Option<SecretRecord>, SecretError>;
61
62    /// Delete a secret
63    async fn delete(&self, id: &str) -> Result<(), SecretError>;
64
65    /// List all secrets for a user/tenant/factor combination
66    async fn list(
67        &self,
68        user_id: &str,
69        tenant_id: &TenantId,
70        factor_id: Option<&str>,
71    ) -> Result<Vec<SecretRecord>, SecretError>;
72
73    /// Rotate encryption keys (for implementations that support it)
74    async fn rotate_encryption(&self) -> Result<usize, SecretError> {
75        // Default: not supported
76        Ok(0)
77    }
78}