The authorization server accepts us
`nim/web/test/authorize.js`, run against the real network, gets a `request_uri` back from bsky.social — which means it fetched `client-metadata.json` from the deployed origin and took it as the authority on where a code may be sent. That is the whole premise of doing the OAuth here rather than through the broker, and it is now checked rather than assumed. It resolved `nandi.uk` to a PDS on `amanita.us-east.host.bsky.network`, which is a shard — the case the protected-resource fallback exists for, and the one that testing against bsky.social alone hid from the build before this. The fallback took it to bsky.social as the authorization server and the push went through. It stops before the reader logs in, which needs their credentials and is theirs to do. Nothing is signed in by it and nothing is spent: PAR happens before any login and the pushed request expires unused. Not part of `just test web`, which stays offline. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
d67d954 parent: 2931d32 added
nim/web/test/authorize.js +48 -0 | new file mode 100644 | ||
| @@ -0,0 +1,48 @@ | ||
| 1 | +// The browser sign-in, as far as it goes without a reader. | |
| 2 | +// | |
| 3 | +// node nim/web/test/authorize.js [handle] | |
| 4 | +// | |
| 5 | +// Not part of `just test web`, which runs offline. This one talks to the real | |
| 6 | +// network — it resolves a handle, finds the PDS, discovers the authorization | |
| 7 | +// server and pushes an authorization request — and what it proves is the one | |
| 8 | +// thing nothing local can: that the authorization server fetches our | |
| 9 | +// `client-metadata.json` from the deployed origin and accepts it. A | |
| 10 | +// `request_uri` coming back is that answer. | |
| 11 | +// | |
| 12 | +// It stops there. What follows is the reader logging in, which needs their | |
| 13 | +// credentials and is theirs to do. | |
| 14 | +// | |
| 15 | +// Nobody is signed in by this and nothing is spent: PAR happens before any | |
| 16 | +// login, and the request it pushes expires unused. | |
| 17 | +const fs = require('fs'); | |
| 18 | +let assigned = ''; | |
| 19 | +global.window = { | |
| 20 | + location: { origin: 'https://codegod100--frq-web-serve.modal.run', pathname: '/', search: '', | |
| 21 | + assign: (u) => { assigned = u; } }, | |
| 22 | + history: { replaceState() {} }, | |
| 23 | +}; | |
| 24 | +global.localStorage = { _v: {}, getItem(k) { return this._v[k] || null; }, | |
| 25 | + setItem(k, v) { this._v[k] = String(v); }, removeItem(k) { delete this._v[k]; } }; | |
| 26 | +global.crypto = require('crypto').webcrypto; | |
| 27 | +global.btoa = (s) => Buffer.from(s, 'binary').toString('base64'); | |
| 28 | +global.TextEncoder = require('util').TextEncoder; | |
| 29 | +global.URLSearchParams = URLSearchParams; | |
| 30 | +global.fetch = fetch; | |
| 31 | + | |
| 32 | +(0, eval)(fs.readFileSync('flutter/web/frq_dpop.js', 'utf8')); | |
| 33 | +(0, eval)(fs.readFileSync('flutter/web/frq_oauth.js', 'utf8')); | |
| 34 | + | |
| 35 | +(async () => { | |
| 36 | + try { | |
| 37 | + await window.frqOauth.begin(process.argv[2] || 'nandi.uk'); | |
| 38 | + const u = new URL(assigned); | |
| 39 | + console.log('authorize host:', u.host + u.pathname); | |
| 40 | + console.log('client_id:', u.searchParams.get('client_id')); | |
| 41 | + console.log('request_uri:', (u.searchParams.get('request_uri') || '').slice(0, 40) + '…'); | |
| 42 | + const pending = JSON.parse(localStorage.getItem('frq:oauth:pending')); | |
| 43 | + console.log('pending kept:', Object.keys(pending).join(', ')); | |
| 44 | + console.log('pds:', pending.pds); | |
| 45 | + } catch (e) { | |
| 46 | + console.log('FAILED:', e.message); | |
| 47 | + } | |
| 48 | +})(); | |
| new file mode 100644 | |||
| @@ -0,0 +1,48 @@ | |||
| 1 | +// The browser sign-in, as far as it goes without a reader. | ||
| 2 | +// | ||
| 3 | +// node nim/web/test/authorize.js [handle] | ||
| 4 | +// | ||
| 5 | +// Not part of `just test web`, which runs offline. This one talks to the real | ||
| 6 | +// network — it resolves a handle, finds the PDS, discovers the authorization | ||
| 7 | +// server and pushes an authorization request — and what it proves is the one | ||
| 8 | +// thing nothing local can: that the authorization server fetches our | ||
| 9 | +// `client-metadata.json` from the deployed origin and accepts it. A | ||
| 10 | +// `request_uri` coming back is that answer. | ||
| 11 | +// | ||
| 12 | +// It stops there. What follows is the reader logging in, which needs their | ||
| 13 | +// credentials and is theirs to do. | ||
| 14 | +// | ||
| 15 | +// Nobody is signed in by this and nothing is spent: PAR happens before any | ||
| 16 | +// login, and the request it pushes expires unused. | ||
| 17 | +const fs = require('fs'); | ||
| 18 | +let assigned = ''; | ||
| 19 | +global.window = { | ||
| 20 | + location: { origin: 'https://codegod100--frq-web-serve.modal.run', pathname: '/', search: '', | ||
| 21 | + assign: (u) => { assigned = u; } }, | ||
| 22 | + history: { replaceState() {} }, | ||
| 23 | +}; | ||
| 24 | +global.localStorage = { _v: {}, getItem(k) { return this._v[k] || null; }, | ||
| 25 | + setItem(k, v) { this._v[k] = String(v); }, removeItem(k) { delete this._v[k]; } }; | ||
| 26 | +global.crypto = require('crypto').webcrypto; | ||
| 27 | +global.btoa = (s) => Buffer.from(s, 'binary').toString('base64'); | ||
| 28 | +global.TextEncoder = require('util').TextEncoder; | ||
| 29 | +global.URLSearchParams = URLSearchParams; | ||
| 30 | +global.fetch = fetch; | ||
| 31 | + | ||
| 32 | +(0, eval)(fs.readFileSync('flutter/web/frq_dpop.js', 'utf8')); | ||
| 33 | +(0, eval)(fs.readFileSync('flutter/web/frq_oauth.js', 'utf8')); | ||
| 34 | + | ||
| 35 | +(async () => { | ||
| 36 | + try { | ||
| 37 | + await window.frqOauth.begin(process.argv[2] || 'nandi.uk'); | ||
| 38 | + const u = new URL(assigned); | ||
| 39 | + console.log('authorize host:', u.host + u.pathname); | ||
| 40 | + console.log('client_id:', u.searchParams.get('client_id')); | ||
| 41 | + console.log('request_uri:', (u.searchParams.get('request_uri') || '').slice(0, 40) + '…'); | ||
| 42 | + const pending = JSON.parse(localStorage.getItem('frq:oauth:pending')); | ||
| 43 | + console.log('pending kept:', Object.keys(pending).join(', ')); | ||
| 44 | + console.log('pds:', pending.pds); | ||
| 45 | + } catch (e) { | ||
| 46 | + console.log('FAILED:', e.message); | ||
| 47 | + } | ||
| 48 | +})(); | ||