use std::sync::Arc; use rocket::{serde::json::Json, State}; use serde::{Deserialize, Serialize}; use crate::{ api::{ auth::{self, WithFxaLogin}, Empty, EMPTY, }, auth::Authenticated, db::{Db, DbConn}, mailer::Mailer, push::PushClient, types::UserID, utils::DeferAction, }; // MISSING get /recovery_emails // MISSING post /recovery_email // MISSING post /recovery_email/destroy // MISSING post /recovery_email/resend_code // MISSING post /recovery_email/set_primary // MISSING post /emails/reminders/cad // MISSING post /recovery_email/secondary/resend_code // MISSING post /recovery_email/secondary/verify_code #[derive(Debug, Serialize)] #[allow(non_snake_case)] pub(crate) struct StatusResp { email: String, verified: bool, sessionVerified: bool, emailVerified: bool, } // MISSING arg: reason #[get("/recovery_email/status")] pub(crate) async fn status( db: &DbConn, req: Authenticated<(), WithFxaLogin>, ) -> auth::Result { let user = db.get_user_by_id(&req.context.uid).await?; Ok(Json(StatusResp { email: user.email, verified: user.verified, sessionVerified: req.context.verified, emailVerified: user.verified, })) } #[derive(Debug, Deserialize)] #[serde(deny_unknown_fields)] pub(crate) struct VerifyReq { uid: UserID, code: String, // MISSING service // MISSING reminder // MISSING type // MISSING style // MISSING marketingOptIn // MISSING newsletters } #[post("/recovery_email/verify_code", data = "")] pub(crate) async fn verify_code( db: &DbConn, db_pool: &Db, defer: &DeferAction, pc: &State>, req: Json, ) -> auth::Result { let code = match db.try_use_verify_code(&req.uid, &req.code).await? { Some(code) => code, None => return Err(auth::Error::InvalidVerificationCode), }; db.set_user_verified(&req.uid).await?; if let Some(sid) = code.session_id { db.set_session_verified(&sid).await?; } match db.get_devices(&req.uid).await { Ok(devs) => defer.spawn_after_success("api::auth/recovery_email/verify_code(post)", { let (pc, db) = (Arc::clone(pc), db_pool.clone()); async move { let db = db.begin().await?; pc.account_verified(&db, &devs).await; db.commit().await?; Ok(()) } }), Err(e) => warn!("account_verified push failed: {e}"), } Ok(EMPTY) } #[derive(Debug, Deserialize)] #[serde(deny_unknown_fields)] pub(crate) struct ResendReq { // MISSING email // MISSING service // MISSING redirectTo // MISSING resume // MISSING style // MISSING type } // MISSING arg: service // MISSING arg: type #[post("/recovery_email/resend_code", data = "")] pub(crate) async fn resend_code( db: &DbConn, mailer: &State>, req: Authenticated, ) -> auth::Result { let (email, code) = match db.get_verify_code(&req.context.uid).await { Ok(v) => v, Err(_) => return Err(auth::Error::InvalidVerificationCode), }; // NOTE we send the email in this context rather than a spawn to signal // send errors to the client. mailer.send_account_verify(&req.context.uid, &email, &code.code).await.map_err(|e| { error!("failed to send email: {e}"); auth::Error::EmailFailed })?; Ok(EMPTY) }