Skip to content

Commit 32a970f

Browse files
Polish pass: sync README with 0.0.2 code, small UX fixes
README had drifted from the code after the OIDC dance was dropped in cb0c996: - Configuration documents the real `resolver` param (not `idp`), with nostr.social noted as the did-nostr.com source fallback - drop the localStorage-persistence claim (that's roadmap 0.0.6) - diagram shows the DID-doc fetch + ?webid= handoff, not Solid-OIDC - remove "open index.html directly" (module scripts need http) - Status: 0.0.2 phase 1; roadmap renumbered so 0.0.2 is the identity handoff, later items shift to 0.0.3-0.0.8 login.js: - fallback-resolver link preserves ?next= (was dropped on click) - success message names the WebID and actual destination - aria-busy on the button while the flow runs index.html: - #status is a div (help() injects a <ul>; invalid inside <p>) - inline SVG favicon (kills the /favicon.ico 404)
1 parent cb0c996 commit 32a970f

3 files changed

Lines changed: 54 additions & 27 deletions

File tree

README.md

Lines changed: 40 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@ with the smallest possible UI surface — one big button.
1515
1. User clicks **Sign in**
1616
2. `window.nostr.getPublicKey()` — signer extension hands us the pubkey
1717
3. `fetch <resolver>/.well-known/did/nostr/<pubkey>.json` — DID document with `alsoKnownAs`
18-
4. Redirect to the WebID's pod root
18+
4. Redirect to the WebID's pod root with `?webid=did:nostr:<pubkey>`
19+
attached, so the pod's auth widget knows who just arrived
1920

2021
**Failure path** — at every step, if something's not set up, the
2122
page shows a precise diagnosis + the user's next concrete action
@@ -24,10 +25,12 @@ resolver, etc.). PoC philosophy: assume everything works; gracefully
2425
degrade with guidance when it doesn't.
2526

2627
**Trade-off**: the user lands at their pod *without* an
27-
authenticated Solid-OIDC session. They can browse public content;
28-
anything WAC-protected prompts for a real session via whatever app
29-
needs it. For the "magic SSO landing" promise that's enough — the
30-
full OIDC + DPoP session is on the v0.2 roadmap.
28+
authenticated session. They can browse public content; anything
29+
WAC-protected prompts for a real session via whatever app needs it.
30+
The `?webid=` handoff is plain text for now — unsigned, unverified.
31+
For the "magic SSO landing" promise that's enough — the signed
32+
handoff (NIP-98) is 0.0.2 phase 2, and the full authenticated
33+
session is 0.0.7 on the roadmap.
3134

3235
No username box. No IdP picker. No "what app are you logging in from?"
3336
question. Click → resolve → arrive.
@@ -41,7 +44,7 @@ question. Click → resolve → arrive.
4144
│ │ Sign in to Solid │ ← only UI │
4245
│ └──────────────────────┘ │
4346
└────────────────┬───────────────────────┘
44-
Solid-OIDC redirect to configured IdP
47+
fetch /.well-known/did/nostr/<pubkey>.json
4548
4649
┌────────────────────────────────────────┐
4750
│ JSS resolver chain (already shipped) │
@@ -55,6 +58,7 @@ question. Click → resolve → arrive.
5558
5659
┌────────────────────────────────────────┐
5760
│ Redirect to pod root │
61+
│ + ?webid=did:nostr:<pubkey> │
5862
└────────────────────────────────────────┘
5963
```
6064

@@ -71,24 +75,31 @@ npx serve . # any port
7175
python3 -m http.server 8080
7276
```
7377

74-
Or open `index.html` directly (CDN imports resolve over the network).
78+
(A server is required — `login.js` is an ES module, and browsers
79+
block module scripts on `file://` URLs.)
7580

7681
## Configuration
7782

78-
URL params (also persisted to `localStorage` so post-redirect navigation
79-
finds them):
83+
URL params:
8084

8185
| Param | Default | Purpose |
8286
|-------|---------|---------|
83-
| `idp` | `https://solid.social` | OIDC issuer to authenticate against |
87+
| `resolver` | `https://solid.social` | Host serving `/.well-known/did/nostr/<pubkey>.json` |
8488
| `next` | pod root derived from WebID | Where to land after sign-in |
8589

86-
Example: a Solid app wants users to authenticate and come back —
90+
[`https://nostr.social`](https://nostr.social) serves as the
91+
[did-nostr.com](https://did-nostr.com) source fallback when a key
92+
isn't linked at the default resolver.
93+
94+
Example: a Solid app wants users identified and sent back —
8795

8896
```
89-
https://sso.solid.social/?idp=https://solid.social&next=https://myapp.example/
97+
https://sso.solid.social/?resolver=https://nostr.social&next=https://myapp.example/
9098
```
9199

100+
(Resolver memory in `localStorage` — so return visits skip straight
101+
to the right host — is 0.0.6 on the roadmap.)
102+
92103
## Hosting
93104

94105
Three sensible deployment targets:
@@ -101,10 +112,11 @@ Three sensible deployment targets:
101112

102113
## Status
103114

104-
**0.0.1 — proof of concept**. Demonstrates the one-button UX
105-
end-to-end against `solid.social` as the resolver. Users registered
106-
with solid.social land at their pod in one click. Resolution uses
107-
the JSS resolver chain shipped in
115+
**0.0.2 phase 1 — identity handoff**. The one-button UX works
116+
end-to-end against `solid.social` as the resolver, and the redirect
117+
now carries `?webid=did:nostr:<pubkey>` so the pod's auth widget can
118+
pick up the arriving identity (unsigned for now; phase 2 signs it
119+
with NIP-98). Resolution uses the JSS resolver chain shipped in
108120
[#408](https://github.com/JavaScriptSolidServer/JavaScriptSolidServer/pull/408)
109121
through
110122
[#418](https://github.com/JavaScriptSolidServer/JavaScriptSolidServer/pull/418).
@@ -115,21 +127,26 @@ Following JSS's own 0.0.x versioning (small, frequent bumps, each
115127
adding one capability).
116128

117129
- **0.0.1** — One-button flow against a single hardcoded resolver. ✅
118-
- **0.0.2** — Step-by-step coaching UI: checklist that ticks each
130+
- **0.0.2** — Identity handoff to the pod. Phase 1: append
131+
`?webid=did:nostr:<pubkey>` to the redirect (plain text). ✅
132+
Phase 2: sign it (NIP-98 in the URL fragment) so the pod can
133+
verify the arriving identity.
134+
- **0.0.3** — Step-by-step coaching UI: checklist that ticks each
119135
step (extension detected, pubkey resolved, DID doc found, WebID
120136
extracted, redirect) instead of a one-shot status line.
121-
- **0.0.3** — Multi-resolver fallback: try a configurable list of
122-
resolvers in order before giving up.
123-
- **0.0.4** — Direct Nostr-relay resolution. Sits on top of
137+
- **0.0.4** — Multi-resolver fallback: try a configurable list of
138+
resolvers in order (`solid.social`, then `nostr.social` as the
139+
did-nostr.com source fallback) before giving up.
140+
- **0.0.5** — Direct Nostr-relay resolution. Sits on top of
124141
[JSS#414](https://github.com/JavaScriptSolidServer/JavaScriptSolidServer/issues/414).
125142
Removes the hardcoded-resolver dependency entirely.
126-
- **0.0.5** — User-preferred resolver memory: first successful
143+
- **0.0.6** — User-preferred resolver memory: first successful
127144
resolution is cached in `localStorage` so subsequent visits skip
128145
straight to the right host.
129-
- **0.0.6** — Authenticated session: optional NIP-98 → DPoP
146+
- **0.0.7** — Authenticated session: optional NIP-98 → DPoP
130147
exchange against the IdP, so the user lands at their pod
131148
already signed in (not just navigated there).
132-
- **0.0.7** — Passkey-only fallback. Sign in without a Nostr
149+
- **0.0.8** — Passkey-only fallback. Sign in without a Nostr
133150
extension via WebAuthn.
134151

135152
## Why a separate repo?

index.html

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
<title>Sign in to Solid</title>
66
<meta name="viewport" content="width=device-width, initial-scale=1">
77
<meta name="description" content="Universal one-click sign-in for Solid pods. Click the button — you're at your pod.">
8+
<link rel="icon" href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'%3E%3Ccircle cx='50' cy='50' r='45' fill='%236839e6'/%3E%3C/svg%3E">
89

910
<!-- Open Graph -->
1011
<meta property="og:title" content="JSS SSO — one click, you're at your pod">
@@ -79,7 +80,7 @@
7980
<h1>Sign in to Solid</h1>
8081
<p class="tagline">One click. We resolve your Nostr / WebID. You land at your pod.</p>
8182
<button id="signin">Sign in</button>
82-
<p id="status" aria-live="polite"></p>
83+
<div id="status" aria-live="polite"></div>
8384
<footer>
8485
<a href="https://github.com/JavaScriptSolidServer/sso">source</a>
8586
&nbsp;·&nbsp;

login.js

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -114,11 +114,16 @@ async function flow() {
114114
headers: { Accept: 'application/did+json, application/json' },
115115
});
116116
if (res.status === 404) {
117+
// Fallback resolver: nostr.social serves the did-nostr.com
118+
// source. Preserve the caller's other params (e.g. ?next=)
119+
// when offering the switch.
120+
const alt = new URLSearchParams(location.search);
121+
alt.set('resolver', 'https://nostr.social');
117122
setStatus(help(
118123
['Your Nostr key isn’t linked to a Solid pod at ', resolver, '. '],
119124
[
120125
{ label: 'How to link your Nostr key to a Solid pod', href: 'https://jss.live/docs/' },
121-
{ label: 'Try a different resolver', href: '?resolver=https://nostr.social' },
126+
{ label: 'Try the fallback resolver (nostr.social)', href: `?${alt}` },
122127
],
123128
), 'error');
124129
return false;
@@ -164,7 +169,7 @@ async function flow() {
164169
setStatus(`Could not build redirect URL from: ${base}`, 'error');
165170
return false;
166171
}
167-
setStatus(`Found your pod: ${webId}. Taking you there now…`);
172+
setStatus(`Found your WebID: ${webId}. Taking you to ${base}…`);
168173
setTimeout(() => { location.href = dest; }, 800);
169174
return true;
170175
}
@@ -174,8 +179,12 @@ function wire() {
174179
if (!button) return;
175180
button.addEventListener('click', async () => {
176181
button.disabled = true;
182+
button.setAttribute('aria-busy', 'true');
177183
const ok = await flow();
178-
if (!ok) button.disabled = false;
184+
if (!ok) {
185+
button.disabled = false;
186+
button.removeAttribute('aria-busy');
187+
}
179188
});
180189
}
181190

0 commit comments

Comments
 (0)