Skip to content

feat(auth): sign in through Logto (desktop PKCE + web) — hold until auth cutover - #1676

Draft
kkerti wants to merge 7 commits into
stablefrom
feat/auth-logto
Draft

kkerti wants to merge 7 commits into
stablefrom
feat/auth-logto

Conversation

@kkerti

@kkerti kkerti commented Nov 4, 2026

Copy link
Copy Markdown
Contributor

Summary

Slice 5 of the authentication rework (webapps/docs/auth-rework-plan.md, section 6). Firebase Auth leaves the editor; the main process runs the OIDC code + PKCE flow in the system browser, the web build uses @logto/browser with the same grid-editor client id.

Do not merge before the cutover: AUTH_CLIENT_ID_PROD in configuration.json is empty until the prod Logto app exists (plan, slice 8 step 1). An editor shipped from this branch today cannot sign in on the production profile cloud.

src/electron/auth.ts              openid-client (lazy import), authorize in system browser, callback on grid-editor://auth/callback,
                                  refresh token in safeStorage (auth.bin, temp+rename), scheduled refresh + resume, revoke on sign-out,
                                  IPC auth:changed { event, providerId: 'oidc', idToken } on every refresh
src/electron/main.ts              deep link routed by scheme (commandLine.find), old credential= deep link removed
src/shared/auth-provider.ts       issuer + client id per environment; VITE_AUTH_ISSUER_DEV / VITE_AUTH_CLIENT_ID_DEV overrides
src/renderer/lib/auth.store.ts    thin IPC client (desktop) / auth.web.ts (@logto/browser, navigator.locks around refresh + sign-out)
ProfileCloud.svelte               awaits the auth environment before mounting; token posted into the embed on every refresh
configuration.json                AUTH_ISSUER_*, AUTH_CLIENT_ID_* (prod id empty until seeded)

Removed: firebase, lib/firebase.js, the login/sign-up/forgot-password forms, the /authorize hand-off, PROFILE_CLOUD_EMAIL_REGISTRATION.

Evidence

  • Before: Firebase client SDK with a stale ID token (no refresh), Google sign-in via a profile-cloud page and a grid-editor://?credential= deep link.
    After: desktop harness 52/52 and web harness 28/28 against the local Logto: real sign-in as a fixture user, sub = fixture uid, aud = grid-editor client id, no nonce; forged/replayed deep links ignored; restore after SIGKILL without a browser; forced and scheduled refresh re-post the token; sign-out clears auth.bin, revokes the refresh token and opens end-session; stale client id / invalid_client / invalid_grant sign out cleanly; offline keeps the session; concurrent tab refreshes never spend a token twice.
  • Packaged macOS build (electron-builder --mac dir): starts, full sign-in and restore pass (before the final round of fixes; not rebuilt after).
  • npm test 43/43, prettier clean, grep -rn firebase src empty.
  • Two reviewer rounds, all findings resolved.

Merge Danger

Door: one-way for users (sessions move from Firebase to Logto; profile-cloud bridge is repointed at cutover).

Blast Radius: sign-in on every platform. Release-time checks still open: signed/notarized macOS build, Windows and Linux packaged sign-in incl. the second-instance deep link, Flatpak keychain after --talk-name=org.freedesktop.secrets.

🤖 Generated with Claude Code

kkerti and others added 5 commits November 4, 2026 00:50
Authorization code + PKCE with openid-client in the system browser, the
callback on grid-editor://auth/callback, the refresh token in safeStorage
under userData/auth.bin and a scheduled refresh that pushes auth:changed
to the renderer. auth.store.ts is now a thin IPC client, the Firebase SDK,
the login forms and the Google credential deep link are gone.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@logto/browser redirect flow behind the same window.electron.auth surface
as the desktop build, using the same grid-editor client id.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A locked keyring at startup should not cost the user their sign-in.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ures

Desktop: load openid-client lazily so a broken dependency only costs
sign-in, key discovery and session checks on issuer and client id, end the
session on invalid_client, keep the session when the disk write fails,
remove a stale auth.bin without a keychain, refuse a login that returns no
refresh token, guard the code exchange against environment changes, keep
error logs free of token responses and find the deep link anywhere in the
second-instance argv.

Web: tabs take turns refreshing, a tab that lost the race keeps the newer
token, a bad callback no longer signs the user out, and start() respects an
environment change.

Both: VITE_AUTH_ISSUER_DEV and VITE_AUTH_CLIENT_ID_DEV override the dev
provider. The panel sets the environment before it receives a token, and
Flatpak can reach the keychain.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…misbehave

Desktop: a file that cannot be removed no longer stops sign-out, auth.bin
is written through a temp file and rename, a new sign-in whose write fails
removes the previous account's file, a discarded code exchange is logged,
and error logs name the OAuth error and network cause.

Web: sign-out and clearing tokens take the same lock as refresh, so a
refresh in flight cannot bring the session back.

Blank VITE_AUTH_* overrides fall back to configuration.json.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@github-actions

github-actions Bot commented Nov 4, 2026 •

Copy link
Copy Markdown

Editor preview — https://pr-1676-grid-editor.intechstudio.workers.dev

Deployed from ca1c4a5. The URL stays the
same for the life of this PR; each push replaces what it serves.

@github-actions

github-actions Bot commented Nov 4, 2026

Copy link
Copy Markdown

kkerti and others added 2 commits November 4, 2026 22:38
Logto adds picture: null to the ID token for a user with no avatar, and
Firebase rejects it (auth/invalid-credential), so the profile-cloud sign-in
fails. Nothing here reads the profile claims. The web build also has to turn
off the Logto SDK's reserved scopes, which always include profile.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant