Remove the reauth view

This commit is contained in:
Quentin Gliech
2025-04-11 13:35:59 +02:00
parent cf732ac8f0
commit e22016f85c
6 changed files with 6 additions and 317 deletions
-4
View File
@@ -371,10 +371,6 @@ where
get(self::views::login::get).post(self::views::login::post),
)
.route(mas_router::Logout::route(), post(self::views::logout::post))
.route(
mas_router::Reauth::route(),
get(self::views::reauth::get).post(self::views::reauth::post),
)
.route(
mas_router::Register::route(),
get(self::views::register::get),
-1
View File
@@ -8,7 +8,6 @@ pub mod app;
pub mod index;
pub mod login;
pub mod logout;
pub mod reauth;
pub mod recovery;
pub mod register;
pub mod shared;
-189
View File
@@ -1,189 +0,0 @@
// Copyright 2024 New Vector Ltd.
// Copyright 2021-2024 The Matrix.org Foundation C.I.C.
//
// SPDX-License-Identifier: AGPL-3.0-only
// Please see LICENSE in the repository root for full details.
use anyhow::Context;
use axum::{
extract::{Form, Query, State},
response::{Html, IntoResponse, Response},
};
use hyper::StatusCode;
use mas_axum_utils::{
FancyError, SessionInfoExt,
cookies::CookieJar,
csrf::{CsrfExt, ProtectedForm},
};
use mas_router::UrlBuilder;
use mas_storage::{
BoxClock, BoxRepository, BoxRng,
user::{BrowserSessionRepository, UserPasswordRepository},
};
use mas_templates::{ReauthContext, TemplateContext, Templates};
use serde::Deserialize;
use zeroize::Zeroizing;
use super::shared::OptionalPostAuthAction;
use crate::{
BoundActivityTracker, PreferredLanguage, SiteConfig,
passwords::PasswordManager,
session::{SessionOrFallback, load_session_or_fallback},
};
#[derive(Deserialize, Debug)]
pub(crate) struct ReauthForm {
password: String,
}
#[tracing::instrument(name = "handlers.views.reauth.get", skip_all, err)]
pub(crate) async fn get(
mut rng: BoxRng,
clock: BoxClock,
PreferredLanguage(locale): PreferredLanguage,
State(templates): State<Templates>,
State(url_builder): State<UrlBuilder>,
State(site_config): State<SiteConfig>,
activity_tracker: BoundActivityTracker,
mut repo: BoxRepository,
Query(query): Query<OptionalPostAuthAction>,
cookie_jar: CookieJar,
) -> Result<Response, FancyError> {
if !site_config.password_login_enabled {
// XXX: do something better here
return Ok(url_builder
.redirect(&mas_router::Account::default())
.into_response());
}
let (cookie_jar, maybe_session) = match load_session_or_fallback(
cookie_jar, &clock, &mut rng, &templates, &locale, &mut repo,
)
.await?
{
SessionOrFallback::MaybeSession {
cookie_jar,
maybe_session,
..
} => (cookie_jar, maybe_session),
SessionOrFallback::Fallback { response } => return Ok(response),
};
let Some(session) = maybe_session else {
// If there is no session, redirect to the login screen, keeping the
// PostAuthAction
let login = mas_router::Login::from(query.post_auth_action);
return Ok((cookie_jar, url_builder.redirect(&login)).into_response());
};
let (csrf_token, cookie_jar) = cookie_jar.csrf_token(&clock, &mut rng);
activity_tracker
.record_browser_session(&clock, &session)
.await;
let ctx = ReauthContext::default();
let next = query.load_context(&mut repo).await?;
let ctx = if let Some(next) = next {
ctx.with_post_action(next)
} else {
ctx
};
let ctx = ctx
.with_session(session)
.with_csrf(csrf_token.form_value())
.with_language(locale);
let content = templates.render_reauth(&ctx)?;
Ok((cookie_jar, Html(content)).into_response())
}
#[tracing::instrument(name = "handlers.views.reauth.post", skip_all, err)]
pub(crate) async fn post(
mut rng: BoxRng,
clock: BoxClock,
PreferredLanguage(locale): PreferredLanguage,
State(templates): State<Templates>,
State(password_manager): State<PasswordManager>,
State(url_builder): State<UrlBuilder>,
State(site_config): State<SiteConfig>,
mut repo: BoxRepository,
Query(query): Query<OptionalPostAuthAction>,
cookie_jar: CookieJar,
Form(form): Form<ProtectedForm<ReauthForm>>,
) -> Result<Response, FancyError> {
if !site_config.password_login_enabled {
// XXX: do something better here
return Ok(StatusCode::METHOD_NOT_ALLOWED.into_response());
}
let form = cookie_jar.verify_form(&clock, form)?;
let (cookie_jar, maybe_session) = match load_session_or_fallback(
cookie_jar, &clock, &mut rng, &templates, &locale, &mut repo,
)
.await?
{
SessionOrFallback::MaybeSession {
cookie_jar,
maybe_session,
..
} => (cookie_jar, maybe_session),
SessionOrFallback::Fallback { response } => return Ok(response),
};
let Some(session) = maybe_session else {
// If there is no session, redirect to the login screen, keeping the
// PostAuthAction
let login = mas_router::Login::from(query.post_auth_action);
return Ok((cookie_jar, url_builder.redirect(&login)).into_response());
};
// Load the user password
let user_password = repo
.user_password()
.active(&session.user)
.await?
.context("User has no password")?;
let password = Zeroizing::new(form.password.as_bytes().to_vec());
// TODO: recover from errors
// Verify the password, and upgrade it on-the-fly if needed
let new_password_hash = password_manager
.verify_and_upgrade(
&mut rng,
user_password.version,
password,
user_password.hashed_password.clone(),
)
.await?;
let user_password = if let Some((version, new_password_hash)) = new_password_hash {
// Save the upgraded password
repo.user_password()
.add(
&mut rng,
&clock,
&session.user,
version,
new_password_hash,
Some(&user_password),
)
.await?
} else {
user_password
};
// Mark the session as authenticated by the password
repo.browser_session()
.authenticate_with_password(&mut rng, &clock, &session, &user_password)
.await?;
let cookie_jar = cookie_jar.set_session(&session);
repo.save().await?;
let reply = query.go_next(&url_builder);
Ok((cookie_jar, reply).into_response())
}
-60
View File
@@ -255,66 +255,6 @@ impl SimpleRoute for Logout {
const PATH: &'static str = "/logout";
}
/// `GET|POST /reauth`
#[derive(Default, Debug, Clone)]
pub struct Reauth {
post_auth_action: Option<PostAuthAction>,
}
impl Reauth {
#[must_use]
pub fn and_then(action: PostAuthAction) -> Self {
Self {
post_auth_action: Some(action),
}
}
#[must_use]
pub fn and_continue_grant(data: Ulid) -> Self {
Self {
post_auth_action: Some(PostAuthAction::continue_grant(data)),
}
}
#[must_use]
pub fn and_continue_device_code_grant(data: Ulid) -> Self {
Self {
post_auth_action: Some(PostAuthAction::continue_device_code_grant(data)),
}
}
/// Get a reference to the reauth's post auth action.
#[must_use]
pub fn post_auth_action(&self) -> Option<&PostAuthAction> {
self.post_auth_action.as_ref()
}
pub fn go_next(&self, url_builder: &UrlBuilder) -> axum::response::Redirect {
match &self.post_auth_action {
Some(action) => action.go_next(url_builder),
None => url_builder.redirect(&Index),
}
}
}
impl Route for Reauth {
type Query = PostAuthAction;
fn route() -> &'static str {
"/reauth"
}
fn query(&self) -> Option<&Self::Query> {
self.post_auth_action.as_ref()
}
}
impl From<Option<PostAuthAction>> for Reauth {
fn from(post_auth_action: Option<PostAuthAction>) -> Self {
Self { post_auth_action }
}
}
/// `POST /register`
#[derive(Default, Debug, Clone)]
pub struct Register {
+2 -55
View File
@@ -381,7 +381,7 @@ impl FormField for LoginFormField {
}
}
/// Inner context used in login and reauth screens. See [`PostAuthContext`].
/// Inner context used in login screen. See [`PostAuthContext`].
#[derive(Serialize)]
#[serde(tag = "kind", rename_all = "snake_case")]
pub enum PostAuthContextInner {
@@ -420,7 +420,7 @@ pub enum PostAuthContextInner {
ManageAccount,
}
/// Context used in login and reauth screens, for the post-auth action to do
/// Context used in login screen, for the post-auth action to do
#[derive(Serialize)]
pub struct PostAuthContext {
/// The post auth action params from the URL
@@ -734,59 +734,6 @@ impl PolicyViolationContext {
}
}
/// Fields of the reauthentication form
#[derive(Serialize, Deserialize, Debug, Clone, Copy, Hash, PartialEq, Eq)]
#[serde(rename_all = "kebab-case")]
pub enum ReauthFormField {
/// The password field
Password,
}
impl FormField for ReauthFormField {
fn keep(&self) -> bool {
match self {
Self::Password => false,
}
}
}
/// Context used by the `reauth.html` template
#[derive(Serialize, Default)]
pub struct ReauthContext {
form: FormState<ReauthFormField>,
next: Option<PostAuthContext>,
}
impl TemplateContext for ReauthContext {
fn sample(_now: chrono::DateTime<Utc>, _rng: &mut impl Rng) -> Vec<Self>
where
Self: Sized,
{
// TODO: samples with errors
vec![ReauthContext {
form: FormState::default(),
next: None,
}]
}
}
impl ReauthContext {
/// Add an error on the reauthentication form
#[must_use]
pub fn with_form_state(self, form: FormState<ReauthFormField>) -> Self {
Self { form, ..self }
}
/// Add a post authentication action to the context
#[must_use]
pub fn with_post_action(self, next: PostAuthContext) -> Self {
Self {
next: Some(next),
..self
}
}
}
/// Context used by the `sso.html` template
#[derive(Serialize)]
pub struct CompatSsoContext {
+4 -8
View File
@@ -38,10 +38,10 @@ pub use self::{
DeviceConsentContext, DeviceLinkContext, DeviceLinkFormField, EmailRecoveryContext,
EmailVerificationContext, EmptyContext, ErrorContext, FormPostContext, IndexContext,
LoginContext, LoginFormField, NotFoundContext, PasswordRegisterContext,
PolicyViolationContext, PostAuthContext, PostAuthContextInner, ReauthContext,
ReauthFormField, RecoveryExpiredContext, RecoveryFinishContext, RecoveryFinishFormField,
RecoveryProgressContext, RecoveryStartContext, RecoveryStartFormField, RegisterContext,
RegisterFormField, RegisterStepsDisplayNameContext, RegisterStepsDisplayNameFormField,
PolicyViolationContext, PostAuthContext, PostAuthContextInner, RecoveryExpiredContext,
RecoveryFinishContext, RecoveryFinishFormField, RecoveryProgressContext,
RecoveryStartContext, RecoveryStartFormField, RegisterContext, RegisterFormField,
RegisterStepsDisplayNameContext, RegisterStepsDisplayNameFormField,
RegisterStepsEmailInUseContext, RegisterStepsVerifyEmailContext,
RegisterStepsVerifyEmailFormField, SiteBranding, SiteConfigExt, SiteFeatures,
TemplateContext, UpstreamExistingLinkContext, UpstreamRegister, UpstreamRegisterFormField,
@@ -372,9 +372,6 @@ register_templates! {
/// Render the account recovery disabled page
pub fn render_recovery_disabled(WithLanguage<EmptyContext>) { "pages/recovery/disabled.html" }
/// Render the re-authentication form
pub fn render_reauth(WithLanguage<WithCsrf<WithSession<ReauthContext>>>) { "pages/reauth.html" }
/// Render the form used by the form_post response mode
pub fn render_form_post<T: Serialize>(WithLanguage<FormPostContext<T>>) { "form_post.html" }
@@ -456,7 +453,6 @@ impl Templates {
check::render_recovery_expired(self, now, rng)?;
check::render_recovery_consumed(self, now, rng)?;
check::render_recovery_disabled(self, now, rng)?;
check::render_reauth(self, now, rng)?;
check::render_form_post::<EmptyContext>(self, now, rng)?;
check::render_error(self, now, rng)?;
check::render_email_verification_txt(self, now, rng)?;