Skip to content

feat(desktop,ui): sign the embedded artist UI in so a project switch doesn't ask for a password - #96

Merged
pyramation merged 1 commit into
mainfrom
feat/desktop-auto-signin
Aug 9, 2026
Merged

feat(desktop,ui): sign the embedded artist UI in so a project switch doesn't ask for a password#96
pyramation merged 1 commit into
mainfrom
feat/desktop-auto-signin

Conversation

@pyramation

Copy link
Copy Markdown
Contributor

Summary

Switching the project in use signed you out of the embedded artist UI, because sessions are stored per project — a token minted for A is invalid the moment B is active. That stays true for anything reaching the brain over the network; it is only wrong for the UI embedded in this desktop app, where the person at the keyboard already owns the store (they can read its secrets, add users, mint access keys), so re-typing a password into their own machine protects nothing.

The desktop now mints the session itself and hands it to the view it loads:

// main/operator-session.ts
operatorToken(project) =
  admin = listUserInfos(project).find(u => u.role === 'admin') ?? users[0]   // null → login screen, as today
  session = store.createSession(project, { userAgent: 'wavegrid-desktop (this laptop)', ttlMs: 12h })
  signJwt(admin.username, { sid: session.id, role, ttlSec })

embeddedUrl(url, project) = `${url}#wg_token=${token}`

It is a real store session with a recognisable user agent, so it shows up in Access → Sessions and is revocable like any other login. The fragment (never a query) keeps the token out of the server's logs, and takeTokenFromUrl() in use-auth consumes it once and rewrites the address, preserving any other fragment the UI owns. No server change: /api/me validates this token exactly as it would one from the login form, so the handoff is transport, not privilege.

One trap worth naming: invalidateLaserView() used to call webContents.reload(), which after the strip would reload a tokenless URL and land on the new project's login screen — the exact bug being fixed. It now re-loads through embeddedUrl() with a fresh token.

Also generalized main-externals.test.ts to walk src/main/*.ts instead of a hand-maintained list, since a new main-process file importing a @wavegrid package is precisely what it guards against (and this PR adds one).

Verified against a running brain: a token minted this way returns {"ok":true,"username":"dan","role":"admin"} from /api/me. pnpm test, pnpm build, pnpm lint pass.

Link to Devin session: https://app.devin.ai/sessions/972698f89f494b86828010666a002b8f
Requested by: @pyramation

@pyramation pyramation self-assigned this Aug 9, 2026
@devin-ai-integration

Copy link
Copy Markdown
Contributor

🤖 Devin AI Engineer

I'll be helping with this pull request! Here's what you should know:

✅ I will automatically:

  • Address comments on this PR. Add '(aside)' to your comment to have me ignore it.
  • Look at CI failures and help fix them

Note: I can only respond to comments from users who have write access to this repository.

⚙️ Control Options:

  • Disable automatic comment, CI, and merge conflict monitoring

@pyramation
pyramation merged commit c1962c9 into main Aug 9, 2026
5 checks passed
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