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

grain.social/grain

photography-focused photo sharing platform

grain.social
1 collaborator
0
2

serve: A login names its account by handle at once

Merged
Merged chadtmiller.com asked to merge into feature/rust from grain.social/grain:serve/login-handle

What changes

A login tracks the account and keeps its handle. When the OAuth callback keeps the web session, grain-serve now tracks the account and writes the handle the PDS gave the session (oauth::named, from getSession) into repo.handle, provided that handle resolves back to the account's DID. So /profile/<handle> and social.grain.actor.getProfile?actor=<handle> find a newly logged-in account straight away. An identity event or a backfill pass still writes over it afterwards, as before.

A handle names one account. Index::set_handle now clears the handle from any other account that held it, in the same transaction. Reader::resolve takes the first DID that has a handle, so a stale row could otherwise win against the account that really has it.

Why

Only backfill (identity::handle) and Jetstream identity events wrote repo.handle, and both only for an account already tracked. The stream sends a handle only when it changes. So an account that had just logged in had no handle in the index, and its profile route said "Profile not found" until backfill reached it. The e2e flows ported from knotless found this.

The handle is checked before it is kept: it must resolve to the DID, the same check knotless's handle cache runs (verified_handle / rename). Without it, a PDS could claim someone else's handle in getSession and take over that handle's route here. A handle that doesn't resolve, or resolves to a different DID, is not kept and is left for backfill to look up. That never fails the login. A database error while tracking or keeping the handle does fail it, the same as a database error from keep_web_session just before.

Reader::resolve still looks only in the index and does not resolve unknown handles over the network. That keeps every request for an unknown handle (crawlers, typos) from becoming a DNS or .well-known lookup to whatever domain is asked for. A DID found that way would also give an empty page, because the index has nothing else for that account. Writing at login covers the case that matters, an account that is here.

How it was checked

a_login_names_its_account_by_handle_at_once in crates/grain-serve/tests/login.rs: a stale account holds the handle first. After login, before any backfill (the repo is still pending), the account is tracked with its handle and the stale row has none. Once the profile is seeded, getProfile by handle returns the account's DID and handle. One local cargo check -p grain-serve --tests passed; the rest is left to the runner.

Shortcomings

The login waits on one extra handle resolution, which is not cached. A handle that fails the check is dropped without a log line. No test covers that path: the test network's handles all resolve. Group accounts made through login::provisioned are not changed by this pull.

🤖 Generated with Claude Code

chadtmiller.com added 1 commit
Approved

This pull makes the OAuth callback track the logged-in account. It writes the handle from getSession into repo.handle only when that handle resolves back to the account's DID. set_handle now clears the handle from any other account in the same transaction. I found nothing that has to change.

  • Verification: the handle is checked with resolve_handle before it is kept, and a failed or mismatched resolution is logged and doesn't fail the login. That closes the takeover where a PDS claims someone else's handle.
  • Clearing in set_handle: set_handle is also used by backfill and identity events. They get the same single-holder rule, which fits what the pull describes.
  • Test: the test covers the stale holder, the pending state and the lookup by handle. I did not see the runner's check results.
  • Unchecked: I did not check that track creates the repo row, so set_handle has a row to update. The new test would fail if it didn't.
chadtmiller.com added 1 commit
Approved

Carried forward: the change this pull makes at 3c1bbc38a88040715f7134e3356d6574b68b1dbe is the one reviewed at 98e1b2468f014387e866702c6719ff72d958d65f, so that review stands. What is new comes from its base.

Approved

This pull makes the login callback track the account. It keeps the getSession handle only when that handle resolves back to the account's DID. set_handle now clears the handle from any other holder in the same transaction.

I found nothing that must change. My earlier approval stands. The verification-before-keep check is still there. The one change I noticed since then is that a failed or mismatched resolution is now dropped silently. The body says so, and it doesn't fail the login. The new test covers the stale holder, the pending state and the lookup by handle. I did not see the runner's check results.

Minor, not blocking: a database error from track or set_handle propagates through named(...)? and fails the login, though the body says the handle step never fails it. That holds for resolution failures only.

Merged as 20c6a3e
Part of a stack of 2
  1. serve: A login names its account by handle at once Landed as 20c6a3e
  2. main