diff options
Diffstat (limited to 'src/api/auth/session.rs')
-rw-r--r-- | src/api/auth/session.rs | 107 |
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) +} |