scufflecloud_core/captcha/
turnstile.rs1use std::sync::Arc;
2
3use tonic_types::{ErrorDetails, StatusExt};
4
5use crate::CoreConfig;
6use crate::std_ext::DisplayExt;
7
8const TURNSTILE_VERIFY_URL: &str = "https://challenges.cloudflare.com/turnstile/v0/siteverify";
9
10#[derive(Debug, serde_derive::Serialize)]
11struct TurnstileSiteVerifyPayload {
12 pub secret: String,
13 pub response: String,
14 pub remoteip: Option<String>,
15}
16
17#[derive(Debug, serde_derive::Deserialize)]
18struct TurnstileSiteVerifyResponse {
19 pub success: bool,
20 #[serde(rename = "error-codes")]
23 pub error_codes: Vec<String>,
24}
25
26#[derive(Debug, thiserror::Error)]
27pub(crate) enum TrunstileVerifyError {
28 #[error("request to verify server failed: {0}")]
29 HttpRequest(#[from] reqwest::Error),
30 #[error("turnstile error code: {0}")]
31 TurnstileError(String),
32 #[error("missing error code in turnstile response")]
33 MissingErrorCode,
34}
35
36pub(crate) async fn verify<G: CoreConfig>(global: &Arc<G>, token: &str) -> Result<(), TrunstileVerifyError> {
37 let payload = TurnstileSiteVerifyPayload {
38 secret: global.turnstile_secret_key().to_string(),
39 response: token.to_string(),
40 remoteip: None, };
42
43 let res: TurnstileSiteVerifyResponse = global
44 .http_client()
45 .post(TURNSTILE_VERIFY_URL)
46 .json(&payload)
47 .send()
48 .await?
49 .json()
50 .await?;
51
52 if !res.success {
53 let Some(error_code) = res.error_codes.into_iter().next() else {
54 return Err(TrunstileVerifyError::MissingErrorCode);
55 };
56 return Err(TrunstileVerifyError::TurnstileError(error_code));
57 }
58
59 Ok(())
60}
61
62pub(crate) async fn verify_in_tonic<G: CoreConfig>(global: &Arc<G>, token: &str) -> Result<(), tonic::Status> {
63 match verify(global, token).await {
64 Ok(_) => Ok(()),
65 Err(TrunstileVerifyError::TurnstileError(e)) => Err(tonic::Status::with_error_details(
66 tonic::Code::Unauthenticated,
67 TrunstileVerifyError::TurnstileError(e).to_string(),
68 ErrorDetails::new(),
69 )),
70 Err(e) => Err(e.into_tonic_internal_err("failed to verify turnstile token")),
71 }
72}