summaryrefslogtreecommitdiff
path: root/src/api/auth/email.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/api/auth/email.rs')
-rw-r--r--src/api/auth/email.rs126
1 files changed, 126 insertions, 0 deletions
diff --git a/src/api/auth/email.rs b/src/api/auth/email.rs
new file mode 100644
index 0000000..f206759
--- /dev/null
+++ b/src/api/auth/email.rs
@@ -0,0 +1,126 @@
+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<StatusResp> {
+ 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 = "<req>")]
+pub(crate) async fn verify_code(
+ db: &DbConn,
+ db_pool: &Db,
+ defer: &DeferAction,
+ pc: &State<Arc<PushClient>>,
+ req: Json<VerifyReq>,
+) -> auth::Result<Empty> {
+ 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 = "<req>")]
+pub(crate) async fn resend_code(
+ db: &DbConn,
+ mailer: &State<Arc<Mailer>>,
+ req: Authenticated<ResendReq, WithFxaLogin>,
+) -> auth::Result<Empty> {
+ 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)
+}