authorgravatar for git@paperclover.netclover caruso <git@paperclover.net> 2026-10-05 11:13:07-07:00
committergravatar for git@paperclover.netclover caruso <git@paperclover.net> 2026-10-05 15:36:02-07:00
log3873b6a5bbe78baba32102186b613d984331144c
treec05d4c7ab3d2b29fdc0bb816b0db06cc695edf08
parent036a0e63f8c0eaa73960243300cfd238c1553210
signature Signed by SSH key SHA256:52mNGHRsVFBDED9IAX5pe+LRWUefqTbxEReunq21QvU

Hide guest providers on the password step and add back navigation

Show the selected account as a large blue heading. Capture only Astheno numeric error identifiers when token exchange is rejected. Assisted-by: gpt-6

3 files changed, 33 insertions(+), 12 deletions(-)

dashboard/src/guest.rs+27-7
......@@ -96,13 +96,13 @@ fn registration(auth: &auth::Store, provider: &str) -> Result<(String, String)>
9696}
9797
9898async fn response_bytes(mut response: reqwest::Response) -> Result<Vec<u8>> {
99 if !response.status().is_success() || response.content_length().is_some_and(|n| n > 65536) {
100 eprintln!(
101 "guest provider response: {} {} status {}",
102 response.url().host_str().unwrap_or_default(),
103 response.url().path(),
104 response.status()
105 );
99 let status = response.status();
100 let endpoint = format!(
101 "{}{}",
102 response.url().host_str().unwrap_or_default(),
103 response.url().path()
104 );
105 if response.content_length().is_some_and(|n| n > 65536) {
106106 return Err(Error::new(
107107 502,
108108 "The provider couldn't complete sign-in. Return to Shale and try again.",
......@@ -118,6 +118,26 @@ async fn response_bytes(mut response: reqwest::Response) -> Result<Vec<u8>> {
118118 }
119119 data.extend_from_slice(&chunk);
120120 }
121 if !status.is_success() {
122 let error: Value = serde_json::from_slice(&data).unwrap_or(Value::Null);
123 let code = string(&error["error_description"])
124 .split(':')
125 .next()
126 .unwrap_or_default();
127 let code = if code.starts_with('E')
128 && code.len() <= 8
129 && code[1..].bytes().all(|c| c.is_ascii_digit())
130 {
131 code
132 } else {
133 "unknown"
134 };
135 eprintln!("guest provider response: {endpoint} status {status} code {code}");
136 return Err(Error::new(
137 502,
138 "The provider couldn't complete sign-in. Return to Shale and try again.",
139 ));
140 }
121141 Ok(data)
122142}
123143
dashboard/web/pages/SignIn.css+3-3
......@@ -6,9 +6,9 @@ body { font: 16px "Name Sans", sans-serif; }
66.pf-v5-c-login__main [aria-disabled="true"], .pf-v5-c-login__main :disabled { opacity: .6; cursor: wait; }
77.setup-intro { margin-bottom: 1rem; text-align: center; }
88.guest-providers { margin-bottom: 1.5rem; }
9.sign-in-account { display: flex; align-items: center; justify-content: space-between; gap: 1rem; margin-bottom: 1rem; }
10.sign-in-account span { overflow-wrap: anywhere; }
11.sign-in-account button { background: none; border: 0; padding: 0; color: var(--text); text-decoration: underline; cursor: pointer; }
9.sign-in-account { margin-bottom: 1.25rem; }
10.sign-in-account h2 { margin: 0; color: var(--primary); font-size: 28px; line-height: 1.2; font-weight: 500; text-align: center; text-transform: none; overflow-wrap: anywhere; }
11.sign-in-account button { display: inline-flex; align-items: center; gap: .375rem; margin-bottom: .75rem; background: none; border: 0; padding: 0; color: var(--primary); cursor: pointer; }
1212.pf-v5-c-login__main .guest-providers a {
1313 display: grid;
1414 grid-template-columns: 20px minmax(0, 1fr) 20px;
dashboard/web/pages/SignIn.tsx+3-2
......@@ -1,5 +1,6 @@
11import { createEffect, createResource, createSignal, For, onCleanup, onMount, Show } from "solid-js";
22import { authReason, authRequest } from "../auth.ts";
3import ArrowLeft from "lucide-solid/icons/arrow-left";
34import Stone from "lucide-solid/icons/stone";
45import githubIcon from "../sso/github.svg";
56import theme from "../sso/css/styles.css?inline";
......@@ -86,7 +87,7 @@ export function SignIn() {
8687 <Show when={status.error || error()}><div class="alert-error pf-v5-c-alert" role="alert"><span class="pf-v5-c-alert__title">{status.error ? authReason(status.error) : error()}</span></div></Show>
8788 <Show when={notice()}><div class="alert-info pf-v5-c-alert" role="status"><span class="pf-v5-c-alert__title">{notice()}</span></div></Show>
8889 <Show when={!status.error} fallback={<button class="pf-v5-c-button pf-m-primary pf-m-block" onClick={() => refetch()}>try again</button>}>
89 <Show when={!setup && status()?.providers?.length}>
90 <Show when={!setup && !passwordStep() && status()?.providers?.length}>
9091 <div id="kc-social-providers" class="guest-providers">
9192 <For each={status()?.providers}>{(provider) => <a rel="external" data-provider={provider.id} class="pf-v5-c-button pf-m-primary pf-m-block" href={`/auth/guest/start/${provider.id}?next=${encodeURIComponent(params.get("next") ?? "")}`}>{provider.id === "astheno" ? <Stone size={18} aria-hidden="true" /> : <img src={githubIcon} width="18" height="18" alt="" />}<span>continue with {provider.name}</span></a>}</For>
9293 </div>
......@@ -94,7 +95,7 @@ export function SignIn() {
9495 <div id="kc-form"><div id="kc-form-wrapper">
9596 <form id="kc-form-login" onSubmit={(event) => { event.preventDefault(); void complete(); }}>
9697 <Show when={setup} fallback={
97 <Show when={!passwordStep()} fallback={<div class="sign-in-account"><span>{username()}</span><button type="button" onClick={() => { setPassword(""); setVisible(false); setError(""); setNotice(""); setPasswordStep(false); }}>change</button><input name="username" type="hidden" autocomplete="username" value={username()} /></div>}>
98 <Show when={!passwordStep()} fallback={<div class="sign-in-account"><button type="button" onClick={() => { setPassword(""); setVisible(false); setError(""); setNotice(""); setPasswordStep(false); }}><ArrowLeft size={18} aria-hidden="true" />back</button><h2>{username()}</h2><input name="username" type="hidden" autocomplete="username" value={username()} /></div>}>
9899 <div class="pf-v5-c-form__group">
99100 <label for="username" class="pf-v5-c-form__label">username or email</label>
100101 <input id="username" class="pf-v5-c-form-control" name="username" type="text" required autocomplete="username webauthn" value={username()} onInput={(event) => setUsername(event.currentTarget.value)} autofocus ref={(input) => queueMicrotask(() => input.focus())} />