A minimal reproduction of issues with the aud parameter within the app.bsky.authViewAll permission set
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

TypeScript 100.0%
4 1 0

Clone this repository

https://tangled.org/tylerjfisher.com/repro-bluesky-aud-bug https://tangled.org/did:plc:2hgmrwevidwsxundvejdeam5/repro-bluesky-aud-bug
git@tangled.org:tylerjfisher.com/repro-bluesky-aud-bug git@tangled.org:did:plc:2hgmrwevidwsxundvejdeam5/repro-bluesky-aud-bug

For self-hosted knots, clone URLs may differ based on your setup.

Download tar.gz
README.md

authViewAll aud inconsistency#

Minimal reproduction of a bug where scope enforcement for RPCs in the app.bsky.authViewAll permission set is inconsistent: some RPCs are authorized against the service-fragmented aud (did:web:api.bsky.app#bsky_appview), others against the bare aud (did:web:api.bsky.app). The client has no way to satisfy both from one include: scope.

Setup#

pnpm install
pnpm dev

Visit http://127.0.0.1:3000/, enter your atproto handle, approve the consent screen, and you'll land on /test. The page calls six RPCs with an identical agent and identical scope and reports which ones succeed.

Observed#

atproto include:app.bsky.authViewAll?aud=did:web:api.bsky.app%23bsky_appview

produces:

Lexicon Status Detail
app.bsky.actor.getProfile OK
app.bsky.feed.getTimeline OK
app.bsky.feed.getAuthorFeed OK
app.bsky.graph.getLists FAIL Missing required scope "rpc:app.bsky.graph.getLists?aud=did:web:api.bsky.app"
app.bsky.notification.listNotifications FAIL Missing required scope "rpc:app.bsky.notification.listNotifications?aud=did:web:api.bsky.app"
app.bsky.feed.getFeedGenerator FAIL Missing required scope "rpc:app.bsky.feed.getFeedGenerator?aud=did:web:api.bsky.app"

Every RPC in the list above is declared in the app.bsky.authViewAll permission set, which uses inheritAud: true — so all of them should be granted at the aud we passed to include:. The agent also sends atproto-proxy: did:web:api.bsky.app#bsky_appview on every call, so the service endpoint is unambiguously specified.

Why we can't work around it with rpc scopes#

Explicit per-RPC rpc: scopes would satisfy enforcement but display to users as broad permission requests on the consent screen, which is poor UX for an otherwise-narrow read-only app.

Files#

  • src/server.ts — the entire OAuth client + test harness: in-memory stores, ephemeral ES256 keypair per run, routes /, /login, /callback, /test, /jwks.json, /client-metadata.json.