summaryrefslogtreecommitdiff
path: root/src/api/auth/session.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/api/auth/session.rs')
-rw-r--r--src/api/auth/session.rs107
1 files changed, 107 insertions, 0 deletions
diff --git a/src/api/auth/session.rs b/src/api/auth/session.rs
new file mode 100644
index 0000000..5911b92
--- /dev/null
+++ b/src/api/auth/session.rs
@@ -0,0 +1,107 @@
+use std::sync::Arc;
+
+use rocket::serde::json::Json;
+use rocket::State;
+use serde::{Deserialize, Serialize};
+
+use crate::api::auth::WithFxaLogin;
+use crate::api::{auth, Empty, EMPTY};
+use crate::auth::Authenticated;
+use crate::db::Db;
+use crate::db::DbConn;
+use crate::mailer::Mailer;
+use crate::push::PushClient;
+use crate::types::{SessionID, UserID};
+use crate::utils::DeferAction;
+
+// MISSING post /session/duplicate
+// MISSING post /session/reauth
+// MISSING post /session/verify/send_push
+
+#[derive(Debug, Serialize)]
+pub(crate) struct StatusResp {
+ state: &'static str, // what does this *do*?
+ uid: UserID,
+}
+
+#[get("/session/status")]
+pub(crate) async fn status(req: Authenticated<(), WithFxaLogin>) -> auth::Result<StatusResp> {
+ Ok(Json(StatusResp { state: "", uid: req.context.uid }))
+}
+
+#[post("/session/resend_code", data = "<req>")]
+pub(crate) async fn resend_code(
+ db: &DbConn,
+ mailer: &State<Arc<Mailer>>,
+ req: Authenticated<Empty, WithFxaLogin>,
+) -> auth::Result<Empty> {
+ let code = match req.context.verify_code {
+ Some(code) => code,
+ _ => return Err(auth::Error::InvalidVerificationCode),
+ };
+
+ let user = db.get_user_by_id(&req.context.uid).await?;
+ mailer.send_session_verify(&user.email, &code).await.map_err(|e| {
+ error!("failed to send email: {e}");
+ auth::Error::EmailFailed
+ })?;
+ Ok(EMPTY)
+}
+
+#[derive(Debug, Deserialize)]
+#[serde(deny_unknown_fields)]
+pub(crate) struct VerifyReq {
+ code: String,
+ // MISSING service
+ // MISSING scopes
+ // MISSING marketingOptIn
+ // MISSING newsletters
+}
+
+#[post("/session/verify_code", data = "<req>")]
+pub(crate) async fn verify_code(
+ db: &DbConn,
+ req: Authenticated<VerifyReq, WithFxaLogin>,
+) -> auth::Result<Empty> {
+ if req.context.verify_code.as_ref() != Some(&req.body.code) {
+ return Err(auth::Error::InvalidVerificationCode);
+ }
+ db.set_session_verified(&req.session).await?;
+ Ok(EMPTY)
+}
+
+#[derive(Debug, Deserialize)]
+#[serde(deny_unknown_fields)]
+pub(crate) struct DestroyReq {
+ custom_session_id: Option<SessionID>,
+}
+
+#[post("/session/destroy", data = "<data>")]
+pub(crate) async fn destroy(
+ db: &DbConn,
+ db_pool: &Db,
+ defer: &DeferAction,
+ client: &State<Arc<PushClient>>,
+ data: Authenticated<DestroyReq, WithFxaLogin>,
+) -> auth::Result<Empty> {
+ if data.body.custom_session_id.is_some() && !data.context.verified {
+ return Err(auth::Error::UnverifiedSession);
+ }
+ let id = data.body.custom_session_id.as_ref().unwrap_or(&data.session);
+ db.delete_session(&data.context.uid, id).await.map_err(|_| auth::Error::UnknownDevice)?;
+ if let Some(id) = data.context.device_id {
+ match db.get_devices(&data.context.uid).await {
+ Err(e) => warn!("device_disconnected push failed: {e}"),
+ Ok(devs) => defer.spawn_after_success("api::auth/session/destroy(post)", {
+ let (client, db) = (Arc::clone(client), db_pool.clone());
+ async move {
+ let db = db.begin().await?;
+ client.device_disconnected(&db, &devs, &id).await;
+ db.commit().await?;
+ Ok(())
+ }
+ }),
+ };
+ }
+ Ok(EMPTY)
+}