UI/Feature polish (#3191)

Post release some small user issues arose

* Optimise the autofocus for logins with passkeys to limit clicks
* Sort login mechs by strength
* Fix cookies to persist between browser restarts
This commit is contained in:
Firstyear 2024-11-10 14:02:27 +10:00 committed by GitHub
parent 1218abd8c6
commit dfbcfa865f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 22 additions and 9 deletions

View file

@ -85,10 +85,10 @@ impl fmt::Debug for AuthCredential {
pub enum AuthMech { pub enum AuthMech {
Anonymous, Anonymous,
Password, Password,
PasswordBackupCode,
// Now represents TOTP. // Now represents TOTP.
#[serde(rename = "passwordmfa")] #[serde(rename = "passwordmfa")]
PasswordTotp, PasswordTotp,
PasswordBackupCode,
PasswordSecurityKey, PasswordSecurityKey,
Passkey, Passkey,
} }

View file

@ -37,6 +37,8 @@ pub fn make_unsigned<'a>(
// then webauthn won't work anyway! // then webauthn won't work anyway!
token_cookie.set_domain(state.domain.clone()); token_cookie.set_domain(state.domain.clone());
token_cookie.set_path(path); token_cookie.set_path(path);
// These last forever.
token_cookie.make_permanent();
token_cookie token_cookie
} }
@ -69,6 +71,8 @@ pub fn make_signed<'a, T: Serialize>(
token_cookie.set_http_only(true); token_cookie.set_http_only(true);
token_cookie.set_path(path); token_cookie.set_path(path);
token_cookie.set_domain(state.domain.clone()); token_cookie.set_domain(state.domain.clone());
// These last forever, we have our own internal expiration handling.
token_cookie.make_permanent();
Some(token_cookie) Some(token_cookie)
} }

View file

@ -99,6 +99,7 @@ struct LoginView {
pub struct Mech<'a> { pub struct Mech<'a> {
name: AuthMech, name: AuthMech,
value: &'a str, value: &'a str,
autofocus: bool,
} }
#[derive(Template)] #[derive(Template)]
@ -754,7 +755,7 @@ async fn view_login_step(
safety -= 1; safety -= 1;
match auth_state { match auth_state {
AuthState::Choose(allowed) => { AuthState::Choose(mut allowed) => {
debug!("🧩 -> AuthState::Choose"); debug!("🧩 -> AuthState::Choose");
jar = add_session_cookie(&state, jar, &session_context)?; jar = add_session_cookie(&state, jar, &session_context)?;
@ -793,13 +794,22 @@ async fn view_login_step(
// Render the list of options. // Render the list of options.
_ => { _ => {
let mechs = allowed allowed.sort_unstable();
// Put strongest first.
allowed.reverse();
let mechs: Vec<_> = allowed
.into_iter() .into_iter()
.map(|m| Mech { .enumerate()
.map(|(i, m)| Mech {
value: m.to_value(), value: m.to_value(),
name: m, name: m,
// Auto focus the first item, it's the strongest
// mechanism and the one we should optimise for.
autofocus: i == 0,
}) })
.collect(); .collect();
LoginMechView { display_ctx, mechs }.into_response() LoginMechView { display_ctx, mechs }.into_response()
} }
}; };

View file

@ -11,6 +11,7 @@
<form id="login" action="/ui/login/mech_choose" method="post"> <form id="login" action="/ui/login/mech_choose" method="post">
<input type="hidden" id="mech" name="mech" value="(( mech.value ))" /> <input type="hidden" id="mech" name="mech" value="(( mech.value ))" />
<button <button
(% if mech.autofocus %)autofocus(% endif %)
type="submit" type="submit"
class="btn btn-dark" class="btn btn-dark"
>(( mech.name ))</button> >(( mech.name ))</button>

View file

@ -16,16 +16,14 @@
(% if passkey %) (% if passkey %)
<form id="cred-form" action="/ui/login/passkey" method="POST"> <form id="cred-form" action="/ui/login/passkey" method="POST">
<input hidden="hidden" name="cred" id="cred"> <input hidden="hidden" name="cred" id="cred">
<button hx-disable type="button" autofocus class="btn btn-dark"
<button hx-disable type="button" class="btn btn-dark"
id="start-passkey-button">Use Passkey</button> id="start-passkey-button">Use Passkey</button>
</form> </form>
(% else %) (% else %)
<form id="cred-form" action="/ui/login/seckey" method="POST"> <form id="cred-form" action="/ui/login/seckey" method="POST">
<input hidden="hidden" name="cred" id="cred"> <input hidden="hidden" name="cred" id="cred">
<button hx-disable type="button" autofocus class="btn btn-dark"
<button type="button" class="btn btn-dark" id="start-seckey-button" id="start-seckey-button">Use Security Key</button>
>Use Security Key</button>
</form> </form>
(% endif %) (% endif %)
</div> </div>