Skip to main content

alibi_core/store/contracts/
verification.rs

1use crate::verification::{VerificationCreation, VerificationPublication, VerificationSnapshot};
2use crate::{AuthError, AuthResult, AuthSchema, CreateVerification};
3use async_trait::async_trait;
4#[async_trait]
5pub trait VerificationStore<S: AuthSchema>: Send + Sync {
6    /// Create through before hooks, optional physical persistence, secondary
7    /// publication, then after hooks. Unsupported adapters must fail closed.
8    async fn create_verification_record(
9        &self,
10        _data: VerificationCreation,
11        _publication: VerificationPublication,
12    ) -> AuthResult<Option<VerificationSnapshot>> {
13        Err(AuthError::NotImplemented(
14            "Verification publication phases are not supported by this store".into(),
15        ))
16    }
17    /// Atomically remove the newest generation and all siblings, returning the
18    /// actual winning snapshot even when expired. This keeps legacy fallback
19    /// from resurrecting an older logical generation.
20    async fn consume_verification_snapshot(
21        &self,
22        _identifier: &str,
23    ) -> AuthResult<Option<S::Verification>> {
24        Err(AuthError::NotImplemented(
25            "Raw atomic verification consumption is not supported by this store".into(),
26        ))
27    }
28    /// Update every physical match, with one actual adapter result snapshot.
29    async fn update_verification_by_identifier(
30        &self,
31        _identifier: &str,
32        _data: crate::UpdateVerification,
33    ) -> AuthResult<Option<VerificationSnapshot>> {
34        Err(AuthError::NotImplemented(
35            "Verification identifier updates are not supported by this store".into(),
36        ))
37    }
38    /// Reserve by the original logical identifier while storing its configured
39    /// transformed identifier. Return the actual newly inserted model only.
40    async fn reserve_verification_record(
41        &self,
42        _logical_identifier: &str,
43        _data: CreateVerification,
44    ) -> AuthResult<Option<S::Verification>> {
45        Err(AuthError::NotImplemented(
46            "Configured verification reservation is not supported by this store".into(),
47        ))
48    }
49    async fn create_verification(
50        &self,
51        verification: CreateVerification,
52    ) -> AuthResult<S::Verification>;
53    async fn get_verification(
54        &self,
55        identifier: &str,
56        value: &str,
57    ) -> AuthResult<Option<S::Verification>>;
58    async fn get_verification_by_value(&self, value: &str) -> AuthResult<Option<S::Verification>>;
59    async fn get_verification_by_identifier(
60        &self,
61        identifier: &str,
62    ) -> AuthResult<Option<S::Verification>>;
63    async fn consume_verification(
64        &self,
65        identifier: &str,
66        value: &str,
67    ) -> AuthResult<Option<S::Verification>>;
68    /// Fetch the newest generation, including expired records. The caller
69    /// decides whether cleanup and a distinct expiry error are required.
70    async fn get_latest_verification_by_identifier(
71        &self,
72        _identifier: &str,
73    ) -> AuthResult<Option<S::Verification>> {
74        Err(AuthError::internal(
75            "raw verification lookup is not supported by this store",
76        ))
77    }
78    /// Atomically invalidate an identifier and return its newest generation.
79    /// Exactly one concurrent caller can receive a row. Expired records are
80    /// removed along with every sibling and return `None`.
81    async fn consume_verification_by_identifier(
82        &self,
83        _identifier: &str,
84    ) -> AuthResult<Option<S::Verification>> {
85        Err(AuthError::internal(
86            "atomic verification consumption is not supported by this store",
87        ))
88    }
89    async fn delete_verifications_by_identifier(&self, _identifier: &str) -> AuthResult<()> {
90        Err(AuthError::internal(
91            "verification invalidation is not supported by this store",
92        ))
93    }
94    /// Update a generation only when its value still matches the snapshot.
95    async fn compare_and_swap_verification(
96        &self,
97        _id: &str,
98        _expected_value: &str,
99        _value: &str,
100        _expires_at: chrono::DateTime<chrono::Utc>,
101    ) -> AuthResult<bool> {
102        Err(AuthError::internal(
103            "atomic verification updates are not supported by this store",
104        ))
105    }
106    /// Insert a deterministic reservation exactly once. An expired marker
107    /// remains reserved until it is cleaned up or explicitly consumed.
108    async fn reserve_verification(&self, _verification: CreateVerification) -> AuthResult<bool> {
109        Err(AuthError::internal(
110            "verification reservation is not supported by this store",
111        ))
112    }
113    async fn delete_verification(&self, id: &str) -> AuthResult<()>;
114    async fn delete_expired_verifications(&self) -> AuthResult<usize>;
115}
116
117/// Upstream's deterministic database key for first-writer verification claims.
118#[must_use]
119pub fn verification_reservation_key(identifier: &str) -> (String, [u8; 32]) {
120    use base64::Engine;
121    use sha2::{Digest, Sha256};
122
123    let mut hash = Sha256::new();
124    hash.update(b"reserve:");
125    hash.update(identifier.as_bytes());
126    let digest: [u8; 32] = hash.finalize().into();
127    (
128        base64::engine::general_purpose::URL_SAFE_NO_PAD.encode(digest),
129        digest,
130    )
131}