alibi_plugins/oauth_provider/
client_secret.rs1use alibi_core::{AuthContext, AuthResult, AuthSchema};
3use async_trait::async_trait;
4use base64::{Engine as _, engine::general_purpose::URL_SAFE_NO_PAD};
5use sha2::{Digest as _, Sha256};
6use subtle::ConstantTimeEq;
7
8#[async_trait]
10pub trait OAuthClientSecretHasher: Send + Sync + std::fmt::Debug {
11 async fn hash(&self, secret: &str) -> AuthResult<String>;
12 async fn verify(&self, secret: &str, stored: &str) -> AuthResult<bool> {
15 let hash = self.hash(secret).await?;
16 Ok(bool::from(hash.as_bytes().ct_eq(stored.as_bytes())))
17 }
18}
19
20#[async_trait]
22pub trait OAuthClientSecretCipher: Send + Sync + std::fmt::Debug {
23 async fn encrypt(&self, secret: &str) -> AuthResult<String>;
24 async fn decrypt(&self, stored: &str) -> AuthResult<String>;
25}
26
27pub(super) async fn store<S: AuthSchema>(
28 ctx: &AuthContext<S>,
29 config: &super::OAuthProviderConfig,
30 secret: &str,
31) -> AuthResult<String> {
32 if let Some(hasher) = &config.client_secret_hasher {
33 return hasher.hash(secret).await;
34 }
35 if let Some(cipher) = &config.client_secret_cipher {
36 return cipher.encrypt(secret).await;
37 }
38 if config.disable_jwt_plugin {
39 crate::token_crypto::encrypt_with_config(secret, &ctx.config)
40 } else {
41 Ok(URL_SAFE_NO_PAD.encode(Sha256::digest(secret)))
42 }
43}
44
45pub(super) async fn verify<S: AuthSchema>(
46 ctx: &AuthContext<S>,
47 config: &super::OAuthProviderConfig,
48 secret: &str,
49 stored: &str,
50) -> AuthResult<bool> {
51 if secret.is_empty() {
52 return Ok(false);
53 }
54 if let Some(hasher) = &config.client_secret_hasher {
55 return hasher.verify(secret, stored).await;
56 }
57 if let Some(cipher) = &config.client_secret_cipher {
58 let plaintext = cipher.decrypt(stored).await?;
59 return Ok(bool::from(plaintext.as_bytes().ct_eq(secret.as_bytes())));
60 }
61 if config.disable_jwt_plugin {
62 Ok(
63 crate::token_crypto::decrypt_with_config(stored, &ctx.config)
64 .is_ok_and(|plaintext| bool::from(plaintext.as_bytes().ct_eq(secret.as_bytes()))),
65 )
66 } else {
67 let hash = URL_SAFE_NO_PAD.encode(Sha256::digest(secret));
68 Ok(bool::from(hash.as_bytes().ct_eq(stored.as_bytes())))
69 }
70}
71
72pub(super) async fn decrypt<S: AuthSchema>(
73 ctx: &AuthContext<S>,
74 config: &super::OAuthProviderConfig,
75 stored: &str,
76) -> AuthResult<String> {
77 if let Some(cipher) = &config.client_secret_cipher {
78 return cipher.decrypt(stored).await;
79 }
80 crate::token_crypto::decrypt_with_config(stored, &ctx.config)
81}