doido_auth/controllers/
registrations.rs1use crate::handlers::{register_user, sign_in};
4use crate::user::{AuthUser, RegisterableAuthUser};
5use doido_auth_macros::auth_controller;
6use doido_core::Result;
7use doido_model::password::HasSecurePassword;
8use serde::Deserialize;
9use serde::Serialize;
10use std::marker::PhantomData;
11
12pub struct AuthRegistrations<U>(PhantomData<U>);
14
15#[derive(Debug, Deserialize)]
16pub struct SignUpForm {
17 pub email: String,
18 pub password: String,
19 #[serde(default)]
20 pub password_confirmation: Option<String>,
21}
22
23#[auth_controller]
24impl<U> AuthRegistrations<U>
25where
26 U: AuthUser + HasSecurePassword + RegisterableAuthUser + Serialize + Send + Sync + 'static,
27{
28 pub async fn new(ctx: doido_controller::Context) -> doido_controller::Response {
30 ctx.render("auth/sign_up", serde_json::json!({}))
31 }
32
33 pub async fn create(mut ctx: doido_controller::Context) -> Result<doido_controller::Response> {
35 let json = ctx.wants_json();
36 let form: SignUpForm = if json {
37 ctx.body_json().await?
38 } else {
39 ctx.form().await?
40 };
41
42 if let Some(ref confirm) = form.password_confirmation {
43 if form.password != *confirm {
44 return registration_error(ctx, json, "Password confirmation does not match");
45 }
46 }
47
48 let db = ctx.db().clone();
49 let user =
50 match register_user::<U, _, _>(&db, &form.email, &form.password, |email, digest| {
51 let db = db.clone();
52 async move { U::register(&db, email, digest).await }
53 })
54 .await
55 {
56 Ok(user) => user,
57 Err(crate::error::AuthError::EmailTaken) => {
58 return registration_error(ctx, json, "Email has already been taken");
59 }
60 Err(crate::error::AuthError::Validation(msg)) => {
62 return registration_error(ctx, json, &msg);
63 }
64 Err(e) => return Err(doido_core::anyhow::anyhow!(e.to_string())),
65 };
66
67 if crate::confirmable::is_enabled() {
70 if let Some(token) =
71 crate::confirmable::generate_confirmation(ctx.db(), &form.email).await?
72 {
73 let _ = crate::confirmable::send_confirmation_email(&form.email, &token).await;
74 }
75 return if json {
76 Ok(ctx.json(serde_json::json!({ "status": "confirmation_sent" })))
77 } else {
78 Ok(ctx.render(
79 "auth/sign_in",
80 serde_json::json!({ "notice": "Please confirm your email to finish signing up." }),
81 ))
82 };
83 }
84
85 sign_in(ctx, &user)?;
86 if json {
87 Ok(ctx.json(user))
88 } else {
89 Ok(ctx.redirect_to("/"))
90 }
91 }
92}
93
94fn registration_error(
95 ctx: &doido_controller::Context,
96 json: bool,
97 message: &str,
98) -> Result<doido_controller::Response> {
99 if json {
100 Ok(ctx.status(422))
101 } else {
102 Ok(ctx.render("auth/sign_up", serde_json::json!({ "error": message })))
103 }
104}