better_auth_api/plugins/two_factor/
native.rs1use super::{TwoFactorConfig, TwoFactorPlugin, request};
2use better_auth_core::{
3 AuthContext, AuthError, AuthResponse, AuthResult, AuthRoute, AuthSchema, HttpMethod,
4 NativeRequest,
5};
6use serde_json::Value;
7
8pub struct TwoFactorApi<'a, S: AuthSchema> {
11 plugin: TwoFactorPlugin,
12 context: &'a AuthContext<S>,
13 source: NativeRequest<'a>,
14}
15impl<'a, S: AuthSchema> TwoFactorApi<'a, S> {
16 pub fn from_context(context: &'a AuthContext<S>) -> AuthResult<Self> {
18 let config = context
19 .extensions
20 .get::<TwoFactorConfig>()
21 .ok_or_else(|| AuthError::config("TwoFactorPlugin is not registered"))?;
22 Ok(Self {
23 plugin: TwoFactorPlugin::with_config(config.clone()),
24 context,
25 source: NativeRequest::default(),
26 })
27 }
28 pub fn with_request(mut self, source: NativeRequest<'a>) -> Self {
30 self.source = source;
31 self
32 }
33 pub async fn generate_totp(&self, body: Option<Value>) -> AuthResult<String> {
35 let route = AuthRoute::server_only(HttpMethod::Post, "generateTOTP")
36 .body_validator(|request| request::validate_native(request, "generateTOTP"));
37 let response = self
38 .context
39 .dispatch_native(self.source, route, body, None, |request, _| async move {
40 let body = request
41 .validated_body::<request::GenerateTotpRequest>()
42 .ok_or_else(|| AuthError::internal("Missing validated TOTP input"))?;
43 AuthResponse::json(
44 200,
45 &serde_json::json!({"code":self.plugin.generate_totp(&body.secret)?}),
46 )
47 .map_err(Into::into)
48 })
49 .await?;
50 #[derive(serde::Deserialize)]
51 struct Result {
52 code: String,
53 }
54 Ok(serde_json::from_slice::<Result>(&response.body.bytes()?)?.code)
55 }
56 pub async fn view_backup_codes(&self, body: Option<Value>) -> AuthResult<Vec<String>> {
58 let route = AuthRoute::server_only(HttpMethod::Post, "viewBackupCodes")
59 .body_validator(|request| request::validate_native(request, "viewBackupCodes"));
60 let response = self
61 .context
62 .dispatch_native(
63 self.source,
64 route,
65 body,
66 None,
67 |request, context| async move {
68 let body = request
69 .validated_body::<request::ViewBackupCodesRequest>()
70 .ok_or_else(|| {
71 AuthError::internal("Missing validated backup code input")
72 })?;
73 let codes = self
74 .plugin
75 .view_backup_codes(&body.user_id, &context)
76 .await?;
77 AuthResponse::json(200, &serde_json::json!({"status":true,"backupCodes":codes}))
78 .map_err(Into::into)
79 },
80 )
81 .await?;
82 #[derive(serde::Deserialize)]
83 struct Result {
84 #[serde(rename = "backupCodes")]
85 backup_codes: Vec<String>,
86 }
87 Ok(serde_json::from_slice::<Result>(&response.body.bytes()?)?.backup_codes)
88 }
89}