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
Tidy up upstream linking templates
This commit is contained in:
@@ -34,7 +34,10 @@ use mas_storage::{
|
|||||||
},
|
},
|
||||||
LookupResultExt,
|
LookupResultExt,
|
||||||
};
|
};
|
||||||
use mas_templates::{EmptyContext, TemplateContext, Templates, UpstreamExistingLinkContext};
|
use mas_templates::{
|
||||||
|
EmptyContext, TemplateContext, Templates, UpstreamExistingLinkContext, UpstreamRegister,
|
||||||
|
UpstreamSuggestLink,
|
||||||
|
};
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use sqlx::PgPool;
|
use sqlx::PgPool;
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
@@ -174,7 +177,7 @@ pub(crate) async fn get(
|
|||||||
|
|
||||||
(Some(user_session), None) => {
|
(Some(user_session), None) => {
|
||||||
// Session not linked, but user logged in: suggest linking account
|
// Session not linked, but user logged in: suggest linking account
|
||||||
let ctx = EmptyContext
|
let ctx = UpstreamSuggestLink::new(link.id)
|
||||||
.with_session(user_session)
|
.with_session(user_session)
|
||||||
.with_csrf(csrf_token.form_value());
|
.with_csrf(csrf_token.form_value());
|
||||||
|
|
||||||
@@ -193,7 +196,7 @@ pub(crate) async fn get(
|
|||||||
(None, None) => {
|
(None, None) => {
|
||||||
// Session not linked and used not logged in: suggest creating an
|
// Session not linked and used not logged in: suggest creating an
|
||||||
// account or logging in an existing user
|
// account or logging in an existing user
|
||||||
let ctx = EmptyContext.with_csrf(csrf_token.form_value());
|
let ctx = UpstreamRegister::new(link.id).with_csrf(csrf_token.form_value());
|
||||||
|
|
||||||
templates.render_upstream_oauth2_do_register(&ctx).await?
|
templates.render_upstream_oauth2_do_register(&ctx).await?
|
||||||
}
|
}
|
||||||
|
@@ -48,10 +48,10 @@ pub(crate) async fn post(
|
|||||||
txn.commit().await?;
|
txn.commit().await?;
|
||||||
|
|
||||||
let destination = if let Some(action) = form {
|
let destination = if let Some(action) = form {
|
||||||
mas_router::Login::and_then(action)
|
action.go_next()
|
||||||
} else {
|
} else {
|
||||||
mas_router::Login::default()
|
mas_router::Login::default().go()
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok((cookie_jar, destination.go()))
|
Ok((cookie_jar, destination))
|
||||||
}
|
}
|
||||||
|
@@ -47,12 +47,27 @@ impl OptionalPostAuthAction {
|
|||||||
let grant = Box::new(grant.into());
|
let grant = Box::new(grant.into());
|
||||||
Ok(Some(PostAuthContext::ContinueAuthorizationGrant { grant }))
|
Ok(Some(PostAuthContext::ContinueAuthorizationGrant { grant }))
|
||||||
}
|
}
|
||||||
|
|
||||||
Some(PostAuthAction::ContinueCompatSsoLogin { data }) => {
|
Some(PostAuthAction::ContinueCompatSsoLogin { data }) => {
|
||||||
let login = get_compat_sso_login_by_id(conn, *data).await?;
|
let login = get_compat_sso_login_by_id(conn, *data).await?;
|
||||||
let login = Box::new(login.into());
|
let login = Box::new(login.into());
|
||||||
Ok(Some(PostAuthContext::ContinueCompatSsoLogin { login }))
|
Ok(Some(PostAuthContext::ContinueCompatSsoLogin { login }))
|
||||||
}
|
}
|
||||||
|
|
||||||
Some(PostAuthAction::ChangePassword) => Ok(Some(PostAuthContext::ChangePassword)),
|
Some(PostAuthAction::ChangePassword) => Ok(Some(PostAuthContext::ChangePassword)),
|
||||||
|
|
||||||
|
Some(PostAuthAction::LinkUpstream { id }) => {
|
||||||
|
let (link, provider_id, _user_id) =
|
||||||
|
mas_storage::upstream_oauth2::lookup_link(&mut *conn, *id).await?;
|
||||||
|
|
||||||
|
let provider =
|
||||||
|
mas_storage::upstream_oauth2::lookup_provider(&mut *conn, provider_id).await?;
|
||||||
|
|
||||||
|
let provider = Box::new(provider);
|
||||||
|
let link = Box::new(link);
|
||||||
|
Ok(Some(PostAuthContext::LinkUpstream { provider, link }))
|
||||||
|
}
|
||||||
|
|
||||||
None => Ok(None),
|
None => Ok(None),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -31,6 +31,10 @@ pub enum PostAuthAction {
|
|||||||
data: Ulid,
|
data: Ulid,
|
||||||
},
|
},
|
||||||
ChangePassword,
|
ChangePassword,
|
||||||
|
LinkUpstream {
|
||||||
|
#[serde_as(as = "DisplayFromStr")]
|
||||||
|
id: Ulid,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PostAuthAction {
|
impl PostAuthAction {
|
||||||
@@ -49,6 +53,7 @@ impl PostAuthAction {
|
|||||||
Self::ContinueAuthorizationGrant { data } => ContinueAuthorizationGrant(*data).go(),
|
Self::ContinueAuthorizationGrant { data } => ContinueAuthorizationGrant(*data).go(),
|
||||||
Self::ContinueCompatSsoLogin { data } => CompatLoginSsoComplete::new(*data, None).go(),
|
Self::ContinueCompatSsoLogin { data } => CompatLoginSsoComplete::new(*data, None).go(),
|
||||||
Self::ChangePassword => AccountPassword.go(),
|
Self::ChangePassword => AccountPassword.go(),
|
||||||
|
Self::LinkUpstream { id } => UpstreamOAuth2Link::new(*id).go(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -18,10 +18,10 @@
|
|||||||
|
|
||||||
use chrono::Utc;
|
use chrono::Utc;
|
||||||
use mas_data_model::{
|
use mas_data_model::{
|
||||||
AuthorizationGrant, BrowserSession, CompatSsoLogin, CompatSsoLoginState, StorageBackend, User,
|
AuthorizationGrant, BrowserSession, CompatSsoLogin, CompatSsoLoginState, StorageBackend,
|
||||||
UserEmail, UserEmailVerification,
|
UpstreamOAuthLink, UpstreamOAuthProvider, User, UserEmail, UserEmailVerification,
|
||||||
};
|
};
|
||||||
use mas_router::PostAuthAction;
|
use mas_router::{PostAuthAction, Route};
|
||||||
use serde::{ser::SerializeStruct, Deserialize, Serialize};
|
use serde::{ser::SerializeStruct, Deserialize, Serialize};
|
||||||
use ulid::Ulid;
|
use ulid::Ulid;
|
||||||
use url::Url;
|
use url::Url;
|
||||||
@@ -263,6 +263,15 @@ pub enum PostAuthContext {
|
|||||||
|
|
||||||
/// Change the account password
|
/// Change the account password
|
||||||
ChangePassword,
|
ChangePassword,
|
||||||
|
|
||||||
|
/// Link an upstream account
|
||||||
|
LinkUpstream {
|
||||||
|
/// The upstream provider
|
||||||
|
provider: Box<UpstreamOAuthProvider>,
|
||||||
|
|
||||||
|
/// The link
|
||||||
|
link: Box<UpstreamOAuthLink>,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Context used by the `login.html` template
|
/// Context used by the `login.html` template
|
||||||
@@ -779,6 +788,57 @@ impl TemplateContext for UpstreamExistingLinkContext {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Context used by the `pages/upstream_oauth2/suggest_link.html`
|
||||||
|
/// templates
|
||||||
|
#[derive(Serialize)]
|
||||||
|
pub struct UpstreamSuggestLink {
|
||||||
|
post_logout_action: PostAuthAction,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl UpstreamSuggestLink {
|
||||||
|
/// Constructs a new context with an existing linked user
|
||||||
|
#[must_use]
|
||||||
|
pub fn new(link_id: Ulid) -> Self {
|
||||||
|
let post_logout_action = PostAuthAction::LinkUpstream { id: link_id };
|
||||||
|
Self { post_logout_action }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TemplateContext for UpstreamSuggestLink {
|
||||||
|
fn sample(_now: chrono::DateTime<Utc>) -> Vec<Self>
|
||||||
|
where
|
||||||
|
Self: Sized,
|
||||||
|
{
|
||||||
|
vec![Self::new(Ulid::nil())]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Context used by the `pages/upstream_oauth2/do_register.html`
|
||||||
|
/// templates
|
||||||
|
#[derive(Serialize)]
|
||||||
|
pub struct UpstreamRegister {
|
||||||
|
login_link: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl UpstreamRegister {
|
||||||
|
/// Constructs a new context with an existing linked user
|
||||||
|
#[must_use]
|
||||||
|
pub fn new(link_id: Ulid) -> Self {
|
||||||
|
let action = PostAuthAction::LinkUpstream { id: link_id };
|
||||||
|
let login_link = mas_router::Login::and_then(action).relative_url().into();
|
||||||
|
Self { login_link }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TemplateContext for UpstreamRegister {
|
||||||
|
fn sample(_now: chrono::DateTime<Utc>) -> Vec<Self>
|
||||||
|
where
|
||||||
|
Self: Sized,
|
||||||
|
{
|
||||||
|
vec![Self::new(Ulid::nil())]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Context used by the `form_post.html` template
|
/// Context used by the `form_post.html` template
|
||||||
#[derive(Serialize)]
|
#[derive(Serialize)]
|
||||||
pub struct FormPostContext<T> {
|
pub struct FormPostContext<T> {
|
||||||
|
@@ -49,7 +49,8 @@ pub use self::{
|
|||||||
EmailVerificationContext, EmailVerificationPageContext, EmptyContext, ErrorContext,
|
EmailVerificationContext, EmailVerificationPageContext, EmptyContext, ErrorContext,
|
||||||
FormPostContext, IndexContext, LoginContext, LoginFormField, PolicyViolationContext,
|
FormPostContext, IndexContext, LoginContext, LoginFormField, PolicyViolationContext,
|
||||||
PostAuthContext, ReauthContext, ReauthFormField, RegisterContext, RegisterFormField,
|
PostAuthContext, ReauthContext, ReauthFormField, RegisterContext, RegisterFormField,
|
||||||
TemplateContext, UpstreamExistingLinkContext, WithCsrf, WithOptionalSession, WithSession,
|
TemplateContext, UpstreamExistingLinkContext, UpstreamRegister, UpstreamSuggestLink,
|
||||||
|
WithCsrf, WithOptionalSession, WithSession,
|
||||||
},
|
},
|
||||||
forms::{FieldError, FormError, FormField, FormState, ToFormState},
|
forms::{FieldError, FormError, FormField, FormState, ToFormState},
|
||||||
};
|
};
|
||||||
@@ -233,13 +234,13 @@ register_templates! {
|
|||||||
pub fn render_upstream_oauth2_link_mismatch(WithCsrf<WithSession<UpstreamExistingLinkContext>>) { "pages/upstream_oauth2/link_mismatch.html" }
|
pub fn render_upstream_oauth2_link_mismatch(WithCsrf<WithSession<UpstreamExistingLinkContext>>) { "pages/upstream_oauth2/link_mismatch.html" }
|
||||||
|
|
||||||
/// Render the upstream suggest link message
|
/// Render the upstream suggest link message
|
||||||
pub fn render_upstream_oauth2_suggest_link(WithCsrf<WithSession<EmptyContext>>) { "pages/upstream_oauth2/suggest_link.html" }
|
pub fn render_upstream_oauth2_suggest_link(WithCsrf<WithSession<UpstreamSuggestLink>>) { "pages/upstream_oauth2/suggest_link.html" }
|
||||||
|
|
||||||
/// Render the upstream login screen
|
/// Render the upstream login screen
|
||||||
pub fn render_upstream_oauth2_do_login(WithCsrf<UpstreamExistingLinkContext>) { "pages/upstream_oauth2/do_login.html" }
|
pub fn render_upstream_oauth2_do_login(WithCsrf<UpstreamExistingLinkContext>) { "pages/upstream_oauth2/do_login.html" }
|
||||||
|
|
||||||
/// Render the upstream register screen
|
/// Render the upstream register screen
|
||||||
pub fn render_upstream_oauth2_do_register(WithCsrf<EmptyContext>) { "pages/upstream_oauth2/do_register.html" }
|
pub fn render_upstream_oauth2_do_register(WithCsrf<UpstreamRegister>) { "pages/upstream_oauth2/do_register.html" }
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Templates {
|
impl Templates {
|
||||||
|
@@ -29,10 +29,10 @@ limitations under the License.
|
|||||||
{% set text_color = "text-black-800 dark:text-grey-300" %}
|
{% set text_color = "text-black-800 dark:text-grey-300" %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
<label class="flex flex-col block {{ class }}">
|
<label class="flex flex-col {{ class }}">
|
||||||
<div class="mx-2 -mb-3 -mt-2 leading-5 px-1 z-10 self-start bg-white dark:bg-black-900 border-white border-1 dark:border-2 dark:border-black-900 rounded-full text-sm {{ text_color }}">{{ label }}</div>
|
<div class="mx-2 -mb-3 -mt-2 leading-5 px-1 z-10 self-start bg-white dark:bg-black-900 border-white border-2 dark:border-black-900 rounded-full text-sm {{ text_color }}">{{ label }}</div>
|
||||||
<input name="{{ name }}"
|
<input name="{{ name }}"
|
||||||
class="z-0 px-3 py-2 bg-white dark:bg-black-900 rounded-lg {{ border_color }} border-1 dark:border-2 focus:border-accent focus:ring-0 focus:outline-0"
|
class="z-0 px-3 py-2 bg-white dark:bg-black-900 rounded-lg {{ border_color }} border-2 focus:border-accent focus:ring-0 focus:outline-0"
|
||||||
type="{{ type }}"
|
type="{{ type }}"
|
||||||
inputmode="{{ inputmode }}"
|
inputmode="{{ inputmode }}"
|
||||||
{% if autocomplete %} autocomplete="{{ autocomplete }}" {% endif %}
|
{% if autocomplete %} autocomplete="{{ autocomplete }}" {% endif %}
|
||||||
|
@@ -19,10 +19,18 @@ limitations under the License.
|
|||||||
{% block content %}
|
{% block content %}
|
||||||
<section class="flex items-center justify-center flex-1">
|
<section class="flex items-center justify-center flex-1">
|
||||||
<form method="POST" class="grid grid-cols-1 gap-6 w-96 m-2">
|
<form method="POST" class="grid grid-cols-1 gap-6 w-96 m-2">
|
||||||
<div class="text-center">
|
{% if next and next.kind == "link_upstream" %}
|
||||||
<h1 class="text-lg text-center font-medium">Sign in</h1>
|
<div class="text-center">
|
||||||
<p>Please sign in to continue:</p>
|
<h1 class="text-lg text-center font-medium">Sign in to link</h1>
|
||||||
</div>
|
<p class="text-sm">Linking your <span class="break-keep text-links">{{ next.provider.issuer }}</span> account</p>
|
||||||
|
</div>
|
||||||
|
{% else %}
|
||||||
|
<div class="text-center">
|
||||||
|
<h1 class="text-lg text-center font-medium">Sign in</h1>
|
||||||
|
<p>Please sign in to continue:</p>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
{% if form.errors is not empty %}
|
{% if form.errors is not empty %}
|
||||||
{% for error in form.errors %}
|
{% for error in form.errors %}
|
||||||
<div class="text-alert font-medium">
|
<div class="text-alert font-medium">
|
||||||
@@ -50,10 +58,13 @@ limitations under the License.
|
|||||||
{{ button::button(text="Next") }}
|
{{ button::button(text="Next") }}
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<div class="text-center mt-4">
|
|
||||||
Don't have an account yet?
|
{% if not next or next.kind != "link_upstream" %}
|
||||||
{{ button::link_text(text="Create an account", href=register_link) }}
|
<div class="text-center mt-4">
|
||||||
</div>
|
Don't have an account yet?
|
||||||
|
{{ button::link_text(text="Create an account", href=register_link) }}
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
</form>
|
</form>
|
||||||
</section>
|
</section>
|
||||||
{% endblock content %}
|
{% endblock content %}
|
||||||
|
@@ -19,10 +19,27 @@ limitations under the License.
|
|||||||
{% block content %}
|
{% block content %}
|
||||||
<section class="flex items-center justify-center flex-1">
|
<section class="flex items-center justify-center flex-1">
|
||||||
<form method="POST" class="grid grid-cols-1 gap-6 w-96">
|
<form method="POST" class="grid grid-cols-1 gap-6 w-96">
|
||||||
<h1 class="rounded-lg bg-grey-25 dark:bg-grey-450 p-2 flex flex-col font-medium text-lg text-center">
|
<h1 class="rounded-lg bg-grey-25 dark:bg-grey-450 p-2 font-medium text-lg text-center">
|
||||||
Continue login
|
Continue login
|
||||||
</h1>
|
</h1>
|
||||||
|
|
||||||
|
<div class="rounded-lg bg-grey-25 dark:bg-grey-450 p-4 flex flex-col">
|
||||||
|
<div>
|
||||||
|
Username: <span class="font-medium">{{ linked_user.username }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="text-sm">
|
||||||
|
Identifier: <span class="font-mono font-medium">{{ linked_user.sub }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="text-sm">
|
||||||
|
Primary email:
|
||||||
|
{% if linked_user.primary_email %}
|
||||||
|
<span class="font-medium">{{ linked_user.primary_email.email }}</span>
|
||||||
|
{% else %}
|
||||||
|
<span class="italic">none</span>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<input type="hidden" name="csrf" value="{{ csrf_token }}" />
|
<input type="hidden" name="csrf" value="{{ csrf_token }}" />
|
||||||
<input type="hidden" name="action" value="login" />
|
<input type="hidden" name="action" value="login" />
|
||||||
|
|
||||||
|
@@ -18,16 +18,24 @@ limitations under the License.
|
|||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<section class="flex items-center justify-center flex-1">
|
<section class="flex items-center justify-center flex-1">
|
||||||
<form method="POST" class="grid grid-cols-1 gap-6 w-96">
|
<div class="grid grid-cols-1 gap-6 w-96">
|
||||||
<h1 class="rounded-lg bg-grey-25 dark:bg-grey-450 p-2 flex flex-col font-medium text-lg text-center">
|
<form method="POST" class="grid grid-cols-1 gap-6">
|
||||||
Choose your username
|
<h1 class="rounded-lg bg-grey-25 dark:bg-grey-450 p-2 text-center font-medium text-lg">
|
||||||
</h1>
|
Choose your username
|
||||||
|
</h1>
|
||||||
|
|
||||||
<input type="hidden" name="csrf" value="{{ csrf_token }}" />
|
<input type="hidden" name="csrf" value="{{ csrf_token }}" />
|
||||||
<input type="hidden" name="action" value="register" />
|
<input type="hidden" name="action" value="register" />
|
||||||
{{ field::input(label="Username", name="username", autocomplete="username", autocorrect="off", autocapitalize="none") }}
|
{{ field::input(label="Username", name="username", autocomplete="username", autocorrect="off", autocapitalize="none") }}
|
||||||
|
|
||||||
{{ button::button(text="Continue") }}
|
{{ button::button(text="Create a new account") }}
|
||||||
</form>
|
</form>
|
||||||
|
<div class="flex items-center">
|
||||||
|
<hr class="flex-1" />
|
||||||
|
<div class="mx-2">Or</div>
|
||||||
|
<hr class="flex-1" />
|
||||||
|
</div>
|
||||||
|
{{ button::link_outline(text="Link to an existing account", href=login_link) }}
|
||||||
|
</div>
|
||||||
</section>
|
</section>
|
||||||
{% endblock content %}
|
{% endblock content %}
|
||||||
|
@@ -24,7 +24,7 @@ limitations under the License.
|
|||||||
This upstream account is already linked to another account.
|
This upstream account is already linked to another account.
|
||||||
</h1>
|
</h1>
|
||||||
|
|
||||||
{{ logout::button(text="Logout", class=button::plain_class(), csrf_token=csrf_token) }}
|
<div>{{ logout::button(text="Logout", class=button::outline_class(), csrf_token=csrf_token) }}</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
{% endblock content %}
|
{% endblock content %}
|
||||||
|
@@ -31,7 +31,7 @@ limitations under the License.
|
|||||||
{{ button::button(text="Link", class="flex-1") }}
|
{{ button::button(text="Link", class="flex-1") }}
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
<div>Or {{ logout::button(text="Logout", class=button::outline_class(), csrf_token=csrf_token) }}</div>
|
<div>Or {{ logout::button(text="Logout", class=button::outline_class(), csrf_token=csrf_token, post_logout_action=post_logout_action) }}</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
{% endblock content %}
|
{% endblock content %}
|
||||||
|
Reference in New Issue
Block a user