Skip to main content

alibi_plugins/sso/
domain_verification.rs

1//! Durable ownership proofs, reused until the reference's seven-day deadline.
2use super::{
3    AuthContext, AuthResult, AuthSchema, EndpointResponse, JsValue, ServerEndpoint, SessionRequest,
4    SsoPlugin, Value, json,
5};
6impl SsoPlugin {
7    pub fn request_domain_verification_endpoint(
8        provider_id: &str,
9    ) -> AuthResult<ServerEndpoint<Value>> {
10        ServerEndpoint::new("sso", "requestDomainVerification")
11            .with_body(&json!({"providerId":provider_id}))
12    }
13    pub(super) fn domain_identifier(&self, provider_id: &str) -> String {
14        let prefix = if self.config.domain_verification_token_prefix.is_empty() {
15            "better-auth-token"
16        } else {
17            &self.config.domain_verification_token_prefix
18        };
19        format!("_{prefix}-{provider_id}")
20    }
21    pub(super) async fn issue_domain_proof<S: AuthSchema>(
22        &self,
23        provider_id: &str,
24        ctx: &AuthContext<S>,
25    ) -> AuthResult<String> {
26        use base64::Engine as _;
27        let bytes = rand::random::<[u8; 18]>();
28        let token = base64::engine::general_purpose::URL_SAFE_NO_PAD.encode(bytes);
29        let now = chrono::Utc::now();
30        _ = ctx
31            .verifications()
32            .create(alibi_core::CreateVerification {
33                identifier: self.domain_identifier(provider_id),
34                value: token.clone(),
35                expires_at: now + chrono::Duration::days(7),
36            })
37            .await?;
38        Ok(token)
39    }
40    pub(super) async fn request_domain<S: AuthSchema>(
41        &self,
42        input: &JsValue,
43        request: &impl SessionRequest,
44        ctx: &AuthContext<S>,
45    ) -> AuthResult<EndpointResponse> {
46        let provider = self.authorized_provider(input, request, ctx).await?;
47        if provider.domain_verified == Some(true) {
48            return Err(alibi_core::AuthError::Api {
49                status: 409,
50                code: Some("DOMAIN_VERIFIED".into()),
51                message: "Domain has already been verified".into(),
52            });
53        }
54        let proof = ctx
55            .verifications()
56            .find(&self.domain_identifier(&provider.provider_id))
57            .await?;
58        let token = if let Some(proof) = proof.filter(|proof| {
59            proof
60                .expires_at()
61                .is_ok_and(|expiry| expiry > chrono::Utc::now())
62        }) {
63            proof.value()?.to_owned()
64        } else {
65            self.issue_domain_proof(&provider.provider_id, ctx).await?
66        };
67        Ok(EndpointResponse::json(&json!({"domainVerificationToken":token}))?.with_status(201))
68    }
69}