You've already forked authentication-service
mirror of
https://github.com/matrix-org/matrix-authentication-service.git
synced 2025-08-07 17:03:01 +03:00
Show a proper 'link expired' page
This commit is contained in:
@@ -28,8 +28,8 @@ use mas_policy::Policy;
|
|||||||
use mas_router::UrlBuilder;
|
use mas_router::UrlBuilder;
|
||||||
use mas_storage::{BoxClock, BoxRepository, BoxRng};
|
use mas_storage::{BoxClock, BoxRepository, BoxRng};
|
||||||
use mas_templates::{
|
use mas_templates::{
|
||||||
EmptyContext, ErrorContext, FieldError, FormState, RecoveryFinishContext,
|
EmptyContext, ErrorContext, FieldError, FormState, RecoveryExpiredContext,
|
||||||
RecoveryFinishFormField, TemplateContext, Templates,
|
RecoveryFinishContext, RecoveryFinishFormField, TemplateContext, Templates,
|
||||||
};
|
};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use zeroize::Zeroizing;
|
use zeroize::Zeroizing;
|
||||||
@@ -78,12 +78,10 @@ pub(crate) async fn get(
|
|||||||
.context("Unknown session")?;
|
.context("Unknown session")?;
|
||||||
|
|
||||||
if !ticket.active(clock.now()) || session.consumed_at.is_some() {
|
if !ticket.active(clock.now()) || session.consumed_at.is_some() {
|
||||||
// TODO: render a 'link expired' page
|
let context = RecoveryExpiredContext::new(session)
|
||||||
let rendered = templates.render_error(
|
.with_csrf(csrf_token.form_value())
|
||||||
&ErrorContext::new()
|
.with_language(locale);
|
||||||
.with_code("Link expired")
|
let rendered = templates.render_recovery_expired(&context)?;
|
||||||
.with_language(&locale),
|
|
||||||
)?;
|
|
||||||
return Ok((cookie_jar, Html(rendered)).into_response());
|
return Ok((cookie_jar, Html(rendered)).into_response());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -155,12 +153,10 @@ pub(crate) async fn post(
|
|||||||
.context("Unknown session")?;
|
.context("Unknown session")?;
|
||||||
|
|
||||||
if !ticket.active(clock.now()) || session.consumed_at.is_some() {
|
if !ticket.active(clock.now()) || session.consumed_at.is_some() {
|
||||||
// TODO: render a 'link expired' page
|
let context = RecoveryExpiredContext::new(session)
|
||||||
let rendered = templates.render_error(
|
.with_csrf(csrf_token.form_value())
|
||||||
&ErrorContext::new()
|
.with_language(locale);
|
||||||
.with_code("Link expired")
|
let rendered = templates.render_recovery_expired(&context)?;
|
||||||
.with_language(&locale),
|
|
||||||
)?;
|
|
||||||
return Ok((cookie_jar, Html(rendered)).into_response());
|
return Ok((cookie_jar, Html(rendered)).into_response());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1056,6 +1056,39 @@ impl TemplateContext for RecoveryProgressContext {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Context used by the `pages/recovery/expired.html` template
|
||||||
|
#[derive(Serialize)]
|
||||||
|
pub struct RecoveryExpiredContext {
|
||||||
|
session: UserRecoverySession,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RecoveryExpiredContext {
|
||||||
|
/// Constructs a context for the recovery expired page
|
||||||
|
#[must_use]
|
||||||
|
pub fn new(session: UserRecoverySession) -> Self {
|
||||||
|
Self { session }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TemplateContext for RecoveryExpiredContext {
|
||||||
|
fn sample(now: chrono::DateTime<Utc>, rng: &mut impl Rng) -> Vec<Self>
|
||||||
|
where
|
||||||
|
Self: Sized,
|
||||||
|
{
|
||||||
|
let session = UserRecoverySession {
|
||||||
|
id: Ulid::from_datetime_with_source(now.into(), rng),
|
||||||
|
email: "name@mail.com".to_owned(),
|
||||||
|
user_agent: UserAgent::parse("Mozilla/5.0".to_owned()),
|
||||||
|
ip_address: None,
|
||||||
|
locale: "en".to_owned(),
|
||||||
|
created_at: now,
|
||||||
|
consumed_at: None,
|
||||||
|
};
|
||||||
|
|
||||||
|
vec![Self { session }]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Fields of the account recovery finish form
|
/// Fields of the account recovery finish form
|
||||||
#[derive(Serialize, Deserialize, Debug, Clone, Copy, Hash, PartialEq, Eq)]
|
#[derive(Serialize, Deserialize, Debug, Clone, Copy, Hash, PartialEq, Eq)]
|
||||||
#[serde(rename_all = "snake_case")]
|
#[serde(rename_all = "snake_case")]
|
||||||
|
@@ -46,12 +46,12 @@ pub use self::{
|
|||||||
DeviceLinkFormField, EmailAddContext, EmailRecoveryContext, EmailVerificationContext,
|
DeviceLinkFormField, EmailAddContext, EmailRecoveryContext, EmailVerificationContext,
|
||||||
EmailVerificationPageContext, EmptyContext, ErrorContext, FormPostContext, IndexContext,
|
EmailVerificationPageContext, EmptyContext, ErrorContext, FormPostContext, IndexContext,
|
||||||
LoginContext, LoginFormField, NotFoundContext, PolicyViolationContext, PostAuthContext,
|
LoginContext, LoginFormField, NotFoundContext, PolicyViolationContext, PostAuthContext,
|
||||||
PostAuthContextInner, ReauthContext, ReauthFormField, RecoveryFinishContext,
|
PostAuthContextInner, ReauthContext, ReauthFormField, RecoveryExpiredContext,
|
||||||
RecoveryFinishFormField, RecoveryProgressContext, RecoveryStartContext,
|
RecoveryFinishContext, RecoveryFinishFormField, RecoveryProgressContext,
|
||||||
RecoveryStartFormField, RegisterContext, RegisterFormField, SiteBranding, SiteConfigExt,
|
RecoveryStartContext, RecoveryStartFormField, RegisterContext, RegisterFormField,
|
||||||
SiteFeatures, TemplateContext, UpstreamExistingLinkContext, UpstreamRegister,
|
SiteBranding, SiteConfigExt, SiteFeatures, TemplateContext, UpstreamExistingLinkContext,
|
||||||
UpstreamRegisterFormField, UpstreamSuggestLink, WithCaptcha, WithCsrf, WithLanguage,
|
UpstreamRegister, UpstreamRegisterFormField, UpstreamSuggestLink, WithCaptcha, WithCsrf,
|
||||||
WithOptionalSession, WithSession,
|
WithLanguage, WithOptionalSession, WithSession,
|
||||||
},
|
},
|
||||||
forms::{FieldError, FormError, FormField, FormState, ToFormState},
|
forms::{FieldError, FormError, FormField, FormState, ToFormState},
|
||||||
};
|
};
|
||||||
@@ -357,6 +357,9 @@ register_templates! {
|
|||||||
/// Render the account recovery finish page
|
/// Render the account recovery finish page
|
||||||
pub fn render_recovery_finish(WithLanguage<WithCsrf<RecoveryFinishContext>>) { "pages/recovery/finish.html" }
|
pub fn render_recovery_finish(WithLanguage<WithCsrf<RecoveryFinishContext>>) { "pages/recovery/finish.html" }
|
||||||
|
|
||||||
|
/// Render the account recovery link expired page
|
||||||
|
pub fn render_recovery_expired(WithLanguage<WithCsrf<RecoveryExpiredContext>>) { "pages/recovery/expired.html" }
|
||||||
|
|
||||||
/// Render the account recovery disabled page
|
/// Render the account recovery disabled page
|
||||||
pub fn render_recovery_disabled(WithLanguage<EmptyContext>) { "pages/recovery/disabled.html" }
|
pub fn render_recovery_disabled(WithLanguage<EmptyContext>) { "pages/recovery/disabled.html" }
|
||||||
|
|
||||||
@@ -428,6 +431,7 @@ impl Templates {
|
|||||||
check::render_recovery_start(self, now, rng)?;
|
check::render_recovery_start(self, now, rng)?;
|
||||||
check::render_recovery_progress(self, now, rng)?;
|
check::render_recovery_progress(self, now, rng)?;
|
||||||
check::render_recovery_finish(self, now, rng)?;
|
check::render_recovery_finish(self, now, rng)?;
|
||||||
|
check::render_recovery_expired(self, now, rng)?;
|
||||||
check::render_recovery_disabled(self, now, rng)?;
|
check::render_recovery_disabled(self, now, rng)?;
|
||||||
check::render_reauth(self, now, rng)?;
|
check::render_reauth(self, now, rng)?;
|
||||||
check::render_form_post::<EmptyContext>(self, now, rng)?;
|
check::render_form_post::<EmptyContext>(self, now, rng)?;
|
||||||
|
@@ -14,16 +14,16 @@ See the License for the specific language governing permissions and
|
|||||||
limitations under the License.
|
limitations under the License.
|
||||||
#}
|
#}
|
||||||
|
|
||||||
{% macro link(text, href="#", class="") %}
|
{% macro link(text, href="#", class="", size="lg") %}
|
||||||
<a class="cpd-button {{ class }}" data-kind="primary" data-size="lg" href="{{ href | prefix_url }}">{{ text }}</a>
|
<a class="cpd-button {{ class }}" data-kind="primary" data-size="{{ size }}" href="{{ href | prefix_url }}">{{ text }}</a>
|
||||||
{% endmacro %}
|
{% endmacro %}
|
||||||
|
|
||||||
{% macro link_text(text, href="#", class="") %}
|
{% macro link_text(text, href="#", class="") %}
|
||||||
<a class="cpd-link {{ class }}" data-kind="primary" href="{{ href | prefix_url }}">{{ text }}</a>
|
<a class="cpd-link {{ class }}" data-kind="primary" href="{{ href | prefix_url }}">{{ text }}</a>
|
||||||
{% endmacro %}
|
{% endmacro %}
|
||||||
|
|
||||||
{% macro link_outline(text, href="#", class="") %}
|
{% macro link_outline(text, href="#", class="", size="lg") %}
|
||||||
<a class="cpd-button {{ class }}" data-kind="secondary" data-size="lg" href="{{ href | prefix_url }}">{{ text }}</a>
|
<a class="cpd-button {{ class }}" data-kind="secondary" data-size="{{ size }}" href="{{ href | prefix_url }}">{{ text }}</a>
|
||||||
{% endmacro %}
|
{% endmacro %}
|
||||||
|
|
||||||
{% macro link_tertiary(text, href="#", class="", size="lg") %}
|
{% macro link_tertiary(text, href="#", class="", size="lg") %}
|
||||||
|
40
templates/pages/recovery/expired.html
Normal file
40
templates/pages/recovery/expired.html
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
{#
|
||||||
|
Copyright 2024 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
#}
|
||||||
|
|
||||||
|
{% extends "base.html" %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<header class="page-heading">
|
||||||
|
<div class="icon invalid">
|
||||||
|
{{ icon.error() }}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="header">
|
||||||
|
<h1 class="title">{{ _("mas.recovery.expired.heading") }}</h1>
|
||||||
|
<p class="text [&>span]:font-medium">{{ _("mas.recovery.expired.description", email=session.email) }}</p>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<div class="flex flex-col gap-6">
|
||||||
|
<form class="cpd-form-root" method="POST" action="{{ '/recover/progress/' + session.id | prefix_url }}">
|
||||||
|
<input type="hidden" name="csrf" value="{{ csrf_token }}" />
|
||||||
|
|
||||||
|
{{ button.button(text=_("mas.recovery.expired.resend_email"), type="submit") }}
|
||||||
|
</form>
|
||||||
|
|
||||||
|
{{ button.link_outline(text=_("action.start_over"), href="/login") }}
|
||||||
|
</div>
|
||||||
|
{% endblock content %}
|
@@ -23,6 +23,10 @@
|
|||||||
"sign_out": "Sign out",
|
"sign_out": "Sign out",
|
||||||
"@sign_out": {
|
"@sign_out": {
|
||||||
"context": "pages/consent.html:71:28-48, pages/device_consent.html:141:30-50, pages/index.html:36:28-48, pages/policy_violation.html:46:28-48, pages/sso.html:53:28-48, pages/upstream_oauth2/link_mismatch.html:32:24-44, pages/upstream_oauth2/suggest_link.html:40:26-46"
|
"context": "pages/consent.html:71:28-48, pages/device_consent.html:141:30-50, pages/index.html:36:28-48, pages/policy_violation.html:46:28-48, pages/sso.html:53:28-48, pages/upstream_oauth2/link_mismatch.html:32:24-44, pages/upstream_oauth2/suggest_link.html:40:26-46"
|
||||||
|
},
|
||||||
|
"start_over": "Start over",
|
||||||
|
"@start_over": {
|
||||||
|
"context": "pages/recovery/expired.html:38:32-54"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"app": {
|
"app": {
|
||||||
@@ -395,6 +399,22 @@
|
|||||||
"context": "pages/recovery/disabled.html:26:27-61"
|
"context": "pages/recovery/disabled.html:26:27-61"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"expired": {
|
||||||
|
"description": "Request a new email that will be sent to: <span>%(email)s</span>.",
|
||||||
|
"@description": {
|
||||||
|
"context": "pages/recovery/expired.html:27:46-104",
|
||||||
|
"description": "Description on the page shown when a user tries to use an expired recovery link"
|
||||||
|
},
|
||||||
|
"heading": "The link to reset your password has expired",
|
||||||
|
"@heading": {
|
||||||
|
"context": "pages/recovery/expired.html:26:27-60",
|
||||||
|
"description": "Title on the page shown when a user tries to use an expired recovery link"
|
||||||
|
},
|
||||||
|
"resend_email": "Resend email",
|
||||||
|
"@resend_email": {
|
||||||
|
"context": "pages/recovery/expired.html:35:28-66"
|
||||||
|
}
|
||||||
|
},
|
||||||
"finish": {
|
"finish": {
|
||||||
"confirm": "Enter new password again",
|
"confirm": "Enter new password again",
|
||||||
"@confirm": {
|
"@confirm": {
|
||||||
|
Reference in New Issue
Block a user