Skip to main content

alibi_plugins/oauth_provider/
token_storage.rs

1//! Application-owned token hashing shared by issuance, lookup and trusted server callers.
2use alibi_core::AuthResult;
3use alibi_core::store::OAuthTokenKind;
4use async_trait::async_trait;
5use base64::{Engine as _, engine::general_purpose::URL_SAFE_NO_PAD};
6use sha2::{Digest as _, Sha256};
7
8/// Custom opaque-token storage matching Better Auth's `storeTokens.hash` callback.
9/// `kind` identifies the token domain; trusted callers may also supply application kinds.
10#[async_trait]
11pub trait OAuthTokenHasher: Send + Sync + std::fmt::Debug {
12    async fn hash(&self, token: &str, kind: &str) -> AuthResult<String>;
13}
14impl super::OAuthProviderPlugin {
15    fn token_prefix(&self, kind: OAuthTokenKind) -> &str {
16        match kind {
17            OAuthTokenKind::Access => self.config.opaque_access_token_prefix.as_deref(),
18            OAuthTokenKind::Refresh => self.config.refresh_token_prefix.as_deref(),
19        }
20        .unwrap_or_default()
21    }
22
23    pub(super) fn encode_token_prefix(&self, token: &str, kind: OAuthTokenKind) -> String {
24        format!("{}{token}", self.token_prefix(kind))
25    }
26
27    /// Match the reference's nonempty-prefix `startsWith` guard followed by one
28    /// literal `replace`. Missing/empty configuration still accepts raw tokens.
29    pub(super) fn decode_token_prefix<'a>(
30        &self,
31        token: &'a str,
32        kind: OAuthTokenKind,
33    ) -> Option<&'a str> {
34        let prefix = self.token_prefix(kind);
35        if prefix.is_empty() {
36            Some(token)
37        } else {
38            token.strip_prefix(prefix)
39        }
40    }
41
42    /// Hash a token using this provider's storage policy (`getOAuthProviderApi().hashToken`).
43    pub async fn hash_token(&self, token: &str, kind: &str) -> AuthResult<String> {
44        if let Some(hasher) = &self.config.token_hasher {
45            hasher.hash(token, kind).await
46        } else {
47            Ok(URL_SAFE_NO_PAD.encode(Sha256::digest(token)))
48        }
49    }
50}