Serverless 'Continue with Google': the credential lives in the user's own Drive

Implements centralized-onramps.md §CO3.3, per the owner call that static
only is the way. No server anywhere: the host registers a Google OAuth
client ID (onramp-google-client-id) and the widget does the rest -- a
popup runs the plain implicit grant (no external script, so
script-src 'self' survives), scoped to drive.appdata only, redirecting
back to the widget page itself, which relays the token fragment to its
opener under the same origin+nonce discipline as the service handshake.

Returning users: the credential file is read from the user's app-data
folder and fed into the ordinary password login. First-timers: fresh
identity or §SF10 import, client-side registration, credential uploaded
to their own Drive -- with the no-orphan rule forcing the phrase
ceremony if the upload fails. Rotating the password later IS the
graduation step; the rotate screen says so for managed sessions, and
the dashboard states the custody plainly: your sign-in key is stored
in your own Google Drive.

driveOnramp.ts is pure and unit-tested (13 tests); new pins keep all
network I/O out of the element and constrain Drive fetches to the two
Google API constants. Hosts add https://www.googleapis.com to
connect-src and register the widget page URL as the redirect URI --
documented in the guide, llms.txt, and the design doc (v0.3), including
the recorded dependency on the legacy implicit grant.

Co-Authored-By: Claude Fable 5 <[email protected]>
This commit is contained in:
thePR0M3TH3ANandClaude Fable 5 committed 2026-08-04 11:48:51 -04:00
1 parent ff1470a849
commit 37f5bbd243
8 files changed
+628 -37

No files matched your search

+48 -2
View File
@@ -1,7 +1,10 @@
# Centralized On-Ramps — OAuth Sign-In as a Journey Toward Self-Custody
**Document version:** 0.2
**Status:** The **widget side of §CO5 is implemented** (2026-08-04):
**Document version:** 0.3
**Status:** **§CO3.3 (serverless Google, provider storage) is implemented
end to end** — it needs no service, so nothing remains to build for it
beyond a host registering an OAuth client ID. The **widget side of §CO5 is
implemented** (2026-08-04):
`onramp-url` / `onramp-name` / `onramp-providers` attributes, the popup
handshake below, "Continue with <Provider>" rows under the "Use an account
you already have" group (§CO11.1 resolved: that heading covers the whole
@@ -90,6 +93,49 @@ NIP-46 support (§LM5) is the whole client.
compatibility answer for services that already exist. Not the recommended
destination.
## CO3.3 Architecture C — provider storage: no server anywhere (implemented)
**Owner decision (2026-08-04): static only is the way.** Architecture B still
assumes *someone* runs the unlock service. For providers that expose
user-owned storage to pure browser clients, even that party disappears: the
credential of a standard capsule account lives in **the user's own cloud
storage**, readable only through the host's OAuth client ID. Google
authenticates the user; Google stores the user's own secret; the static
widget does everything else. Nobody else exists to trust, pay, or lose.
Implemented for Google (`onramp-google-client-id` attribute):
1. The "Continue with Google" row opens a popup on Google's OAuth endpoint —
plain implicit grant, `response_type=token`, no external script, so a
strict `script-src 'self'` CSP survives. Scope is `drive.appdata` only:
no identity claims, no email, minimum disclosure.
2. The registered redirect URI is **the widget page itself** — no callback
artifact to deploy. The widget instance in the popup recognizes the token
fragment, relays it to the opener (same-origin, exact state nonce),
scrubs the URL, and closes.
3. The opener reads `bitlogin-credential-v1.json` from the Drive app-data
folder: present → ordinary password login (§16); absent → first-time
setup (fresh identity or §SF10 import), client-side registration, then
the credential is uploaded. An upload failure triggers the no-orphan
rule: the phrase ceremony runs immediately.
Threat model: Google-account compromise = password compromise, exactly the
spec's §14 analysis; there is no second party to compromise. The app-data
file is plaintext by necessity — any encryption key would have to come from
the same OAuth gate it would be protecting against. Deployment notes: the
host registers its own OAuth client ID and the widget page's exact URL, and
adds `https://www.googleapis.com` to `connect-src`. Known dependency: the
implicit grant is legacy at Google; if it is ever sunset, the fallback is
Google Identity Services, which requires relaxing the CSP for one external
script — recorded here so that trade arrives as a decision, not a surprise.
The same pattern extends to Dropbox and Microsoft OneDrive (PKCE browser
flows with app-scoped storage); GitHub and Facebook offer no equivalent and
remain Architecture A/B territory.
Password rotation while Google-managed is the graduation step (CO6 step 2):
the Drive copy simply goes stale and dead, and the rotate screen says so
before the user commits.
## CO3.2 Architecture B — OAuth as the password manager (recommended)
The insight: a BitLogin account requires a high-entropy password, but the