Skip to content

Commit

Permalink
Make the password registration create a user_registration
Browse files Browse the repository at this point in the history
  • Loading branch information
sandhose committed Jan 14, 2025
1 parent d94fd25 commit 941ab21
Show file tree
Hide file tree
Showing 6 changed files with 95 additions and 43 deletions.
77 changes: 46 additions & 31 deletions crates/handlers/src/views/register/password.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@ use mas_matrix::BoxHomeserverConnection;
use mas_policy::Policy;
use mas_router::UrlBuilder;
use mas_storage::{
queue::{ProvisionUserJob, QueueJobRepositoryExt as _},
user::{BrowserSessionRepository, UserEmailRepository, UserPasswordRepository, UserRepository},
queue::{QueueJobRepositoryExt as _, SendEmailAuthenticationCodeJob},
user::{UserEmailRepository, UserRepository},
BoxClock, BoxRepository, BoxRng, RepositoryAccess,
};
use mas_templates::{
Expand Down Expand Up @@ -141,6 +141,8 @@ pub(crate) async fn post(
Form(form): Form<ProtectedForm<RegisterForm>>,
) -> Result<Response, FancyError> {
let user_agent = user_agent.map(|ua| UserAgent::parse(ua.as_str().to_owned()));

let ip_address = activity_tracker.ip();
if !site_config.password_registration_enabled {
return Ok(StatusCode::METHOD_NOT_ALLOWED.into_response());
}
Expand Down Expand Up @@ -296,49 +298,62 @@ pub(crate) async fn post(
return Ok((cookie_jar, Html(content)).into_response());
}

let user = repo.user().add(&mut rng, &clock, form.username).await?;

if let Some(tos_uri) = &site_config.tos_uri {
repo.user_terms()
.accept_terms(&mut rng, &clock, &user, tos_uri.clone())
.await?;
}
let post_auth_action = query
.post_auth_action
.map(serde_json::to_value)
.transpose()?;
let registration = repo
.user_registration()
.add(&mut rng, &clock, ip_address, user_agent, post_auth_action)
.await?;

let password = Zeroizing::new(form.password.into_bytes());
let (version, hashed_password) = password_manager.hash(&mut rng, password).await?;
let user_password = repo
.user_password()
.add(&mut rng, &clock, &user, version, hashed_password, None)
let registration = repo
.user_registration()
.set_username(registration, form.username)
.await?;

let user_email = repo
let registration = if let Some(tos_uri) = &site_config.tos_uri {
repo.user_registration()
.set_terms_url(registration, tos_uri.clone())
.await?
} else {
registration
};

// Create a new user email authentication session
let user_email_authentication = repo
.user_email()
.add(&mut rng, &clock, &user, form.email)
.add_authentication_for_registration(&mut rng, &clock, form.email, &registration)
.await?;

let next = mas_router::AccountVerifyEmail::new(user_email.id).and_maybe(query.post_auth_action);

let session = repo
.browser_session()
.add(&mut rng, &clock, &user, user_agent)
// Schedule a job to verify the email
repo.queue_job()
.schedule_job(
&mut rng,
&clock,
SendEmailAuthenticationCodeJob::new(&user_email_authentication, locale.to_string()),
)
.await?;

repo.browser_session()
.authenticate_with_password(&mut rng, &clock, &session, &user_password)
let registration = repo
.user_registration()
.set_email_authentication(registration, &user_email_authentication)
.await?;

repo.queue_job()
.schedule_job(&mut rng, &clock, ProvisionUserJob::new(&user))
// Hash the password
let password = Zeroizing::new(form.password.into_bytes());
let (version, hashed_password) = password_manager.hash(&mut rng, password).await?;

// Add the password to the registration
let registration = repo
.user_registration()
.set_password(registration, hashed_password, version)
.await?;

repo.save().await?;

activity_tracker
.record_browser_session(&clock, &session)
.await;

let cookie_jar = cookie_jar.set_session(&session);
Ok((cookie_jar, url_builder.redirect(&next)).into_response())
// TODO: redirect to the next step on the registration
Ok(format!("{}", registration.id).into_response())
}

async fn render(
Expand Down
23 changes: 21 additions & 2 deletions crates/tasks/src/email.rs
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,22 @@ impl RunnableJob for SendEmailAuthenticationCodeJob {
None
};

// Load the registration, if any
let registration =
if let Some(registration_id) = user_email_authentication.user_registration_id {
Some(
repo.user_registration()
.lookup(registration_id)
.await
.map_err(JobError::retry)?
.ok_or(JobError::fail(anyhow::anyhow!(
"Failed to load user registration"
)))?,
)
} else {
None
};

// Generate a new 6-digit authentication code
let range = Uniform::<u32>::from(0..1_000_000);
let code = rng.sample(range);
Expand All @@ -98,14 +114,17 @@ impl RunnableJob for SendEmailAuthenticationCodeJob {
.email
.parse()
.map_err(JobError::fail)?;
let username = browser_session.as_ref().map(|s| s.user.username.clone());
let username_from_session = browser_session.as_ref().map(|s| s.user.username.clone());
let username_from_registration = registration.as_ref().and_then(|r| r.username.clone());
let username = username_from_registration.or(username_from_session);
let mailbox = Mailbox::new(username, address);

info!("Sending email verification code to {}", mailbox);

let language = self.language().parse().map_err(JobError::fail)?;

let context = EmailVerificationContext::new(code, browser_session).with_language(language);
let context = EmailVerificationContext::new(code, browser_session, registration)
.with_language(language);
mailer
.send_verification_email(mailbox, &context)
.await
Expand Down
8 changes: 7 additions & 1 deletion crates/templates/src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ use mas_data_model::{
DeviceCodeGrant, UpstreamOAuthLink, UpstreamOAuthProvider, UpstreamOAuthProviderClaimsImports,
UpstreamOAuthProviderDiscoveryMode, UpstreamOAuthProviderPkceMode,
UpstreamOAuthProviderTokenAuthMethod, User, UserAgent, UserEmail, UserEmailAuthenticationCode,
UserRecoverySession,
UserRecoverySession, UserRegistration,
};
use mas_i18n::DataLocale;
use mas_iana::jose::JsonWebSignatureAlg;
Expand Down Expand Up @@ -878,7 +878,10 @@ impl TemplateContext for EmailRecoveryContext {
/// Context used by the `emails/verification.{txt,html,subject}` templates
#[derive(Serialize)]
pub struct EmailVerificationContext {
#[serde(skip_serializing_if = "Option::is_none")]
browser_session: Option<BrowserSession>,
#[serde(skip_serializing_if = "Option::is_none")]
user_registration: Option<UserRegistration>,
authentication_code: UserEmailAuthenticationCode,
}

Expand All @@ -888,9 +891,11 @@ impl EmailVerificationContext {
pub fn new(
authentication_code: UserEmailAuthenticationCode,
browser_session: Option<BrowserSession>,
user_registration: Option<UserRegistration>,
) -> Self {
Self {
browser_session,
user_registration,
authentication_code,
}
}
Expand Down Expand Up @@ -926,6 +931,7 @@ impl TemplateContext for EmailVerificationContext {

Self {
browser_session: Some(browser_session),
user_registration: None,
authentication_code,
}
})
Expand Down
8 changes: 7 additions & 1 deletion templates/emails/verification.html
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,12 @@

{%- set _ = translator(lang) -%}

{{ _("mas.emails.greeting", username=browser_session.user.username | default("user")) }}<br />
{%- if browser_session is defined -%}
{%- set username = browser_session.user.username -%}
{%- elif user_registration is defined -%}
{%- set username = user_registration.username -%}
{%- endif -%}

{{ _("mas.emails.greeting", username=(username|default("user"))) }}<br />
<br />
{{ _("mas.emails.verify.body_html", code=authentication_code.code) }}<br />
8 changes: 7 additions & 1 deletion templates/emails/verification.txt
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,12 @@ Please see LICENSE in the repository root for full details.

{%- set _ = translator(lang) -%}

{{ _("mas.emails.greeting", username=browser_session.user.username | default("user")) }}
{%- if browser_session is defined -%}
{%- set username = browser_session.user.username -%}
{%- elif user_registration is defined -%}
{%- set username = user_registration.username -%}
{%- endif -%}

{{ _("mas.emails.greeting", username=(username|default("user"))) }}

{{ _("mas.emails.verify.body_text", code=authentication_code.code) }}
14 changes: 7 additions & 7 deletions translations/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -219,7 +219,7 @@
"emails": {
"greeting": "Hello %(username)s,",
"@greeting": {
"context": "emails/verification.html:11:3-85, emails/verification.txt:11:3-85",
"context": "emails/verification.html:17:3-64, emails/verification.txt:17:3-64",
"description": "Greeting at the top of emails sent to the user"
},
"recovery": {
Expand Down Expand Up @@ -251,12 +251,12 @@
"verify": {
"body_html": "Your verification code to confirm this email address is: <strong>%(code)s</strong>",
"@body_html": {
"context": "emails/verification.html:13:3-66",
"context": "emails/verification.html:19:3-66",
"description": "The body of the email sent to verify an email address (HTML)"
},
"body_text": "Your verification code to confirm this email address is: %(code)s",
"@body_text": {
"context": "emails/verification.txt:13:3-66",
"context": "emails/verification.txt:19:3-66",
"description": "The body of the email sent to verify an email address (text)"
},
"subject": "Your email verification code is: %(code)s",
Expand Down Expand Up @@ -327,7 +327,7 @@
},
"continue_with_provider": "Continue with %(provider)s",
"@continue_with_provider": {
"context": "pages/login.html:75:15-67, pages/register/index.html:49:15-67",
"context": "pages/login.html:75:15-67, pages/register/index.html:53:15-67",
"description": "Button to log in with an upstream provider"
},
"description": "Please sign in to continue:",
Expand Down Expand Up @@ -513,12 +513,12 @@
"register": {
"call_to_login": "Already have an account?",
"@call_to_login": {
"context": "pages/register/index.html:55:35-66, pages/register/password.html:77:33-64",
"context": "pages/register/index.html:59:35-66, pages/register/password.html:77:33-64",
"description": "Displayed on the registration page to suggest to log in instead"
},
"continue_with_email": "Continue with email address",
"@continue_with_email": {
"context": "pages/register/index.html:40:30-67"
"context": "pages/register/index.html:44:30-67"
},
"create_account": {
"description": "Choose a username to continue.",
Expand Down Expand Up @@ -682,4 +682,4 @@
}
}
}
}
}

0 comments on commit 941ab21

Please sign in to comment.