use serde::{Deserialize, Serialize};
use crate::{
fnd::authn::{Authn, CaptchaPair, Factor, JwtTokenPair, TotpData, UserHandle, UserIdentifier},
prelude::*,
};
#[allow(non_camel_case_types)]
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "sign_type", content = "sign_data")]
#[allow(clippy::exhaustive_enums)]
pub enum SigninArgs {
username {
username: String,
password: String,
totp_token: Option<String>,
captcha: Option<CaptchaPair>,
},
email {
email: String,
#[serde(flatten)]
factor: Factor,
},
phone {
phone: String,
#[serde(flatten)]
factor: Factor,
},
}
impl<U> Authn<U>
where
U: UserHandle,
{
pub async fn check_sign_in(&self, depot: &mut Depot, args: SigninArgs) -> Result<JwtTokenPair, ApiError> {
let user_id = match args {
SigninArgs::username {
username,
password,
totp_token,
captcha,
} => {
if let Some(captcha) = captcha {
self.verify_captcha(&captcha).await?;
}
let user_ident = UserIdentifier::username(username);
let user_totp_info = self.user_handle.get_totp_info(depot, &user_ident).await?;
if let Some(totp_token) = totp_token {
let matched = self.verify_totp_token(user_totp_info.totp_secret, TotpData::otp(&totp_token))?;
if !matched {
return Err(api_err!(
ET_USER_VERIF_CODE,
"OTP token is not matched.",
&ERR_PATH_AUTHN
));
}
} else if user_totp_info.enable_2fa {
return Err(api_err!(
ET_USER_VERIF_CODE,
"Require two-factor verification.",
&ERR_PATH_AUTHN
));
}
self.verify_user_password(depot, &user_ident, password).await?
}
SigninArgs::email { email, factor } => match factor {
Factor::password { password, captcha } => {
if let Some(captcha) = captcha {
self.verify_captcha(&captcha).await?;
}
self.verify_user_password(depot, &UserIdentifier::email(email), password)
.await?
}
Factor::totp_token { totp_token, captcha } => {
if let Some(captcha) = captcha {
self.verify_captcha(&captcha).await?;
}
self.verify_user_totp_token(depot, &UserIdentifier::email(email), TotpData::email(&totp_token))
.await?
}
},
SigninArgs::phone { phone, factor } => match factor {
Factor::password { password, captcha } => {
if let Some(captcha) = captcha {
self.verify_captcha(&captcha).await?;
}
self.verify_user_password(depot, &UserIdentifier::phone(phone), password)
.await?
}
Factor::totp_token { totp_token, captcha } => {
if let Some(captcha) = captcha {
self.verify_captcha(&captcha).await?;
}
self.verify_user_totp_token(depot, &UserIdentifier::phone(phone), TotpData::sms(&totp_token))
.await?
}
},
};
self.new_jwt_token_pair(user_id, None).await
}
}