Constellation, Spacedust, Slingshot, UFOs: atproto crates and services for microcosm

post for user info follow-up instead of get

saw some spurious double-requests that led to taking the expiring task map key early

which is maybe an indication that the fragility of only having once chance to retrieve it by key is kind of a problem, but hey we can paper over that by POSTing for now for a slightly higher chance that nothing in the stack will try to re-request somehow.

Changed files
+43 -43
who-am-i
+9 -4
who-am-i/src/expiring_task_map.rs
···
.run_until_cancelled(sleep(expiration))
.await
.is_some()
+
// the (sleep) task completed first
{
-
// is Some if the (sleep) task completed first
map.remove(&k);
cancel.cancel();
metrics::counter!("whoami_task_map_completions", "result" => "expired")
···
}
pub fn take(&self, key: &str) -> Option<JoinHandle<T>> {
-
metrics::counter!("whoami_task_map_completions", "result" => "retrieved").increment(1);
-
// when the _guard drops, the token gets cancelled for us
-
self.0.map.remove(key).map(|(_, (_guard, handle))| handle)
+
if let Some((_key, (_guard, handle))) = self.0.map.remove(key) {
+
// when the _guard drops, it cancels the token for us
+
metrics::counter!("whoami_task_map_completions", "result" => "retrieved").increment(1);
+
Some(handle)
+
} else {
+
metrics::counter!("whoami_task_map_gones").increment(1);
+
None
+
}
}
}
+7 -16
who-am-i/src/server.rs
···
use atrium_api::types::string::Did;
use axum::{
Router,
-
extract::{FromRef, Query, State},
+
extract::{FromRef, Json as ExtractJson, Query, State},
http::{
StatusCode,
-
header::{CONTENT_SECURITY_POLICY, CONTENT_TYPE, HeaderMap, REFERER, X_FRAME_OPTIONS},
+
header::{CONTENT_SECURITY_POLICY, CONTENT_TYPE, HeaderMap, REFERER},
},
response::{IntoResponse, Json, Redirect, Response},
routing::{get, post},
···
.route("/favicon.ico", get(favicon)) // todo MIME
.route("/style.css", get(css))
.route("/prompt", get(prompt))
-
.route("/user-info", get(user_info))
+
.route("/user-info", post(user_info))
.route("/auth", get(start_oauth))
.route("/authorized", get(complete_oauth))
.route("/disconnect", post(disconnect))
···
} else {
json!({})
};
-
let frame_headers = [
-
(X_FRAME_OPTIONS, "deny"),
-
(CONTENT_SECURITY_POLICY, "frame-ancestors 'none'"),
-
];
+
let frame_headers = [(CONTENT_SECURITY_POLICY, "frame-ancestors 'none'")];
(frame_headers, jar, RenderHtml("hello", engine, info)).into_response()
}
···
return err("Referer origin is opaque", true);
}
-
let frame_headers = [
-
(X_FRAME_OPTIONS, format!("allow-from {parent_origin}")),
-
(
-
CONTENT_SECURITY_POLICY,
-
format!("frame-ancestors {parent_origin}"),
-
),
-
];
+
let csp = format!("frame-ancestors {parent_origin}");
+
let frame_headers = [(CONTENT_SECURITY_POLICY, &csp)];
if let Some(did) = jar.get(DID_COOKIE_KEY) {
let Ok(did) = Did::new(did.value_trimmed().to_string()) else {
···
}
#[derive(Debug, Deserialize)]
-
#[serde(rename_all = "kebab-case")]
struct UserInfoParams {
fetch_key: String,
}
···
State(AppState {
resolve_handles, ..
}): State<AppState>,
-
Query(params): Query<UserInfoParams>,
+
ExtractJson(params): ExtractJson<UserInfoParams>,
) -> impl IntoResponse {
let err = |status, reason: &str| {
metrics::counter!("whoami_user_info", "found" => "false", "reason" => reason.to_string())
+5 -4
who-am-i/templates/hello.hbs
···
({{{json did}}}) && (async () => {
const handle = await lookUp({{{json fetch_key}}});
-
console.log('got handle', handle);
loaderEl.classList.add('hidden');
handleViewEl.textContent = `@${handle}`;
···
})();
async function lookUp(fetch_key) {
-
const user_info = new URL('/user-info', window.location);
-
user_info.searchParams.set('fetch-key', fetch_key);
let info;
try {
-
const resp = await fetch(user_info);
+
const resp = await fetch('/user-info', {
+
method: 'POST',
+
headers: {'Content-Type': 'application/json'},
+
body: JSON.stringify({ fetch_key }),
+
});
if (!resp.ok) throw resp;
info = await resp.json();
} catch (e) {
+22 -19
who-am-i/templates/prompt.hbs
···
// already-known user
({{{json did}}}) && (async () => {
-
const handle = await lookUp({{{json fetch_key}}});
-
console.log('got handle', handle);
-
loaderEl.classList.add('hidden');
handleViewEl.textContent = `@${handle}`;
allowEl.addEventListener('click', () => shareAllow(handle, {{{json token}}}));
···
// so if you have two flows going, it grants for both (or the first responder?) if you grant for either.
// (letting this slide while parent pages are allowlisted to microcosm only)
-
const fail = (e, msg) => {
-
loaderEl.classList.add('hidden');
-
formEl.classList.remove('hidden');
-
handleInputEl.focus();
-
handleInputEl.select();
-
err(e, msg);
-
}
+
if (e.key !== 'who-am-i') return;
+
if (e.newValue === null) return;
-
const details = localStorage.getItem("who-am-i");
+
const details = e.newValue;
if (!details) {
-
console.error("hmm, heard from localstorage but did not get DID");
-
return;
+
console.error("hmm, heard from localstorage but did not get DID", details, e);
+
err('sorry, something went wrong getting your details');
}
-
localStorage.removeItem("who-am-i");
+
localStorage.removeItem(e.key);
let parsed;
try {
···
err(e, "something went wrong getting the details back");
}
+
const fail = (e, msg) => {
+
loaderEl.classList.add('hidden');
+
formEl.classList.remove('hidden');
+
handleInputEl.focus();
+
handleInputEl.select();
+
err(e, msg);
+
}
+
if (parsed.result === "fail") {
fail(`uh oh: ${parsed.reason}`);
}
···
const handle = await lookUp(parsed.fetch_key);
-
shareAllow(handle, token);
+
shareAllow(handle, parsed.token);
});
async function lookUp(fetch_key) {
-
const user_info = new URL('/user-info', window.location);
-
user_info.searchParams.set('fetch-key', fetch_key);
let info;
try {
-
const resp = await fetch(user_info);
+
const resp = await fetch('/user-info', {
+
method: 'POST',
+
headers: { 'Content-Type': 'application/json' },
+
body: JSON.stringify({ fetch_key }),
+
});
if (!resp.ok) throw resp;
info = await resp.json();
} catch (e) {
-
err(e, 'failed to resolve handle from DID')
+
err(e, `failed to resolve handle from DID with ${fetch_key}`);
}
return info.handle;
}
···
{ action: "allow", handle, token },
{{{json parent_origin}}},
);
+
promptEl.textContent = '✔️ shared';
}
const shareDeny = reason => {