me: token sign-in — /me works without an OIDC provider - #14
Merged
Conversation
The existing guides all assume docker compose on a host you control, where volumes outlive containers. On a PaaS the container is recreated on every deploy, and the image's `VOLUME /data` then resolves to an anonymous volume that goes away with it — so each deploy silently starts from an empty database and every upstream, role and grant is lost. Hit in production; the failure looks like "the admin UI didn't save my settings". Adds docs/deploy-coolify.md covering what lives on /data, the build settings (the Dockerfile is under docker/, not the repo root), why the mount must be a named volume rather than a host bind mount (the container runs as uid 1000, and a named volume inherits node:node from the image while a host directory arrives root-owned), deploying from the Coolify API, and a redeploy that proves persistence. Ends with a troubleshooting table of the failures we actually hit, including `Cannot GET /auth/login` on /me with static-token auth, a literal `bao:…` reaching an upstream, doubled `Bearer`, and the namespace charset rule. README gets a "Hosting on a container PaaS" subsection — hosting is an axis orthogonal to the three auth/secret scenarios, so it sits beside the table rather than in it. .gitignore now excludes *.local.md for stand-specific runbooks that must not be published. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The /api/me backend was already identity-agnostic: static-token principals get working prefs, personal credentials and per-user upstream sessions (proven by personal-sessions.test.ts). Only the browser layer demanded the cookie flow. me.html now renders the same sign-in panel as admin.html: "Sign in with Microsoft" when interactive login is configured (surfaced via a new `login` flag on /health) plus bearer-token paste as break-glass; the token's label is the identity. With login configured nothing changes: session-less HTML GETs of / and /me still silently redirect to /auth/login and signed-in users land straight on the page. /me?signin=token bypasses the gate for when the IdP is unavailable. Token-only deployments get / -> /me and the token-only panel. /api/me/access now emits connect: null unless the one-click Connect flow is actually mounted, so the UI never renders dead Connect buttons. BREAKING: duplicate labels across MCP_TOKENS_* are now a ConfigError. Labels key /me prefs, personal credentials and per-user upstream links — a shared label silently shared that state across tokens. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
/me("My MCP Access") previously dead-ended inCannot GET /auth/loginon deployments with static tokens only. The/api/me/*backend was already identity-agnostic — static-token principals get working prefs, personal credentials and per-user upstream sessions — the OIDC coupling lived entirely in the browser layer. This PR givesme.htmlthe same sign-in paneladmin.htmlalready has, so/meworks everywhere.sessionStorage, distinct key from the admin page). The token's label is the identity the page keys prefs/credentials by. Sign-out and 401 handling degrade cleanly without the login routes; 403 (signed in, no role) is deliberately NOT treated as sign-out — bouncing it to the IdP would loop./healthgains alogincapability flag;GET /redirects to/meon token-only deploys too. With login configured the UX is unchanged: session-less HTML GETs of/and/mestill silently redirect to/auth/login;?signin=tokenbypasses the gate for when the IdP is down.GET /api/me/accessemitsconnect: nullunless the one-click Connect flow is actually mounted (login + userConnect + secret store) — no more dead Connect buttons.MCP_TOKENS_*are now aConfigError. Labels key/meprefs, personal credentials and per-user upstream links; a shared label silently shared that state across tokens (even across roles).deploy-coolify.mdtroubleshooting row rewritten, README + CLAUDE.md updated.Includes the Coolify deployment guide commit (a409677) this builds on.
Test plan
npm test— 211 passed (9 new: label collisions,/health.login, gate regression +?signin=tokenbypass on a login-configured app, connect-metadata gating both ways)npm run buildclean/→/me; panel shows no Microsoft button; valid token signs in (bob · viewer); invalid token shows inline error; reload auto-signs-in from sessionStorage; sign-out returns to the panel without touching/auth/logouterrors🤖 Generated with Claude Code