~ knotless Search/
↑↓ move↵ open⇧↵ all results

chadtmiller.com/knotless

git that lives in your Atmosphere account

knotless.dev
1 collaborator

knotless 0.13.0 Latest 2 packages, 1 image

1
10

The page after a CLI login looks like knotless and says when it failed

Merged chadtmiller.com asked to merge into main from chadtmiller.com/knotless:web/login-page
.changeset/login-page.mdnew+7−0
@@ -0,0 +1,7 @@
---
default: patch
---
# The page after logging in is easier to read
The page the browser shows once you have approved a login now has a heading and the look of knotless's own pages, in light and dark, and says so when the server refused the login rather than that you are logged in.
crates/knotless/src/oauth.rs+45−9
@@ -680,9 +680,48 @@ pub async fn start(ctx: &Ctx, identifier: &str) -> Result<Pending> {
})
}
/// What the browser shows once it is back.
const DONE: &str = "<!doctype html><meta charset=utf-8><title>knotless</title>\
<p>Back to knotless: you can close this tab and return to your terminal.";
/// What the browser shows once it is back: a small page of its own, with
/// its styles inline and the palette of knotless's pages, as the browser
/// is at our own loopback address and asks nothing of anyone else.
fn page(heading: &str, line: &str) -> String {
format!(
"<!doctype html><html lang=en><meta charset=utf-8>\
<meta name=viewport content=\"width=device-width,initial-scale=1\">\
<title>knotless</title>\
<style>\
:root{{color-scheme:light dark;--bg:#fff;--panel:#fafafa;--line:#e8e8e8;--fg:#171717;--mute:#525252}}\
@media(prefers-color-scheme:dark){{:root{{--bg:#111;--panel:#161616;--line:#262626;--fg:#ededed;--mute:#a1a1a1}}}}\
body{{margin:0;min-height:100vh;display:grid;place-items:center;padding:1rem;box-sizing:border-box;\
background:var(--bg);color:var(--fg);font:1rem/1.5 system-ui,-apple-system,'Segoe UI',Roboto,'Helvetica Neue',sans-serif}}\
main{{max-width:24rem;padding:2rem;text-align:center;background:var(--panel);border:1px solid var(--line);border-radius:6px}}\
b{{display:block;margin-bottom:1.5rem;font:600 1rem ui-monospace,SFMono-Regular,Menlo,monospace}}\
h1{{margin:0 0 .5rem;font-size:1.25rem;font-weight:600}}\
p{{margin:0;color:var(--mute)}}\
</style>\
<main><b>~ knotless</b><h1>{heading}</h1><p>{line}</p></main>"
)
}
/// The reply to a browser that came back with `params`: the login's, or
/// the server's word that it was refused.
fn reply(params: &HashMap<String, String>) -> String {
let body = if params.contains_key("error") {
page(
"The login did not go through",
"Go back to your terminal to see why, then try again.",
)
} else {
page(
"You are logged in",
"You can close this tab and go back to your terminal.",
)
};
format!(
"HTTP/1.1 200 OK\r\ncontent-type: text/html; charset=utf-8\r\n\
content-length: {}\r\nconnection: close\r\n\r\n{body}",
body.len()
)
}
impl Pending {
/// Waits for the browser, trades the code for tokens, and stores the
@@ -718,13 +757,10 @@ impl Pending {
.await?;
continue;
};
let reply = format!(
"HTTP/1.1 200 OK\r\ncontent-type: text/html; charset=utf-8\r\n\
content-length: {}\r\nconnection: close\r\n\r\n{DONE}",
DONE.len()
);
let params: HashMap<String, String> = url.query_pairs().into_owned().collect();
let reply = reply(&params);
stream.write_all(reply.as_bytes()).await?;
return Ok(url.query_pairs().into_owned().collect());
return Ok(params);
}
}
}
crates/knotless/tests/oauth.rs+26−1
@@ -201,7 +201,32 @@ fn the_listener_takes_only_its_own_callback() {
.send()
.await
.unwrap();
assert!(page.text().await.unwrap().contains("knotless"));
let page = page.text().await.unwrap();
assert!(page.contains("You are logged in"), "{page}");
assert!(page.contains("prefers-color-scheme"), "{page}");
});
let res = pending.finish(&ctx).await;
poke.await.unwrap();
res
})
.unwrap_err();
assert!(err.to_string().contains("did not start"), "{err}");
}
#[test]
fn the_page_says_when_the_server_refused_the_login() {
let s = oauth_sandbox(account(), "");
let ctx = Ctx::new(s.env());
let err = rt()
.block_on(async {
let pending = oauth::start(&ctx, &s.account.handle).await?;
let back = pending.redirect_uri.clone();
let poke = tokio::spawn(async move {
let page = reqwest::get(format!("{back}?error=access_denied"))
.await
.unwrap();
let page = page.text().await.unwrap();
assert!(page.contains("did not go through"), "{page}");
});
let res = pending.finish(&ctx).await;
poke.await.unwrap();