diff --git a/.beans/ATFS-5gqb--devatfsauthwriter-a-permission-set-for-browser-cli.md b/.beans/ATFS-5gqb--devatfsauthwriter-a-permission-set-for-browser-cli.md index f833473..477a8d0 100644 --- a/.beans/ATFS-5gqb--devatfsauthwriter-a-permission-set-for-browser-cli.md +++ b/.beans/ATFS-5gqb--devatfsauthwriter-a-permission-set-for-browser-cli.md @@ -63,7 +63,7 @@ version. Ship both, watch, then drop it. ## Todos - [ ] `lexicons/dev/atfs/authWriter.json` -- [ ] `site/client-metadata.json` scope, with the transitional fallback +- [ ] `site/oauth-client-metadata.json` scope, with the transitional fallback - [ ] Verify a token actually mints for `dev.atfs.repo.pinFile` against a real PDS - [ ] Check the consent screen reads sensibly to a non-expert - [ ] Verify a PDS that rejects `include:` still signs in via the fallback diff --git a/.beans/ATFS-nx4w--one-oauth-client-document-for-the-whole-site-equal.md b/.beans/ATFS-nx4w--one-oauth-client-document-for-the-whole-site-equal.md new file mode 100644 index 0000000..9b60297 --- /dev/null +++ b/.beans/ATFS-nx4w--one-oauth-client-document-for-the-whole-site-equal.md @@ -0,0 +1,41 @@ +--- +# ATFS-nx4w +title: One OAuth client document for the whole site; equal logo heights on the home page +status: in-progress +type: task +priority: normal +created_at: 2026-08-20T16:59:48Z +updated_at: 2026-08-20T17:00:29Z +parent: ATFS-qchs +--- + +Two changes to the atfs.dev site, both left over from feat/site-logos-downloads-move review: + +1. Collapse the two separate OAuth client-metadata documents (site/client-metadata.json for the home page, web/setup/public/client-metadata.json for the setup flow) into ONE, at the site root, renamed site/oauth-client-metadata.json. Both sign-ins are the same thing — the setup flow uses the same auth as the home page. The renamed filename also happens to be what the pinned @atproto/oauth-types actually requires: site/login/login.js's bundled validator rejects any client_id whose pathname isn't exactly /oauth-client-metadata.json (confirmed by reading the built bundle), so the old site/client-metadata.json name would never have validated in the client library's own client-id-metadata-document check. + +2. Make the two inline logo marks on the home page (site/index.html) read at the same optical height. Both viewBoxes are square and both already render at the same 23.5x23.5px box, so 1.4em height/width alone changes nothing — the Docker whale artwork only fills ~49.7% of its own viewBox vertically (wide, short whale, lots of vertical whitespace) while the Pi fills 100%. Tighten each viewBox to its measured artwork bounding box, then let height drive sizing with width auto. + +Do NOT rebase, push, or open a PR — adding two commits to the existing feat/site-logos-downloads-move branch, which already has an open PR. + + + +## Todo + +- [x] Rename site/client-metadata.json -> site/oauth-client-metadata.json (git mv), update client_id/client_uri/redirect_uris (two URIs: / and /downloads/) +- [x] Delete web/setup/public/client-metadata.json +- [x] web/setup/src/lib/session.js resolves client id against location.origin, not location.href +- [x] web/home/src/lib/session.js points at the new filename; comment rewritten (the /downloads/ distinction is gone) +- [x] hack/deploy-site.sh's required-files check and comment updated +- [x] Sweep the repo for any other client-metadata.json reference — CLAUDE.md and README.md never named the file directly (nothing to change there); updated the one open, forward-looking bean reference (ATFS-5gqb's todo item); left archived beans (ATFS-t7d4, ATFS-vw63, ATFS-nc7y) and ATFS-ji1d's completed Summary of Changes alone, since those describe history as it actually happened +- [ ] Tighten both logo viewBoxes in site/index.html to their artwork bounding boxes +- [ ] .run .mark: height: 1.4em; width: auto; flex: none, with a comment on why +- [ ] make web && make check pass; cd web/setup && vp run check clean +- [ ] git status clean of build output after committing + + + +## Notes + +- **Identity change, deliberately accepted.** `client_id` changing from `https://atfs.dev/client-metadata.json` to `https://atfs.dev/oauth-client-metadata.json` is a new OAuth client identity as far as any PDS/authorization-server is concerned — any consent grants already made against the old client_id (there won't be many; the site is new) don't carry over, and whoever signed in before will be asked to re-authorise. Accepted. +- **Why two `redirect_uris` is correct, not accidental.** Verified by reading the pinned `@atproto/oauth-client-browser`'s bundled output (site/login/login.js, built from web/home): `BrowserOAuthClient`'s redirect selection matches the declared redirect_uri whose origin AND pathname equal the current page, so the home page (served at `/`) resolves to the `https://atfs.dev/` entry and the setup flow (served at `/downloads/`) resolves to the `https://atfs.dev/downloads/` entry, from the same client document. Order doesn't matter to that match, but neither entry is removable while both pages sign in. +- **The bundled OAuth library requires this exact filename.** site/login/login.js's bundled `@atproto/oauth-types`'s client-id-metadata-document validator hard-rejects any `client_id` URL whose pathname isn't exactly `/oauth-client-metadata.json` (own error message: "ClientID must be \"/oauth-client-metadata.json\""). The old `site/client-metadata.json` name would never have passed that check — this rename isn't just a naming cleanup, it fixes an OAuth client that couldn't have validated in the browser at all. diff --git a/hack/deploy-site.sh b/hack/deploy-site.sh index c0a1020..94934c5 100755 --- a/hack/deploy-site.sh +++ b/hack/deploy-site.sh @@ -12,7 +12,9 @@ # current release's raw downloads, baked in on top by # hack/bake-site-downloads.sh (site/downloads/images.json, # site/downloads/raw/) — nothing built is kept in git. -# site/client-metadata.json, unlike those, is committed as-is (ATFS-t7d4). +# site/oauth-client-metadata.json, unlike those, is committed as-is +# (ATFS-t7d4) — one OAuth client document for the whole site, used by both +# the home page's Log in button and the setup flow. # site/setup/index.html is also committed as-is: a redirect stub at the # setup flow's old URL, so a link or bookmark still made there lands on # /downloads/. @@ -89,7 +91,7 @@ for required in \ "$SITE_PATH/index.html" \ "$SITE_PATH/.well-known/did.json" \ "$SITE_PATH/.well-known/atproto-did" \ - "$SITE_PATH/client-metadata.json" \ + "$SITE_PATH/oauth-client-metadata.json" \ "$SITE_PATH/downloads/index.html" \ "$SITE_PATH/downloads/images.json" \ "$SITE_PATH/downloads/raw/index.html" \ diff --git a/site/client-metadata.json b/site/oauth-client-metadata.json similarity index 68% rename from site/client-metadata.json rename to site/oauth-client-metadata.json index 5085193..f938884 100644 --- a/site/client-metadata.json +++ b/site/oauth-client-metadata.json @@ -1,8 +1,8 @@ { - "client_id": "https://atfs.dev/client-metadata.json", + "client_id": "https://atfs.dev/oauth-client-metadata.json", "client_name": "atfs", "client_uri": "https://atfs.dev/", - "redirect_uris": ["https://atfs.dev/"], + "redirect_uris": ["https://atfs.dev/", "https://atfs.dev/downloads/"], "scope": "atproto transition:generic", "grant_types": ["authorization_code", "refresh_token"], "response_types": ["code"], diff --git a/web/home/src/lib/session.js b/web/home/src/lib/session.js index cdf091a..4196de4 100644 --- a/web/home/src/lib/session.js +++ b/web/home/src/lib/session.js @@ -1,6 +1,7 @@ // Signing in with atproto, mirroring web/setup/src/lib/session.js — the -// same mechanism, pointed at the site root's own client-metadata.json -// rather than /downloads/'s, since the Log in button lives on the home page. +// same mechanism, since both surfaces share one OAuth client +// (site/oauth-client-metadata.json): this page picks up its own +// /-rooted redirect_uri, the setup flow its /downloads/ one. import { BrowserOAuthClient } from "@atproto/oauth-client-browser"; @@ -15,7 +16,7 @@ export const HANDLE_RESOLVER = "https://public.api.bsky.app"; * On a loopback origin the library synthesises development client metadata, * which is what makes sign-in testable without a public URL; anywhere else * the metadata has to be a real document, which is what - * site/client-metadata.json is for. + * site/oauth-client-metadata.json is for. */ export async function startSession() { const isLoopback = LOOPBACK.includes(location.hostname); @@ -24,7 +25,7 @@ export async function startSession() { const client = isLoopback ? new BrowserOAuthClient({ clientMetadata: undefined, handleResolver }) : await BrowserOAuthClient.load({ - clientId: new URL("./client-metadata.json", location.href).href, + clientId: new URL("/oauth-client-metadata.json", location.origin).href, handleResolver, }); diff --git a/web/setup/public/client-metadata.json b/web/setup/public/client-metadata.json deleted file mode 100644 index ed8bf60..0000000 --- a/web/setup/public/client-metadata.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "client_id": "https://atfs.dev/downloads/client-metadata.json", - "client_name": "atfs setup", - "client_uri": "https://atfs.dev/downloads/", - "redirect_uris": ["https://atfs.dev/downloads/"], - "scope": "atproto transition:generic", - "grant_types": ["authorization_code", "refresh_token"], - "response_types": ["code"], - "application_type": "web", - "dpop_bound_access_tokens": true, - "token_endpoint_auth_method": "none" -} diff --git a/web/setup/src/lib/session.js b/web/setup/src/lib/session.js index 7a1afdb..3c5aa91 100644 --- a/web/setup/src/lib/session.js +++ b/web/setup/src/lib/session.js @@ -18,8 +18,13 @@ const LOOPBACK = ["localhost", "127.0.0.1", "[::1]"]; * including the one a sign-in redirect has just come back with. * * On a loopback origin the library synthesises development client metadata; - * anywhere else the metadata has to be a real URL, which is what - * public/client-metadata.json is for. + * anywhere else the metadata has to be a real URL. There is one OAuth + * client for the whole site (site/oauth-client-metadata.json — both this + * flow and the home page's Log in button are the same sign-in), so it's + * resolved against the ORIGIN rather than this page's own path — this app + * is served from /downloads/, not the site root, and the document itself + * lists /downloads/ as one of two redirect_uris precisely so both pages' + * callback lands where each was launched from. */ export async function startSession() { const isLoopback = LOOPBACK.includes(location.hostname); @@ -28,7 +33,7 @@ export async function startSession() { const client = isLoopback ? new BrowserOAuthClient({ clientMetadata: undefined, handleResolver }) : await BrowserOAuthClient.load({ - clientId: new URL("./client-metadata.json", location.href).href, + clientId: new URL("/oauth-client-metadata.json", location.origin).href, handleResolver, });