You've already forked authentication-service
mirror of
https://github.com/matrix-org/matrix-authentication-service.git
synced 2025-07-31 09:24:31 +03:00
Add an email field in the registration form
This commit is contained in:
@ -86,7 +86,7 @@ pub(crate) async fn post(
|
|||||||
return Ok((cookie_jar, login.go()).into_response());
|
return Ok((cookie_jar, login.go()).into_response());
|
||||||
};
|
};
|
||||||
|
|
||||||
let user_email = add_user_email(&mut txn, &session.user, form.email).await?;
|
let user_email = add_user_email(&mut txn, &session.user, &form.email).await?;
|
||||||
let next = mas_router::AccountVerifyEmail::new(user_email.data);
|
let next = mas_router::AccountVerifyEmail::new(user_email.data);
|
||||||
let next = if let Some(action) = query.post_auth_action {
|
let next = if let Some(action) = query.post_auth_action {
|
||||||
next.and_then(action)
|
next.and_then(action)
|
||||||
|
@ -141,7 +141,7 @@ pub(crate) async fn post(
|
|||||||
|
|
||||||
match form {
|
match form {
|
||||||
ManagementForm::Add { email } => {
|
ManagementForm::Add { email } => {
|
||||||
let user_email = add_user_email(&mut txn, &session.user, email).await?;
|
let user_email = add_user_email(&mut txn, &session.user, &email).await?;
|
||||||
let next = mas_router::AccountVerifyEmail::new(user_email.data);
|
let next = mas_router::AccountVerifyEmail::new(user_email.data);
|
||||||
start_email_verification(&mailer, &mut txn, &session.user, user_email).await?;
|
start_email_verification(&mailer, &mut txn, &session.user, user_email).await?;
|
||||||
txn.commit().await?;
|
txn.commit().await?;
|
||||||
|
@ -14,23 +14,30 @@
|
|||||||
|
|
||||||
#![allow(clippy::trait_duplication_in_bounds)]
|
#![allow(clippy::trait_duplication_in_bounds)]
|
||||||
|
|
||||||
|
use std::str::FromStr;
|
||||||
|
|
||||||
use argon2::Argon2;
|
use argon2::Argon2;
|
||||||
use axum::{
|
use axum::{
|
||||||
extract::{Extension, Form, Query},
|
extract::{Extension, Form, Query},
|
||||||
response::{Html, IntoResponse, Response},
|
response::{Html, IntoResponse, Response},
|
||||||
};
|
};
|
||||||
use axum_extra::extract::PrivateCookieJar;
|
use axum_extra::extract::PrivateCookieJar;
|
||||||
|
use lettre::{message::Mailbox, Address};
|
||||||
use mas_axum_utils::{
|
use mas_axum_utils::{
|
||||||
csrf::{CsrfExt, CsrfToken, ProtectedForm},
|
csrf::{CsrfExt, CsrfToken, ProtectedForm},
|
||||||
FancyError, SessionInfoExt,
|
FancyError, SessionInfoExt,
|
||||||
};
|
};
|
||||||
use mas_config::Encrypter;
|
use mas_config::Encrypter;
|
||||||
|
use mas_email::Mailer;
|
||||||
use mas_router::Route;
|
use mas_router::Route;
|
||||||
use mas_storage::user::{register_user, start_session, username_exists};
|
use mas_storage::user::{
|
||||||
use mas_templates::{
|
add_user_email, add_user_email_verification_code, register_user, start_session, username_exists,
|
||||||
FieldError, FormError, RegisterContext, RegisterFormField, TemplateContext, Templates,
|
|
||||||
ToFormState,
|
|
||||||
};
|
};
|
||||||
|
use mas_templates::{
|
||||||
|
EmailVerificationContext, FieldError, FormError, RegisterContext, RegisterFormField,
|
||||||
|
TemplateContext, Templates, ToFormState,
|
||||||
|
};
|
||||||
|
use rand::{distributions::Uniform, thread_rng, Rng};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use sqlx::{PgConnection, PgPool};
|
use sqlx::{PgConnection, PgPool};
|
||||||
|
|
||||||
@ -39,6 +46,7 @@ use super::shared::OptionalPostAuthAction;
|
|||||||
#[derive(Debug, Deserialize, Serialize)]
|
#[derive(Debug, Deserialize, Serialize)]
|
||||||
pub(crate) struct RegisterForm {
|
pub(crate) struct RegisterForm {
|
||||||
username: String,
|
username: String,
|
||||||
|
email: String,
|
||||||
password: String,
|
password: String,
|
||||||
password_confirm: String,
|
password_confirm: String,
|
||||||
}
|
}
|
||||||
@ -78,6 +86,7 @@ pub(crate) async fn get(
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) async fn post(
|
pub(crate) async fn post(
|
||||||
|
Extension(mailer): Extension<Mailer>,
|
||||||
Extension(templates): Extension<Templates>,
|
Extension(templates): Extension<Templates>,
|
||||||
Extension(pool): Extension<PgPool>,
|
Extension(pool): Extension<PgPool>,
|
||||||
Query(query): Query<OptionalPostAuthAction>,
|
Query(query): Query<OptionalPostAuthAction>,
|
||||||
@ -100,6 +109,12 @@ pub(crate) async fn post(
|
|||||||
state.add_error_on_field(RegisterFormField::Username, FieldError::Exists);
|
state.add_error_on_field(RegisterFormField::Username, FieldError::Exists);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if form.email.is_empty() {
|
||||||
|
state.add_error_on_field(RegisterFormField::Email, FieldError::Required);
|
||||||
|
} else if Address::from_str(&form.email).is_err() {
|
||||||
|
state.add_error_on_field(RegisterFormField::Email, FieldError::Invalid);
|
||||||
|
}
|
||||||
|
|
||||||
if form.password.is_empty() {
|
if form.password.is_empty() {
|
||||||
state.add_error_on_field(RegisterFormField::Password, FieldError::Required);
|
state.add_error_on_field(RegisterFormField::Password, FieldError::Required);
|
||||||
}
|
}
|
||||||
@ -133,13 +148,32 @@ pub(crate) async fn post(
|
|||||||
let pfh = Argon2::default();
|
let pfh = Argon2::default();
|
||||||
let user = register_user(&mut txn, pfh, &form.username, &form.password).await?;
|
let user = register_user(&mut txn, pfh, &form.username, &form.password).await?;
|
||||||
|
|
||||||
|
let user_email = add_user_email(&mut txn, &user, &form.email).await?;
|
||||||
|
|
||||||
|
// First, generate a code
|
||||||
|
let range = Uniform::<u32>::from(0..1_000_000);
|
||||||
|
let code = thread_rng().sample(range).to_string();
|
||||||
|
|
||||||
|
let address: Address = user_email.email.parse()?;
|
||||||
|
|
||||||
|
let verification = add_user_email_verification_code(&mut txn, user_email, code).await?;
|
||||||
|
|
||||||
|
// And send the verification email
|
||||||
|
let mailbox = Mailbox::new(Some(user.username.clone()), address);
|
||||||
|
|
||||||
|
let context = EmailVerificationContext::new(user.clone().into(), verification.clone().into());
|
||||||
|
|
||||||
|
mailer.send_verification_email(mailbox, &context).await?;
|
||||||
|
|
||||||
|
let next =
|
||||||
|
mas_router::AccountVerifyEmail::new(verification.data).and_maybe(query.post_auth_action);
|
||||||
|
|
||||||
let session = start_session(&mut txn, user).await?;
|
let session = start_session(&mut txn, user).await?;
|
||||||
|
|
||||||
txn.commit().await?;
|
txn.commit().await?;
|
||||||
|
|
||||||
let cookie_jar = cookie_jar.set_session(&session);
|
let cookie_jar = cookie_jar.set_session(&session);
|
||||||
let reply = query.go_next();
|
Ok((cookie_jar, next.go()).into_response())
|
||||||
Ok((cookie_jar, reply).into_response())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn render(
|
async fn render(
|
||||||
|
@ -331,6 +331,12 @@ impl AccountVerifyEmail {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn and_maybe(mut self, action: Option<PostAuthAction>) -> Self {
|
||||||
|
self.post_auth_action = action;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn and_then(mut self, action: PostAuthAction) -> Self {
|
pub fn and_then(mut self, action: PostAuthAction) -> Self {
|
||||||
self.post_auth_action = Some(action);
|
self.post_auth_action = Some(action);
|
||||||
|
@ -572,7 +572,7 @@ pub async fn get_user_email(
|
|||||||
pub async fn add_user_email(
|
pub async fn add_user_email(
|
||||||
executor: impl PgExecutor<'_>,
|
executor: impl PgExecutor<'_>,
|
||||||
user: &User<PostgresqlBackend>,
|
user: &User<PostgresqlBackend>,
|
||||||
email: String,
|
email: &str,
|
||||||
) -> anyhow::Result<UserEmail<PostgresqlBackend>> {
|
) -> anyhow::Result<UserEmail<PostgresqlBackend>> {
|
||||||
let res = sqlx::query_as!(
|
let res = sqlx::query_as!(
|
||||||
UserEmailLookup,
|
UserEmailLookup,
|
||||||
|
@ -319,6 +319,9 @@ pub enum RegisterFormField {
|
|||||||
/// The username field
|
/// The username field
|
||||||
Username,
|
Username,
|
||||||
|
|
||||||
|
/// The email field
|
||||||
|
Email,
|
||||||
|
|
||||||
/// The password field
|
/// The password field
|
||||||
Password,
|
Password,
|
||||||
|
|
||||||
@ -329,7 +332,7 @@ pub enum RegisterFormField {
|
|||||||
impl FormField for RegisterFormField {
|
impl FormField for RegisterFormField {
|
||||||
fn keep(&self) -> bool {
|
fn keep(&self) -> bool {
|
||||||
match self {
|
match self {
|
||||||
Self::Username => true,
|
Self::Username | Self::Email => true,
|
||||||
Self::Password | Self::PasswordConfirm => false,
|
Self::Password | Self::PasswordConfirm => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -33,6 +33,9 @@ pub enum FieldError {
|
|||||||
/// An unspecified error on the field
|
/// An unspecified error on the field
|
||||||
Unspecified,
|
Unspecified,
|
||||||
|
|
||||||
|
/// Invalid value for this field
|
||||||
|
Invalid,
|
||||||
|
|
||||||
/// That value already exists
|
/// That value already exists
|
||||||
Exists,
|
Exists,
|
||||||
}
|
}
|
||||||
|
@ -33,6 +33,7 @@ limitations under the License.
|
|||||||
|
|
||||||
<input type="hidden" name="csrf" value="{{ csrf_token }}" />
|
<input type="hidden" name="csrf" value="{{ csrf_token }}" />
|
||||||
{{ field::input(label="Username", name="username", form_state=form, autocomplete="username") }}
|
{{ field::input(label="Username", name="username", form_state=form, autocomplete="username") }}
|
||||||
|
{{ field::input(label="Email", name="email", type="email", form_state=form, autocomplete="email") }}
|
||||||
{{ field::input(label="Password", name="password", type="password", form_state=form, autocomplete="new-password") }}
|
{{ field::input(label="Password", name="password", type="password", form_state=form, autocomplete="new-password") }}
|
||||||
{{ field::input(label="Confirm Password", name="password_confirm", type="password", form_state=form, autocomplete="new-password") }}
|
{{ field::input(label="Confirm Password", name="password_confirm", type="password", form_state=form, autocomplete="new-password") }}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user