summaryrefslogtreecommitdiff
path: root/src/api/auth/invite.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/api/auth/invite.rs')
-rw-r--r--src/api/auth/invite.rs47
1 files changed, 47 insertions, 0 deletions
diff --git a/src/api/auth/invite.rs b/src/api/auth/invite.rs
new file mode 100644
index 0000000..dd81540
--- /dev/null
+++ b/src/api/auth/invite.rs
@@ -0,0 +1,47 @@
+use base64::URL_SAFE_NO_PAD;
+use chrono::{Duration, Utc};
+use rocket::{http::uri::Reference, serde::json::Json, State};
+use serde::{Deserialize, Serialize};
+
+use crate::{api::auth, auth::Authenticated, crypto::SecretBytes, db::DbConn, Config};
+
+use super::WithVerifiedFxaLogin;
+
+pub(crate) async fn generate_invite_link(
+ db: &DbConn,
+ cfg: &Config,
+ ttl: Duration,
+) -> anyhow::Result<Reference<'static>> {
+ let code = base64::encode_config(&SecretBytes::<32>::generate().0, URL_SAFE_NO_PAD);
+ db.add_invite_code(&code, Utc::now() + ttl).await?;
+ Ok(Reference::parse_owned(format!("{}/#/register/{}", cfg.location, code))
+ .map_err(|e| anyhow!("url building failed at {e}"))?)
+}
+
+#[derive(Debug, Deserialize)]
+#[serde(deny_unknown_fields)]
+pub(crate) struct GenerateReq {
+ ttl_hours: u32,
+}
+
+#[derive(Debug, Serialize)]
+pub(crate) struct GenerateResp {
+ url: Reference<'static>,
+}
+
+#[post("/generate", data = "<req>")]
+pub(crate) async fn generate(
+ db: &DbConn,
+ cfg: &State<Config>,
+ req: Authenticated<GenerateReq, WithVerifiedFxaLogin>,
+) -> auth::Result<GenerateResp> {
+ if !req.context.verified {
+ return Err(auth::Error::UnverifiedSession);
+ }
+ let user = db.get_user_by_id(&req.context.uid).await?;
+ if user.email != cfg.invite_admin_address {
+ return Err(auth::Error::InvalidAuthToken);
+ }
+ let url = generate_invite_link(&db, &cfg, Duration::hours(req.body.ttl_hours as i64)).await?;
+ Ok(Json(GenerateResp { url }))
+}