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.

initial commit

Tyler Fisher d58d8c6a

+997
+1
.gitignore
··· 1 + node_modules
+55
README.md
··· 1 + # authViewAll aud-enforcement inconsistency — Bluesky OAuth repro 2 + 3 + Minimal reproduction of a Bluesky authorization-server bug where scope 4 + enforcement for RPCs in the `app.bsky.authViewAll` permission set is 5 + inconsistent: some RPCs are authorized against the service-fragmented aud 6 + (`did:web:api.bsky.app#bsky_appview`), others against the bare aud 7 + (`did:web:api.bsky.app`). The client has no way to satisfy both from one 8 + `include:` scope. 9 + 10 + ## Setup 11 + 12 + ```bash 13 + pnpm install 14 + pnpm dev 15 + ``` 16 + 17 + Visit http://127.0.0.1:3000/, enter your atproto handle, approve the 18 + consent screen, and you'll land on `/test`. The page calls six RPCs with 19 + an identical agent and identical scope and reports which ones succeed. 20 + 21 + ## Observed 22 + 23 + ``` 24 + atproto include:app.bsky.authViewAll?aud=did:web:api.bsky.app%23bsky_appview 25 + ``` 26 + 27 + produces: 28 + 29 + | Lexicon | Status | Detail | 30 + |---|---|---| 31 + | `app.bsky.actor.getProfile` | OK | | 32 + | `app.bsky.feed.getTimeline` | OK | | 33 + | `app.bsky.feed.getAuthorFeed` | OK | | 34 + | `app.bsky.graph.getLists` | FAIL | `Missing required scope "rpc:app.bsky.graph.getLists?aud=did:web:api.bsky.app"` | 35 + | `app.bsky.notification.listNotifications` | FAIL | `Missing required scope "rpc:app.bsky.notification.listNotifications?aud=did:web:api.bsky.app"` | 36 + | `app.bsky.feed.getFeedGenerator` | FAIL | `Missing required scope "rpc:app.bsky.feed.getFeedGenerator?aud=did:web:api.bsky.app"` | 37 + 38 + Every RPC in the list above is declared in the 39 + [`app.bsky.authViewAll` permission set](https://lexicon.garden/lexicon/did:plc:4v4y5r3lwsbtmsxhile2ljac/app.bsky.authViewAll/llms.txt), 40 + which uses `inheritAud: true` — so all of them should be granted at the 41 + aud we passed to `include:`. The agent also sends 42 + `atproto-proxy: did:web:api.bsky.app#bsky_appview` on every call, so the 43 + service endpoint is unambiguously specified. 44 + 45 + ## Why we can't work around it with rpc scopes 46 + 47 + Explicit per-RPC `rpc:` scopes would satisfy enforcement but display to 48 + users as broad permission requests on the consent screen, which is poor 49 + UX for an otherwise-narrow read-only app. 50 + 51 + ## Files 52 + 53 + - `src/server.ts` — the entire OAuth client + test harness: in-memory 54 + stores, ephemeral ES256 keypair per run, routes `/`, `/login`, 55 + `/callback`, `/test`, `/jwks.json`, `/client-metadata.json`.
+23
package.json
··· 1 + { 2 + "name": "bluesky-authviewall-aud-bug-repro", 3 + "private": true, 4 + "version": "0.0.0", 5 + "type": "module", 6 + "scripts": { 7 + "dev": "tsx watch src/server.ts", 8 + "start": "tsx src/server.ts" 9 + }, 10 + "dependencies": { 11 + "@atproto/api": "^0.19.8", 12 + "@atproto/jwk-jose": "0.1.11", 13 + "@atproto/oauth-client-node": "0.3.17", 14 + "@hono/node-server": "^1.13.5", 15 + "hono": "^4.6.14", 16 + "jose": "^6.2.2" 17 + }, 18 + "devDependencies": { 19 + "@types/node": "^22.0.0", 20 + "tsx": "^4.19.0", 21 + "typescript": "^5.5.0" 22 + } 23 + }
+672
pnpm-lock.yaml
··· 1 + lockfileVersion: '9.0' 2 + 3 + settings: 4 + autoInstallPeers: true 5 + excludeLinksFromLockfile: false 6 + 7 + importers: 8 + 9 + .: 10 + dependencies: 11 + '@atproto/api': 12 + specifier: ^0.19.8 13 + version: 0.19.8 14 + '@atproto/jwk-jose': 15 + specifier: 0.1.11 16 + version: 0.1.11 17 + '@atproto/oauth-client-node': 18 + specifier: 0.3.17 19 + version: 0.3.17 20 + '@hono/node-server': 21 + specifier: ^1.13.5 22 + version: 1.19.14(hono@4.12.12) 23 + hono: 24 + specifier: ^4.6.14 25 + version: 4.12.12 26 + jose: 27 + specifier: ^6.2.2 28 + version: 6.2.2 29 + devDependencies: 30 + '@types/node': 31 + specifier: ^22.0.0 32 + version: 22.19.17 33 + tsx: 34 + specifier: ^4.19.0 35 + version: 4.21.0 36 + typescript: 37 + specifier: ^5.5.0 38 + version: 5.9.3 39 + 40 + packages: 41 + 42 + '@atproto-labs/did-resolver@0.2.6': 43 + resolution: {integrity: sha512-2K1bC04nI2fmgNcvof+yA28IhGlpWn2JKYlPa7To9JTKI45FINCGkQSGiL2nyXlyzDJJ34fZ1aq6/IRFIOIiqg==} 44 + 45 + '@atproto-labs/fetch-node@0.2.0': 46 + resolution: {integrity: sha512-Krq09nH/aeoiU2s9xdHA0FjTEFWG9B5FFenipv1iRixCcPc7V3DhTNDawxG9gI8Ny0k4dBVS9WTRN/IDzBx86Q==} 47 + engines: {node: '>=18.7.0'} 48 + 49 + '@atproto-labs/fetch@0.2.3': 50 + resolution: {integrity: sha512-NZtbJOCbxKUFRFKMpamT38PUQMY0hX0p7TG5AEYOPhZKZEP7dHZ1K2s1aB8MdVH0qxmqX7nQleNrrvLf09Zfdw==} 51 + 52 + '@atproto-labs/handle-resolver-node@0.1.25': 53 + resolution: {integrity: sha512-NY9WYM2VLd3IuMGRkkmvGBg8xqVEaK/fitv1vD8SMXqFTekdpjOLCCyv7EFtqVHouzmDcL83VOvWRfHVa8V9Yw==} 54 + engines: {node: '>=18.7.0'} 55 + 56 + '@atproto-labs/handle-resolver@0.3.6': 57 + resolution: {integrity: sha512-qnSTXvOBNj1EHhp2qTWSX8MS5q3AwYU5LKlt5fBvSbCjgmTr2j0URHCv+ydrwO55KvsojIkTMgeMOh4YuY4fCA==} 58 + 59 + '@atproto-labs/identity-resolver@0.3.6': 60 + resolution: {integrity: sha512-qoWqBDRobln0NR8L8dQjSp79E0chGkBhibEgxQa2f9WD+JbJdjQ0YvwwO5yeQn05pJoJmAwmI2wyJ45zjU7aWg==} 61 + 62 + '@atproto-labs/pipe@0.1.1': 63 + resolution: {integrity: sha512-hdNw2oUs2B6BN1lp+32pF7cp8EMKuIN5Qok2Vvv/aOpG/3tNSJ9YkvfI0k6Zd188LeDDYRUpYpxcoFIcGH/FNg==} 64 + 65 + '@atproto-labs/simple-store-memory@0.1.4': 66 + resolution: {integrity: sha512-3mKY4dP8I7yKPFj9VKpYyCRzGJOi5CEpOLPlRhoJyLmgs3J4RzDrjn323Oakjz2Aj2JzRU/AIvWRAZVhpYNJHw==} 67 + 68 + '@atproto-labs/simple-store@0.3.0': 69 + resolution: {integrity: sha512-nOb6ONKBRJHRlukW1sVawUkBqReLlLx6hT35VS3imaNPwiXDxLnTK7lxw3Lrl9k5yugSBDQAkZAq3MPTEFSUBQ==} 70 + 71 + '@atproto/api@0.19.8': 72 + resolution: {integrity: sha512-b79kuI3AzEmpLLi9afRNq6T0KFEEVL4d+vHFAtWxeDwS7lfwUOIIngMjAVvwmwC5nJRZIrK8L9d4y7LD8zdvsg==} 73 + 74 + '@atproto/common-web@0.4.20': 75 + resolution: {integrity: sha512-RcsYT28yQgVi/Glb/hHPGpqpzIlKrbMLeldEd7PmmMLWDaJL2j3lb92qytvxjl1yhi2Ssq2TEuMZ2NlWaAbpow==} 76 + 77 + '@atproto/did@0.3.0': 78 + resolution: {integrity: sha512-raUPzUGegtW/6OxwCmM8bhZvuIMzxG5t9oWsth6Tp91Kb5fTnHV2h/KKNF1C82doeA4BdXCErTyg7ISwLbQkzA==} 79 + 80 + '@atproto/jwk-jose@0.1.11': 81 + resolution: {integrity: sha512-i4Fnr2sTBYmMmHXl7NJh8GrCH+tDQEVWrcDMDnV5DjJfkgT17wIqvojIw9SNbSL4Uf0OtfEv6AgG0A+mgh8b5Q==} 82 + 83 + '@atproto/jwk-webcrypto@0.2.0': 84 + resolution: {integrity: sha512-UmgRrrEAkWvxwhlwe30UmDOdTEFidlIzBC7C3cCbeJMcBN1x8B3KH+crXrsTqfWQBG58mXgt8wgSK3Kxs2LhFg==} 85 + 86 + '@atproto/jwk@0.6.0': 87 + resolution: {integrity: sha512-bDoJPvt7TrQVi/rBfBrSSpGykhtIriKxeYCYQTiPRKFfyRhbgpElF0wPXADjIswnbzZdOwbY63az4E/CFVT3Tw==} 88 + 89 + '@atproto/lex-data@0.0.15': 90 + resolution: {integrity: sha512-ZsbGiaM5S3CnGrcTMbDGON3bLZzCi/Mx9UvcMREKSRujnF68eHgMiXxJqvykP7+QpOX6tYCK93axZkuJVhtSEw==} 91 + 92 + '@atproto/lex-json@0.0.15': 93 + resolution: {integrity: sha512-kCLdP629H6GhgPjBTpZibUoqlpmW0hnVfZVwcD4s4Jch1KAqY/QcfL24Ih8wrW0Ok1YvtMIhjk98evdTA2OJcw==} 94 + 95 + '@atproto/lexicon@0.6.2': 96 + resolution: {integrity: sha512-p3Ly6hinVZW0ETuAXZMeUGwuMm3g8HvQMQ41yyEE6AL0hAkfeKFaZKos6BdBrr6CjkpbrDZqE8M+5+QOceysMw==} 97 + 98 + '@atproto/oauth-client-node@0.3.17': 99 + resolution: {integrity: sha512-67LNuKAlC35Exe7CB5S0QCAnEqr6fKV9Nvp64jAHFof1N+Vc9Ltt1K9oekE5Ctf7dvpGByrHRF0noUw9l9sWLA==} 100 + engines: {node: '>=18.7.0'} 101 + 102 + '@atproto/oauth-client@0.6.0': 103 + resolution: {integrity: sha512-F7ZTKzFptXgyihMkd7QTdRSkrh4XqrS+qTw+V81k5Q6Bh3MB1L3ypvfSJ6v7SSUJa6XxoZYJTCahHC1e+ndE6Q==} 104 + 105 + '@atproto/oauth-types@0.6.3': 106 + resolution: {integrity: sha512-jdKuoPknJuh/WjI+mYk7agSbx9mNVMbS6Dr3k1z2YMY2oRiCQjxYBuo4MLKATbxj05nMQaZRWlHRUazoAu5Cng==} 107 + 108 + '@atproto/syntax@0.5.3': 109 + resolution: {integrity: sha512-gzhlHOJHm5KXdCc17fXi1fXM81ccs5jJfNgCui84ay9JGvczxegpYHNqdMlv+iBuhtBzFIjgx6ChjRxN/kO8kQ==} 110 + 111 + '@atproto/xrpc@0.7.7': 112 + resolution: {integrity: sha512-K1ZyO/BU8JNtXX5dmPp7b5UrkLMMqpsIa/Lrj5D3Su+j1Xwq1m6QJ2XJ1AgjEjkI1v4Muzm7klianLE6XGxtmA==} 113 + 114 + '@esbuild/aix-ppc64@0.27.7': 115 + resolution: {integrity: sha512-EKX3Qwmhz1eMdEJokhALr0YiD0lhQNwDqkPYyPhiSwKrh7/4KRjQc04sZ8db+5DVVnZ1LmbNDI1uAMPEUBnQPg==} 116 + engines: {node: '>=18'} 117 + cpu: [ppc64] 118 + os: [aix] 119 + 120 + '@esbuild/android-arm64@0.27.7': 121 + resolution: {integrity: sha512-62dPZHpIXzvChfvfLJow3q5dDtiNMkwiRzPylSCfriLvZeq0a1bWChrGx/BbUbPwOrsWKMn8idSllklzBy+dgQ==} 122 + engines: {node: '>=18'} 123 + cpu: [arm64] 124 + os: [android] 125 + 126 + '@esbuild/android-arm@0.27.7': 127 + resolution: {integrity: sha512-jbPXvB4Yj2yBV7HUfE2KHe4GJX51QplCN1pGbYjvsyCZbQmies29EoJbkEc+vYuU5o45AfQn37vZlyXy4YJ8RQ==} 128 + engines: {node: '>=18'} 129 + cpu: [arm] 130 + os: [android] 131 + 132 + '@esbuild/android-x64@0.27.7': 133 + resolution: {integrity: sha512-x5VpMODneVDb70PYV2VQOmIUUiBtY3D3mPBG8NxVk5CogneYhkR7MmM3yR/uMdITLrC1ml/NV1rj4bMJuy9MCg==} 134 + engines: {node: '>=18'} 135 + cpu: [x64] 136 + os: [android] 137 + 138 + '@esbuild/darwin-arm64@0.27.7': 139 + resolution: {integrity: sha512-5lckdqeuBPlKUwvoCXIgI2D9/ABmPq3Rdp7IfL70393YgaASt7tbju3Ac+ePVi3KDH6N2RqePfHnXkaDtY9fkw==} 140 + engines: {node: '>=18'} 141 + cpu: [arm64] 142 + os: [darwin] 143 + 144 + '@esbuild/darwin-x64@0.27.7': 145 + resolution: {integrity: sha512-rYnXrKcXuT7Z+WL5K980jVFdvVKhCHhUwid+dDYQpH+qu+TefcomiMAJpIiC2EM3Rjtq0sO3StMV/+3w3MyyqQ==} 146 + engines: {node: '>=18'} 147 + cpu: [x64] 148 + os: [darwin] 149 + 150 + '@esbuild/freebsd-arm64@0.27.7': 151 + resolution: {integrity: sha512-B48PqeCsEgOtzME2GbNM2roU29AMTuOIN91dsMO30t+Ydis3z/3Ngoj5hhnsOSSwNzS+6JppqWsuhTp6E82l2w==} 152 + engines: {node: '>=18'} 153 + cpu: [arm64] 154 + os: [freebsd] 155 + 156 + '@esbuild/freebsd-x64@0.27.7': 157 + resolution: {integrity: sha512-jOBDK5XEjA4m5IJK3bpAQF9/Lelu/Z9ZcdhTRLf4cajlB+8VEhFFRjWgfy3M1O4rO2GQ/b2dLwCUGpiF/eATNQ==} 158 + engines: {node: '>=18'} 159 + cpu: [x64] 160 + os: [freebsd] 161 + 162 + '@esbuild/linux-arm64@0.27.7': 163 + resolution: {integrity: sha512-RZPHBoxXuNnPQO9rvjh5jdkRmVizktkT7TCDkDmQ0W2SwHInKCAV95GRuvdSvA7w4VMwfCjUiPwDi0ZO6Nfe9A==} 164 + engines: {node: '>=18'} 165 + cpu: [arm64] 166 + os: [linux] 167 + 168 + '@esbuild/linux-arm@0.27.7': 169 + resolution: {integrity: sha512-RkT/YXYBTSULo3+af8Ib0ykH8u2MBh57o7q/DAs3lTJlyVQkgQvlrPTnjIzzRPQyavxtPtfg0EopvDyIt0j1rA==} 170 + engines: {node: '>=18'} 171 + cpu: [arm] 172 + os: [linux] 173 + 174 + '@esbuild/linux-ia32@0.27.7': 175 + resolution: {integrity: sha512-GA48aKNkyQDbd3KtkplYWT102C5sn/EZTY4XROkxONgruHPU72l+gW+FfF8tf2cFjeHaRbWpOYa/uRBz/Xq1Pg==} 176 + engines: {node: '>=18'} 177 + cpu: [ia32] 178 + os: [linux] 179 + 180 + '@esbuild/linux-loong64@0.27.7': 181 + resolution: {integrity: sha512-a4POruNM2oWsD4WKvBSEKGIiWQF8fZOAsycHOt6JBpZ+JN2n2JH9WAv56SOyu9X5IqAjqSIPTaJkqN8F7XOQ5Q==} 182 + engines: {node: '>=18'} 183 + cpu: [loong64] 184 + os: [linux] 185 + 186 + '@esbuild/linux-mips64el@0.27.7': 187 + resolution: {integrity: sha512-KabT5I6StirGfIz0FMgl1I+R1H73Gp0ofL9A3nG3i/cYFJzKHhouBV5VWK1CSgKvVaG4q1RNpCTR2LuTVB3fIw==} 188 + engines: {node: '>=18'} 189 + cpu: [mips64el] 190 + os: [linux] 191 + 192 + '@esbuild/linux-ppc64@0.27.7': 193 + resolution: {integrity: sha512-gRsL4x6wsGHGRqhtI+ifpN/vpOFTQtnbsupUF5R5YTAg+y/lKelYR1hXbnBdzDjGbMYjVJLJTd2OFmMewAgwlQ==} 194 + engines: {node: '>=18'} 195 + cpu: [ppc64] 196 + os: [linux] 197 + 198 + '@esbuild/linux-riscv64@0.27.7': 199 + resolution: {integrity: sha512-hL25LbxO1QOngGzu2U5xeXtxXcW+/GvMN3ejANqXkxZ/opySAZMrc+9LY/WyjAan41unrR3YrmtTsUpwT66InQ==} 200 + engines: {node: '>=18'} 201 + cpu: [riscv64] 202 + os: [linux] 203 + 204 + '@esbuild/linux-s390x@0.27.7': 205 + resolution: {integrity: sha512-2k8go8Ycu1Kb46vEelhu1vqEP+UeRVj2zY1pSuPdgvbd5ykAw82Lrro28vXUrRmzEsUV0NzCf54yARIK8r0fdw==} 206 + engines: {node: '>=18'} 207 + cpu: [s390x] 208 + os: [linux] 209 + 210 + '@esbuild/linux-x64@0.27.7': 211 + resolution: {integrity: sha512-hzznmADPt+OmsYzw1EE33ccA+HPdIqiCRq7cQeL1Jlq2gb1+OyWBkMCrYGBJ+sxVzve2ZJEVeePbLM2iEIZSxA==} 212 + engines: {node: '>=18'} 213 + cpu: [x64] 214 + os: [linux] 215 + 216 + '@esbuild/netbsd-arm64@0.27.7': 217 + resolution: {integrity: sha512-b6pqtrQdigZBwZxAn1UpazEisvwaIDvdbMbmrly7cDTMFnw/+3lVxxCTGOrkPVnsYIosJJXAsILG9XcQS+Yu6w==} 218 + engines: {node: '>=18'} 219 + cpu: [arm64] 220 + os: [netbsd] 221 + 222 + '@esbuild/netbsd-x64@0.27.7': 223 + resolution: {integrity: sha512-OfatkLojr6U+WN5EDYuoQhtM+1xco+/6FSzJJnuWiUw5eVcicbyK3dq5EeV/QHT1uy6GoDhGbFpprUiHUYggrw==} 224 + engines: {node: '>=18'} 225 + cpu: [x64] 226 + os: [netbsd] 227 + 228 + '@esbuild/openbsd-arm64@0.27.7': 229 + resolution: {integrity: sha512-AFuojMQTxAz75Fo8idVcqoQWEHIXFRbOc1TrVcFSgCZtQfSdc1RXgB3tjOn/krRHENUB4j00bfGjyl2mJrU37A==} 230 + engines: {node: '>=18'} 231 + cpu: [arm64] 232 + os: [openbsd] 233 + 234 + '@esbuild/openbsd-x64@0.27.7': 235 + resolution: {integrity: sha512-+A1NJmfM8WNDv5CLVQYJ5PshuRm/4cI6WMZRg1by1GwPIQPCTs1GLEUHwiiQGT5zDdyLiRM/l1G0Pv54gvtKIg==} 236 + engines: {node: '>=18'} 237 + cpu: [x64] 238 + os: [openbsd] 239 + 240 + '@esbuild/openharmony-arm64@0.27.7': 241 + resolution: {integrity: sha512-+KrvYb/C8zA9CU/g0sR6w2RBw7IGc5J2BPnc3dYc5VJxHCSF1yNMxTV5LQ7GuKteQXZtspjFbiuW5/dOj7H4Yw==} 242 + engines: {node: '>=18'} 243 + cpu: [arm64] 244 + os: [openharmony] 245 + 246 + '@esbuild/sunos-x64@0.27.7': 247 + resolution: {integrity: sha512-ikktIhFBzQNt/QDyOL580ti9+5mL/YZeUPKU2ivGtGjdTYoqz6jObj6nOMfhASpS4GU4Q/Clh1QtxWAvcYKamA==} 248 + engines: {node: '>=18'} 249 + cpu: [x64] 250 + os: [sunos] 251 + 252 + '@esbuild/win32-arm64@0.27.7': 253 + resolution: {integrity: sha512-7yRhbHvPqSpRUV7Q20VuDwbjW5kIMwTHpptuUzV+AA46kiPze5Z7qgt6CLCK3pWFrHeNfDd1VKgyP4O+ng17CA==} 254 + engines: {node: '>=18'} 255 + cpu: [arm64] 256 + os: [win32] 257 + 258 + '@esbuild/win32-ia32@0.27.7': 259 + resolution: {integrity: sha512-SmwKXe6VHIyZYbBLJrhOoCJRB/Z1tckzmgTLfFYOfpMAx63BJEaL9ExI8x7v0oAO3Zh6D/Oi1gVxEYr5oUCFhw==} 260 + engines: {node: '>=18'} 261 + cpu: [ia32] 262 + os: [win32] 263 + 264 + '@esbuild/win32-x64@0.27.7': 265 + resolution: {integrity: sha512-56hiAJPhwQ1R4i+21FVF7V8kSD5zZTdHcVuRFMW0hn753vVfQN8xlx4uOPT4xoGH0Z/oVATuR82AiqSTDIpaHg==} 266 + engines: {node: '>=18'} 267 + cpu: [x64] 268 + os: [win32] 269 + 270 + '@hono/node-server@1.19.14': 271 + resolution: {integrity: sha512-GwtvgtXxnWsucXvbQXkRgqksiH2Qed37H9xHZocE5sA3N8O8O8/8FA3uclQXxXVzc9XBZuEOMK7+r02FmSpHtw==} 272 + engines: {node: '>=18.14.1'} 273 + peerDependencies: 274 + hono: ^4 275 + 276 + '@types/node@22.19.17': 277 + resolution: {integrity: sha512-wGdMcf+vPYM6jikpS/qhg6WiqSV/OhG+jeeHT/KlVqxYfD40iYJf9/AE1uQxVWFvU7MipKRkRv8NSHiCGgPr8Q==} 278 + 279 + await-lock@2.2.2: 280 + resolution: {integrity: sha512-aDczADvlvTGajTDjcjpJMqRkOF6Qdz3YbPZm/PyW6tKPkx2hlYBzxMhEywM/tU72HrVZjgl5VCdRuMlA7pZ8Gw==} 281 + 282 + core-js@3.49.0: 283 + resolution: {integrity: sha512-es1U2+YTtzpwkxVLwAFdSpaIMyQaq0PBgm3YD1W3Qpsn1NAmO3KSgZfu+oGSWVu6NvLHoHCV/aYcsE5wiB7ALg==} 284 + 285 + esbuild@0.27.7: 286 + resolution: {integrity: sha512-IxpibTjyVnmrIQo5aqNpCgoACA/dTKLTlhMHihVHhdkxKyPO1uBBthumT0rdHmcsk9uMonIWS0m4FljWzILh3w==} 287 + engines: {node: '>=18'} 288 + hasBin: true 289 + 290 + fsevents@2.3.3: 291 + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} 292 + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} 293 + os: [darwin] 294 + 295 + get-tsconfig@4.13.7: 296 + resolution: {integrity: sha512-7tN6rFgBlMgpBML5j8typ92BKFi2sFQvIdpAqLA2beia5avZDrMs0FLZiM5etShWq5irVyGcGMEA1jcDaK7A/Q==} 297 + 298 + hono@4.12.12: 299 + resolution: {integrity: sha512-p1JfQMKaceuCbpJKAPKVqyqviZdS0eUxH9v82oWo1kb9xjQ5wA6iP3FNVAPDFlz5/p7d45lO+BpSk1tuSZMF4Q==} 300 + engines: {node: '>=16.9.0'} 301 + 302 + ipaddr.js@2.3.0: 303 + resolution: {integrity: sha512-Zv/pA+ciVFbCSBBjGfaKUya/CcGmUHzTydLMaTwrUUEM2DIEO3iZvueGxmacvmN50fGpGVKeTXpb2LcYQxeVdg==} 304 + engines: {node: '>= 10'} 305 + 306 + iso-datestring-validator@2.2.2: 307 + resolution: {integrity: sha512-yLEMkBbLZTlVQqOnQ4FiMujR6T4DEcCb1xizmvXS+OxuhwcbtynoosRzdMA69zZCShCNAbi+gJ71FxZBBXx1SA==} 308 + 309 + jose@5.10.0: 310 + resolution: {integrity: sha512-s+3Al/p9g32Iq+oqXxkW//7jk2Vig6FF1CFqzVXoTUXt2qz89YWbL+OwS17NFYEvxC35n0FKeGO2LGYSxeM2Gg==} 311 + 312 + jose@6.2.2: 313 + resolution: {integrity: sha512-d7kPDd34KO/YnzaDOlikGpOurfF0ByC2sEV4cANCtdqLlTfBlw2p14O/5d/zv40gJPbIQxfES3nSx1/oYNyuZQ==} 314 + 315 + lru-cache@10.4.3: 316 + resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} 317 + 318 + multiformats@9.9.0: 319 + resolution: {integrity: sha512-HoMUjhH9T8DDBNT+6xzkrd9ga/XiBI4xLr58LJACwK6G3HTOPeMz4nB4KJs33L2BelrIJa7P0VuNaVF3hMYfjg==} 320 + 321 + resolve-pkg-maps@1.0.0: 322 + resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} 323 + 324 + tlds@1.261.0: 325 + resolution: {integrity: sha512-QXqwfEl9ddlGBaRFXIvNKK6OhipSiLXuRuLJX5DErz0o0Q0rYxulWLdFryTkV5PkdZct5iMInwYEGe/eR++1AA==} 326 + hasBin: true 327 + 328 + tslib@2.8.1: 329 + resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} 330 + 331 + tsx@4.21.0: 332 + resolution: {integrity: sha512-5C1sg4USs1lfG0GFb2RLXsdpXqBSEhAaA/0kPL01wxzpMqLILNxIxIOKiILz+cdg/pLnOUxFYOR5yhHU666wbw==} 333 + engines: {node: '>=18.0.0'} 334 + hasBin: true 335 + 336 + typescript@5.9.3: 337 + resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==} 338 + engines: {node: '>=14.17'} 339 + hasBin: true 340 + 341 + uint8arrays@3.0.0: 342 + resolution: {integrity: sha512-HRCx0q6O9Bfbp+HHSfQQKD7wU70+lydKVt4EghkdOvlK/NlrF90z+eXV34mUd48rNvVJXwkrMSPpCATkct8fJA==} 343 + 344 + undici-types@6.21.0: 345 + resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==} 346 + 347 + undici@6.24.1: 348 + resolution: {integrity: sha512-sC+b0tB1whOCzbtlx20fx3WgCXwkW627p4EA9uM+/tNNPkSS+eSEld6pAs9nDv7WbY1UUljBMYPtu9BCOrCWKA==} 349 + engines: {node: '>=18.17'} 350 + 351 + unicode-segmenter@0.14.5: 352 + resolution: {integrity: sha512-jHGmj2LUuqDcX3hqY12Ql+uhUTn8huuxNZGq7GvtF6bSybzH3aFgedYu/KTzQStEgt1Ra2F3HxadNXsNjb3m3g==} 353 + 354 + zod@3.25.76: 355 + resolution: {integrity: sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==} 356 + 357 + snapshots: 358 + 359 + '@atproto-labs/did-resolver@0.2.6': 360 + dependencies: 361 + '@atproto-labs/fetch': 0.2.3 362 + '@atproto-labs/pipe': 0.1.1 363 + '@atproto-labs/simple-store': 0.3.0 364 + '@atproto-labs/simple-store-memory': 0.1.4 365 + '@atproto/did': 0.3.0 366 + zod: 3.25.76 367 + 368 + '@atproto-labs/fetch-node@0.2.0': 369 + dependencies: 370 + '@atproto-labs/fetch': 0.2.3 371 + '@atproto-labs/pipe': 0.1.1 372 + ipaddr.js: 2.3.0 373 + undici: 6.24.1 374 + 375 + '@atproto-labs/fetch@0.2.3': 376 + dependencies: 377 + '@atproto-labs/pipe': 0.1.1 378 + 379 + '@atproto-labs/handle-resolver-node@0.1.25': 380 + dependencies: 381 + '@atproto-labs/fetch-node': 0.2.0 382 + '@atproto-labs/handle-resolver': 0.3.6 383 + '@atproto/did': 0.3.0 384 + 385 + '@atproto-labs/handle-resolver@0.3.6': 386 + dependencies: 387 + '@atproto-labs/simple-store': 0.3.0 388 + '@atproto-labs/simple-store-memory': 0.1.4 389 + '@atproto/did': 0.3.0 390 + zod: 3.25.76 391 + 392 + '@atproto-labs/identity-resolver@0.3.6': 393 + dependencies: 394 + '@atproto-labs/did-resolver': 0.2.6 395 + '@atproto-labs/handle-resolver': 0.3.6 396 + 397 + '@atproto-labs/pipe@0.1.1': {} 398 + 399 + '@atproto-labs/simple-store-memory@0.1.4': 400 + dependencies: 401 + '@atproto-labs/simple-store': 0.3.0 402 + lru-cache: 10.4.3 403 + 404 + '@atproto-labs/simple-store@0.3.0': {} 405 + 406 + '@atproto/api@0.19.8': 407 + dependencies: 408 + '@atproto/common-web': 0.4.20 409 + '@atproto/lexicon': 0.6.2 410 + '@atproto/syntax': 0.5.3 411 + '@atproto/xrpc': 0.7.7 412 + await-lock: 2.2.2 413 + multiformats: 9.9.0 414 + tlds: 1.261.0 415 + zod: 3.25.76 416 + 417 + '@atproto/common-web@0.4.20': 418 + dependencies: 419 + '@atproto/lex-data': 0.0.15 420 + '@atproto/lex-json': 0.0.15 421 + '@atproto/syntax': 0.5.3 422 + zod: 3.25.76 423 + 424 + '@atproto/did@0.3.0': 425 + dependencies: 426 + zod: 3.25.76 427 + 428 + '@atproto/jwk-jose@0.1.11': 429 + dependencies: 430 + '@atproto/jwk': 0.6.0 431 + jose: 5.10.0 432 + 433 + '@atproto/jwk-webcrypto@0.2.0': 434 + dependencies: 435 + '@atproto/jwk': 0.6.0 436 + '@atproto/jwk-jose': 0.1.11 437 + zod: 3.25.76 438 + 439 + '@atproto/jwk@0.6.0': 440 + dependencies: 441 + multiformats: 9.9.0 442 + zod: 3.25.76 443 + 444 + '@atproto/lex-data@0.0.15': 445 + dependencies: 446 + multiformats: 9.9.0 447 + tslib: 2.8.1 448 + uint8arrays: 3.0.0 449 + unicode-segmenter: 0.14.5 450 + 451 + '@atproto/lex-json@0.0.15': 452 + dependencies: 453 + '@atproto/lex-data': 0.0.15 454 + tslib: 2.8.1 455 + 456 + '@atproto/lexicon@0.6.2': 457 + dependencies: 458 + '@atproto/common-web': 0.4.20 459 + '@atproto/syntax': 0.5.3 460 + iso-datestring-validator: 2.2.2 461 + multiformats: 9.9.0 462 + zod: 3.25.76 463 + 464 + '@atproto/oauth-client-node@0.3.17': 465 + dependencies: 466 + '@atproto-labs/did-resolver': 0.2.6 467 + '@atproto-labs/handle-resolver-node': 0.1.25 468 + '@atproto-labs/simple-store': 0.3.0 469 + '@atproto/did': 0.3.0 470 + '@atproto/jwk': 0.6.0 471 + '@atproto/jwk-jose': 0.1.11 472 + '@atproto/jwk-webcrypto': 0.2.0 473 + '@atproto/oauth-client': 0.6.0 474 + '@atproto/oauth-types': 0.6.3 475 + 476 + '@atproto/oauth-client@0.6.0': 477 + dependencies: 478 + '@atproto-labs/did-resolver': 0.2.6 479 + '@atproto-labs/fetch': 0.2.3 480 + '@atproto-labs/handle-resolver': 0.3.6 481 + '@atproto-labs/identity-resolver': 0.3.6 482 + '@atproto-labs/simple-store': 0.3.0 483 + '@atproto-labs/simple-store-memory': 0.1.4 484 + '@atproto/did': 0.3.0 485 + '@atproto/jwk': 0.6.0 486 + '@atproto/oauth-types': 0.6.3 487 + '@atproto/xrpc': 0.7.7 488 + core-js: 3.49.0 489 + multiformats: 9.9.0 490 + zod: 3.25.76 491 + 492 + '@atproto/oauth-types@0.6.3': 493 + dependencies: 494 + '@atproto/did': 0.3.0 495 + '@atproto/jwk': 0.6.0 496 + zod: 3.25.76 497 + 498 + '@atproto/syntax@0.5.3': 499 + dependencies: 500 + tslib: 2.8.1 501 + 502 + '@atproto/xrpc@0.7.7': 503 + dependencies: 504 + '@atproto/lexicon': 0.6.2 505 + zod: 3.25.76 506 + 507 + '@esbuild/aix-ppc64@0.27.7': 508 + optional: true 509 + 510 + '@esbuild/android-arm64@0.27.7': 511 + optional: true 512 + 513 + '@esbuild/android-arm@0.27.7': 514 + optional: true 515 + 516 + '@esbuild/android-x64@0.27.7': 517 + optional: true 518 + 519 + '@esbuild/darwin-arm64@0.27.7': 520 + optional: true 521 + 522 + '@esbuild/darwin-x64@0.27.7': 523 + optional: true 524 + 525 + '@esbuild/freebsd-arm64@0.27.7': 526 + optional: true 527 + 528 + '@esbuild/freebsd-x64@0.27.7': 529 + optional: true 530 + 531 + '@esbuild/linux-arm64@0.27.7': 532 + optional: true 533 + 534 + '@esbuild/linux-arm@0.27.7': 535 + optional: true 536 + 537 + '@esbuild/linux-ia32@0.27.7': 538 + optional: true 539 + 540 + '@esbuild/linux-loong64@0.27.7': 541 + optional: true 542 + 543 + '@esbuild/linux-mips64el@0.27.7': 544 + optional: true 545 + 546 + '@esbuild/linux-ppc64@0.27.7': 547 + optional: true 548 + 549 + '@esbuild/linux-riscv64@0.27.7': 550 + optional: true 551 + 552 + '@esbuild/linux-s390x@0.27.7': 553 + optional: true 554 + 555 + '@esbuild/linux-x64@0.27.7': 556 + optional: true 557 + 558 + '@esbuild/netbsd-arm64@0.27.7': 559 + optional: true 560 + 561 + '@esbuild/netbsd-x64@0.27.7': 562 + optional: true 563 + 564 + '@esbuild/openbsd-arm64@0.27.7': 565 + optional: true 566 + 567 + '@esbuild/openbsd-x64@0.27.7': 568 + optional: true 569 + 570 + '@esbuild/openharmony-arm64@0.27.7': 571 + optional: true 572 + 573 + '@esbuild/sunos-x64@0.27.7': 574 + optional: true 575 + 576 + '@esbuild/win32-arm64@0.27.7': 577 + optional: true 578 + 579 + '@esbuild/win32-ia32@0.27.7': 580 + optional: true 581 + 582 + '@esbuild/win32-x64@0.27.7': 583 + optional: true 584 + 585 + '@hono/node-server@1.19.14(hono@4.12.12)': 586 + dependencies: 587 + hono: 4.12.12 588 + 589 + '@types/node@22.19.17': 590 + dependencies: 591 + undici-types: 6.21.0 592 + 593 + await-lock@2.2.2: {} 594 + 595 + core-js@3.49.0: {} 596 + 597 + esbuild@0.27.7: 598 + optionalDependencies: 599 + '@esbuild/aix-ppc64': 0.27.7 600 + '@esbuild/android-arm': 0.27.7 601 + '@esbuild/android-arm64': 0.27.7 602 + '@esbuild/android-x64': 0.27.7 603 + '@esbuild/darwin-arm64': 0.27.7 604 + '@esbuild/darwin-x64': 0.27.7 605 + '@esbuild/freebsd-arm64': 0.27.7 606 + '@esbuild/freebsd-x64': 0.27.7 607 + '@esbuild/linux-arm': 0.27.7 608 + '@esbuild/linux-arm64': 0.27.7 609 + '@esbuild/linux-ia32': 0.27.7 610 + '@esbuild/linux-loong64': 0.27.7 611 + '@esbuild/linux-mips64el': 0.27.7 612 + '@esbuild/linux-ppc64': 0.27.7 613 + '@esbuild/linux-riscv64': 0.27.7 614 + '@esbuild/linux-s390x': 0.27.7 615 + '@esbuild/linux-x64': 0.27.7 616 + '@esbuild/netbsd-arm64': 0.27.7 617 + '@esbuild/netbsd-x64': 0.27.7 618 + '@esbuild/openbsd-arm64': 0.27.7 619 + '@esbuild/openbsd-x64': 0.27.7 620 + '@esbuild/openharmony-arm64': 0.27.7 621 + '@esbuild/sunos-x64': 0.27.7 622 + '@esbuild/win32-arm64': 0.27.7 623 + '@esbuild/win32-ia32': 0.27.7 624 + '@esbuild/win32-x64': 0.27.7 625 + 626 + fsevents@2.3.3: 627 + optional: true 628 + 629 + get-tsconfig@4.13.7: 630 + dependencies: 631 + resolve-pkg-maps: 1.0.0 632 + 633 + hono@4.12.12: {} 634 + 635 + ipaddr.js@2.3.0: {} 636 + 637 + iso-datestring-validator@2.2.2: {} 638 + 639 + jose@5.10.0: {} 640 + 641 + jose@6.2.2: {} 642 + 643 + lru-cache@10.4.3: {} 644 + 645 + multiformats@9.9.0: {} 646 + 647 + resolve-pkg-maps@1.0.0: {} 648 + 649 + tlds@1.261.0: {} 650 + 651 + tslib@2.8.1: {} 652 + 653 + tsx@4.21.0: 654 + dependencies: 655 + esbuild: 0.27.7 656 + get-tsconfig: 4.13.7 657 + optionalDependencies: 658 + fsevents: 2.3.3 659 + 660 + typescript@5.9.3: {} 661 + 662 + uint8arrays@3.0.0: 663 + dependencies: 664 + multiformats: 9.9.0 665 + 666 + undici-types@6.21.0: {} 667 + 668 + undici@6.24.1: {} 669 + 670 + unicode-segmenter@0.14.5: {} 671 + 672 + zod@3.25.76: {}
+233
src/server.ts
··· 1 + /** 2 + * Minimal reproduction of a Bluesky OAuth aud-enforcement inconsistency. 3 + * 4 + * Scope requested (via the include: permission set "app.bsky.authViewAll" 5 + * bound to aud=did:web:api.bsky.app#bsky_appview): 6 + * 7 + * atproto include:app.bsky.authViewAll?aud=did:web:api.bsky.app%23bsky_appview 8 + * 9 + * The permission set declares `inheritAud: true` and lists every app.bsky.* 10 + * view RPC including `app.bsky.feed.getFeedGenerator` (see 11 + * https://lexicon.garden/lexicon/did:plc:4v4y5r3lwsbtmsxhile2ljac/app.bsky.authViewAll/llms.txt). 12 + * 13 + * The Agent is configured with `.withProxy("bsky_appview", "did:web:api.bsky.app")` 14 + * so every call sends `atproto-proxy: did:web:api.bsky.app#bsky_appview`. 15 + * 16 + * Expected: every tested RPC (which are all in the permission set) succeeds. 17 + * Observed: most RPCs succeed but `app.bsky.feed.getFeedGenerator` fails with 18 + * 19 + * XRPCError: Missing required scope 20 + * "rpc:app.bsky.feed.getFeedGenerator?aud=did:web:api.bsky.app" 21 + * 22 + * The required scope reported by the AS uses the BARE appview DID (no 23 + * `#bsky_appview` service fragment), ignoring the `atproto-proxy` target. 24 + * Granting the bare-aud form on the consent screen is impossible because 25 + * Bluesky dedupes two `include:` tokens with the same NSID, keeping only 26 + * the fragmented-aud form. 27 + * 28 + * Usage: 29 + * pnpm install 30 + * pnpm dev 31 + * # visit http://127.0.0.1:3000/ 32 + */ 33 + 34 + import { Agent } from "@atproto/api"; 35 + import { JoseKey } from "@atproto/jwk-jose"; 36 + import { 37 + NodeOAuthClient, 38 + type NodeSavedSession, 39 + type NodeSavedSessionStore, 40 + type NodeSavedState, 41 + type NodeSavedStateStore, 42 + } from "@atproto/oauth-client-node"; 43 + import { serve } from "@hono/node-server"; 44 + import { Hono } from "hono"; 45 + import { exportPKCS8, generateKeyPair } from "jose"; 46 + 47 + const PORT = 3000; 48 + const BASE_URL = `http://127.0.0.1:${PORT}`; 49 + 50 + const SCOPE = 51 + "atproto include:app.bsky.authViewAll?aud=did:web:api.bsky.app%23bsky_appview"; 52 + 53 + const enc = encodeURIComponent; 54 + const CLIENT_ID = `http://localhost?redirect_uri=${enc( 55 + `${BASE_URL}/callback`, 56 + )}&scope=${enc(SCOPE)}`; 57 + 58 + // In-memory stores — fine for a single-process repro. 59 + const stateRows = new Map<string, NodeSavedState>(); 60 + const sessionRows = new Map<string, NodeSavedSession>(); 61 + 62 + class StateStore implements NodeSavedStateStore { 63 + async get(k: string) { 64 + return stateRows.get(k); 65 + } 66 + async set(k: string, v: NodeSavedState) { 67 + stateRows.set(k, v); 68 + } 69 + async del(k: string) { 70 + stateRows.delete(k); 71 + } 72 + } 73 + 74 + class SessionStore implements NodeSavedSessionStore { 75 + async get(k: string) { 76 + return sessionRows.get(k); 77 + } 78 + async set(k: string, v: NodeSavedSession) { 79 + sessionRows.set(k, v); 80 + } 81 + async del(k: string) { 82 + sessionRows.delete(k); 83 + } 84 + } 85 + 86 + // Generate one ephemeral ES256 keypair per process start for private_key_jwt 87 + // client auth. JWKs are served at /jwks.json so the AS can verify. 88 + const { privateKey } = await generateKeyPair("ES256", { extractable: true }); 89 + const pkcs8 = await exportPKCS8(privateKey); 90 + const key = await JoseKey.fromImportable(pkcs8, "key1"); 91 + 92 + const client = new NodeOAuthClient({ 93 + clientMetadata: { 94 + client_name: "authViewAll aud-bug repro", 95 + client_id: CLIENT_ID, 96 + client_uri: BASE_URL, 97 + jwks_uri: `${BASE_URL}/jwks.json`, 98 + redirect_uris: [`${BASE_URL}/callback`], 99 + scope: SCOPE, 100 + grant_types: ["authorization_code", "refresh_token"], 101 + response_types: ["code"], 102 + application_type: "web", 103 + token_endpoint_auth_method: "private_key_jwt", 104 + token_endpoint_auth_signing_alg: "ES256", 105 + dpop_bound_access_tokens: true, 106 + }, 107 + keyset: [key], 108 + stateStore: new StateStore(), 109 + sessionStore: new SessionStore(), 110 + }); 111 + 112 + // Track the most-recently-logged-in DID so /test can restore without a cookie. 113 + let lastDid: string | null = null; 114 + 115 + const app = new Hono(); 116 + 117 + app.get("/", (c) => { 118 + return c.html(`<!doctype html> 119 + <meta charset="utf-8" /> 120 + <title>authViewAll aud-bug repro</title> 121 + <style>body{font-family:system-ui;max-width:40em;margin:2em auto;padding:0 1em;line-height:1.5}input,button{font-size:1em;padding:.4em}code{background:#eee;padding:.15em .35em;border-radius:3px}</style> 122 + <h1>authViewAll aud enforcement repro</h1> 123 + <p>Scope: <code>${SCOPE}</code></p> 124 + <form action="/login" method="POST"> 125 + <input name="handle" placeholder="your.bsky.social handle" required /> 126 + <button>Log in with Bluesky</button> 127 + </form> 128 + <p>After login you will be redirected to <a href="/test">/test</a>, which calls several RPCs and reports results.</p>`); 129 + }); 130 + 131 + app.post("/login", async (c) => { 132 + const form = await c.req.parseBody(); 133 + const handle = (form.handle as string)?.trim(); 134 + if (!handle) return c.text("handle required", 400); 135 + const url = await client.authorize(handle, { scope: SCOPE }); 136 + return c.redirect(url.toString()); 137 + }); 138 + 139 + app.get("/callback", async (c) => { 140 + const url = new URL(c.req.url); 141 + const { session } = await client.callback(url.searchParams); 142 + lastDid = session.did; 143 + return c.redirect("/test"); 144 + }); 145 + 146 + app.get("/jwks.json", (c) => c.json(client.jwks)); 147 + app.get("/client-metadata.json", (c) => c.json(client.clientMetadata)); 148 + 149 + interface TestResult { 150 + lxm: string; 151 + status: "OK" | "FAIL"; 152 + detail?: string; 153 + } 154 + 155 + app.get("/test", async (c) => { 156 + if (!lastDid) { 157 + return c.html(`<p>Not logged in. <a href="/">Log in</a> first.</p>`, 401); 158 + } 159 + 160 + const oauthSession = await client.restore(lastDid); 161 + const agent = new Agent(oauthSession).withProxy( 162 + "bsky_appview", 163 + "did:web:api.bsky.app", 164 + ); 165 + 166 + const results: TestResult[] = []; 167 + const run = async (lxm: string, fn: () => Promise<unknown>) => { 168 + try { 169 + await fn(); 170 + results.push({ lxm, status: "OK" }); 171 + } catch (e) { 172 + const detail = 173 + e instanceof Error ? `${e.constructor.name}: ${e.message}` : String(e); 174 + results.push({ lxm, status: "FAIL", detail }); 175 + } 176 + }; 177 + 178 + // Every RPC below is listed in the app.bsky.authViewAll permission set. 179 + await run("app.bsky.actor.getProfile", () => 180 + agent.app.bsky.actor.getProfile({ actor: lastDid! }), 181 + ); 182 + await run("app.bsky.feed.getTimeline", () => 183 + agent.app.bsky.feed.getTimeline({ limit: 1 }), 184 + ); 185 + await run("app.bsky.feed.getAuthorFeed", () => 186 + agent.app.bsky.feed.getAuthorFeed({ actor: lastDid!, limit: 1 }), 187 + ); 188 + await run("app.bsky.graph.getLists", () => 189 + agent.app.bsky.graph.getLists({ actor: lastDid!, limit: 1 }), 190 + ); 191 + await run("app.bsky.notification.listNotifications", () => 192 + agent.app.bsky.notification.listNotifications({ limit: 1 }), 193 + ); 194 + // "What's Hot" is a well-known public feed. 195 + await run("app.bsky.feed.getFeedGenerator", () => 196 + agent.app.bsky.feed.getFeedGenerator({ 197 + feed: "at://did:plc:z72i7hdynmk6r22z27h6tvur/app.bsky.feed.generator/whats-hot", 198 + }), 199 + ); 200 + 201 + const rows = results 202 + .map( 203 + (r) => 204 + `<tr><td><code>${r.lxm}</code></td><td class="${r.status.toLowerCase()}">${r.status}</td><td>${ 205 + r.detail ?? "" 206 + }</td></tr>`, 207 + ) 208 + .join(""); 209 + 210 + return c.html(`<!doctype html> 211 + <meta charset="utf-8" /> 212 + <title>Results — authViewAll aud-bug repro</title> 213 + <style> 214 + body{font-family:system-ui;max-width:60em;margin:2em auto;padding:0 1em;line-height:1.5} 215 + table{border-collapse:collapse;width:100%} 216 + td,th{border-bottom:1px solid #ccc;padding:.5em;text-align:left;vertical-align:top} 217 + .ok{color:#067d17;font-weight:600} 218 + .fail{color:#b3261e;font-weight:600} 219 + code{background:#eee;padding:.15em .35em;border-radius:3px} 220 + </style> 221 + <h1>RPC results for ${lastDid}</h1> 222 + <p>Scope granted: <code>${SCOPE}</code></p> 223 + <p>Agent proxy header: <code>atproto-proxy: did:web:api.bsky.app#bsky_appview</code></p> 224 + <table> 225 + <thead><tr><th>Lexicon</th><th>Status</th><th>Detail</th></tr></thead> 226 + <tbody>${rows}</tbody> 227 + </table> 228 + <p><a href="/test">Re-run tests</a></p>`); 229 + }); 230 + 231 + serve({ fetch: app.fetch, port: PORT }, () => { 232 + console.log(`\n authViewAll aud-bug repro listening on ${BASE_URL}\n`); 233 + });
+13
tsconfig.json
··· 1 + { 2 + "compilerOptions": { 3 + "target": "ES2022", 4 + "module": "ESNext", 5 + "moduleResolution": "bundler", 6 + "esModuleInterop": true, 7 + "strict": true, 8 + "skipLibCheck": true, 9 + "resolveJsonModule": true, 10 + "isolatedModules": true 11 + }, 12 + "include": ["src/**/*"] 13 + }