nandi/frqpublic Fork 0
d67d954
Commits
Clone
git clone https://git.rickub.com/nandi/frq.git
git clone ssh://git@rickub.com/nandi/frq.git

Host key fingerprint (ed25519): SHA256:iycHnxEyq0Q7uyVpB7JlznP0G7JrTPXLYRcAU5CSLhc — verify it before your first connect.

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>
nandi committed 2026-09-20T10:31:29-07:00 Browse files
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+})();