Check an rpc: grant against the service the caller named #650

Merged ngerakines.me
targeting main
Merged opened by and assigned to ngerakines.me
targeting main

The aud of an rpc: scope is a service reference — a DID plus the fragment naming one entry in its document, as in did:web:api.bsky.app#bsky_appview. The proxy path checked the grant against ProxyTarget::did instead: the bare DID, which is the audience of the service-auth JWT it was about to mint. The two are different values for the same call by design, and the reference implementation keeps them apart — the scope check sees did#serviceId, the outbound JWT keeps the DID alone.

Comparing the grant against the wrong half refuses every conformant client. A Colibri sign-in against a PDS built from this workspace sent Atproto-Proxy: did:web:api.colibri.social#colibri_appview holding rpc:social.colibri.actor.listCommunities?aud=did:web:api.colibri.social#colibri_appview, and got back InsufficientScope: this token does not grant rpc:social.colibri.actor.listCommunities?aud=did:web:api.colibri.social — the scope it plainly held, with the fragment shaved off. The check passes only for a grant that names the DID alone, which is what this workspace's own client and its own tests wrote, so client and server agreed with each other and every test passed.

ProxyTarget now carries the service_id it resolved by rather than discarding it, and the assertion is ScopesSet::assert_rpc_service, which asks about did#service_id and names that reference when it refuses. A grant naming a bare DID still answers for every service under it: nothing conformant emits one, but tokens this server issued did, and a DID with no fragment has no other reading. The service-auth JWT's audience is unchanged.

com.atproto.moderation.createReport is the same kind of proxied call and had the same defect; it is checked against <report did>#atproto_labeler. The default AppView pin has no header to read a fragment from, so it assumes bsky_appview and honours one written on PDS_BSKY_APP_VIEW_DID when the operator's AppView names its service differently.

This pull request has no versions submitted yet.