Skip to content

feat(auth): pick a lab at sign-in and switch labs - #24

Merged
BackStacked merged 4 commits into
mainfrom
claude/project-thread-ovi2jd
Nov 10, 2026
Merged

BackStacked merged 4 commits into
mainfrom
claude/project-thread-ovi2jd

Conversation

@BackStacked

Copy link
Copy Markdown
Contributor

One person (one email) can have an account in several labs. This lets the dashboard show a pick list at sign-in and switch labs once signed in. The owner chose switching without asking for the password again.

Changes

  • Pick list at sign-in. ORG_REQUIRED now carries extensions.organizations, a list of [{ code, name }].
    • It lists only the labs whose account matched the password, with no ids.
    • A wrong password still lists nothing.
  • Query myOrganizations: [OrganizationChoice!]! returns { code, name, current } for every lab the person can work in.
  • Mutation switchOrganization(orgCode: String!, device: DeviceInput): LoginResult!, built on auth-kit switchOrganization.
    • It takes the lab code, not an id, so rule 3 (no orgId arguments) holds.
    • An unknown code and a lab without an account both answer ORG_UNAVAILABLE, so codes can't be probed.
    • The old session ends.
    • The same device rule as refreshSession applies.
    • The switch is audited in both labs.
  • New error code ORG_UNAVAILABLE. It is in ERROR_CODES and docs/05-errors.md.
  • Docs. docs/06 has a "One person, several labs" section. schema.graphql, docs/04 and llms-full.txt are regenerated.

Why this is a draft

It needs auth-kit 1.5.0 (mgpl-diagnostics/auth-kit#13).

  • kits.json points auth-kit at that branch's commit for npm run kits.
  • package.json and package-lock.json still pin the published auth-kit, so CI's npm ci + typecheck will fail until 1.5.0 is published.
  • After the release, run npm install @mgpl-diagnostics/auth-kit@1.5.0, set kits.json to v1.5.0, and mark this ready.
  • The client side is mgpl-diagnostics/client (1.5.0); bump its ref here after it's released.

Tests

I ran KITS_SRC=.. npm run kits, then npm run typecheck, then TEST_BACKENDS=pg npm test. The result was 127 passed and 1 failed.

  • New checks on PostgreSQL:
    • the pick list (codes and names, no ids) and nothing on a wrong password;
    • myOrganizations for a two-lab and a one-lab person;
    • a switch works in the other lab and ends the old session;
    • ORG_UNAVAILABLE for the current lab, an unknown code, or a lab without an account;
    • no session gives UNAUTHENTICATED;
    • a different device gives INVALID_TOKEN and ends the session.
  • The one failure is not from this change. It is "client: all GraphQL operations are valid": client main already selects partner-lab fields (provenance, externalReports, …) that api main doesn't serve yet. With client 1.5.0 the "every field has a client operation" check passes.
  • MongoDB didn't run here. The binary download is blocked by this environment's proxy (403); CI runs both backends.

Generated by Claude Code

…witchOrganization)

- ORG_REQUIRED errors carry extensions.organizations ({ code, name }; only the
  labs whose account matched the password, no ids).
- Query myOrganizations: every lab the signed-in person can work in, the
  current one marked.
- Mutation switchOrganization(orgCode, device): moves the session to the
  person's account in another lab without the password (auth-kit 1.5.0).
  The old session ends; same device rule as refreshSession; audited in both
  labs. New error code ORG_UNAVAILABLE.
- kits.json points auth-kit at its unreleased branch commit; package.json and
  the lockfile move to auth-kit 1.5.0 once it is published.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018jBohk4otGVRjt3Y1cF1N2

Copy link
Copy Markdown
Contributor Author

The ci check fails at npm run typecheck, and this PR can't fix that by itself.

Cause. npm ci installs the published @mgpl-diagnostics/auth-kit pinned in package-lock.json, which is 1.4.x. The errors are all about what this PR uses from auth-kit 1.5.0:

  • organizationsOf and switchOrganization are missing;
  • AuthError.details.organizations is missing;
  • the ORG_UNAVAILABLE code is missing.

Which PR has the fix. auth-kit 1.5.0 is in mgpl-diagnostics/auth-kit#13. It isn't on GitHub Packages yet, so the lockfile can't move to it from here.

Evidence. Locally, with that branch built (KITS_SRC=.. npm run kits):

  • npm run typecheck passes;
  • TEST_BACKENDS=pg npm test passes all the new lab-switching checks.

Next steps. After auth-kit#13 is merged and 1.5.0 is published:

  1. Run npm install @mgpl-diagnostics/auth-kit@1.5.0 here.
  2. Set kits.json to v1.5.0.
  3. Push. CI should then go green, and the PR can come out of draft.

I'll do those steps once the release exists.


Generated by Claude Code

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018jBohk4otGVRjt3Y1cF1N2
@BackStacked
BackStacked marked this pull request as ready for review November 10, 2026 07:29

Copy link
Copy Markdown
Contributor Author

With auth-kit 1.5.0 the auth-kit part is now green. The run has one failure left out of 320 checks:

✗ client: every query and mutation has a client operation (missing: myOrganizations, switchOrganization)

CI installs @mgpl-diagnostics/client 1.2.0 from the lockfile. The operations for the two new fields were merged in client #12 as 1.5.0, but that version isn't published yet (the latest tag is v1.4.0). Once client 1.5.0 is on GitHub Packages, I'll bump it here in package.json, package-lock and kits.json, and the check should pass.


Generated by Claude Code

Brings the myOrganizations and switchOrganization operations, so the
client-operations check passes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018jBohk4otGVRjt3Y1cF1N2

Copy link
Copy Markdown
Contributor Author

With client 1.5.0, both new operations are found now. The one failure left is the other direction of the contract check:

✗ client: all 152 GraphQL operations are valid: report: Cannot query field "provenance" on type "Report" … externalReports … importExternalReport …

Since 1.4.0, the client has carried the partner-lab import operations (externalReports, importExternalReport, Report.provenance, …). The schema behind them comes from #23, which isn't merged yet. So this PR can only pass once #23 is on main.

Merge order: #23 first (it pins client 1.4.0), then I rebase this branch onto main and CI should pass with client 1.5.0. I'm not loosening the check to get around this.


Generated by Claude Code

Conflicts in kits.json, package files, errors.ts and llms-full.txt: kept both
sides (report-reader and db-kit 2.1.1 from main; auth-kit 1.5.0, client 1.5.0
and ORG_UNAVAILABLE from this branch). Generated files regenerated.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018jBohk4otGVRjt3Y1cF1N2
@BackStacked
BackStacked merged commit 6d5e04a into main Nov 10, 2026
1 check passed
@BackStacked
BackStacked deleted the claude/project-thread-ovi2jd branch November 10, 2026 07:49
BackStacked added a commit that referenced this pull request Nov 10, 2026
…dates) (#26)

<!-- claude-projects-attribution: {"github_login":"BackStacked"} -->
_Requested by **Himanshu** · [project
thread](https://claude.ai/code/project/chan_016JXeBZ2UxSi4N5VU2C26fQ?thread=cmsg_016JXeBZ2UxSi4N5VU2C26fQU57JpX29saJ5azzjf6zEWH)_

## What

**Images per release.** `.github/workflows/release.yml`: a `v*` tag
(admin-only) or a manual run on `main` checks the tag equals
`package.json`, builds `ghcr.io/mgpl-diagnostics/api` for linux/amd64
and arm64 (tags `1.2.3`, `1.2`, `1` from 1.0, `latest`; kits read with
the workflow token as a build secret) and creates the GitHub release
with generated notes.

**Easy hosting.** `docker-compose.yml` now pulls the released api and
dashboard images (`MGPL_API_VERSION` / `MGPL_DASHBOARD_VERSION`, default
`latest`) and adds the dashboard. nginx serves both on one origin:
`/graphql` and `/health` → api replicas, the rest → dashboard, so
`TRUST_PROXY=1` stays right and there is no CORS. Building from a
checkout moved to `docker-compose.build.yml`.

**Update notices.** New query `systemUpdates(dashboardVersion, refresh)`
(permission `settings:manage`: owner and admin) compares this api's
version and the dashboard's with the newest published GitHub releases of
both repos, and returns the changelog in between plus the update steps.
One GET per repo per `UPDATE_CHECK_INTERVAL_MS` (6 h) per process,
"refresh" at most once a minute, last good answer kept on failure;
`UPDATE_CHECK=false` makes no outbound call. Feed data is typed and
capped; non-https links dropped; pre-releases and drafts ignored. New
module `system` (schema, controller, service), no change to existing
modules.

**Fix.** `health.version` read `../../package.json` relative to the
bundled chunk, so production images answered `0.0.0`; `src/version.ts`
now reads the right file in both `tsx` and `dist`.

Docs: new `docs/18-updates.md` (images, hosting, updating, releasing,
the check), config table, `.env.example`, 12/17 updated.
`schema.graphql`, API reference and `llms-full.txt` regenerated.

## Merge order
`@mgpl-diagnostics/client` 1.6.0 (mgpl-diagnostics/client PR) has the
`systemUpdates` operation, which the "every root field has a client
operation" check needs, so this PR bumps the client to `^1.6.0`. Client
main already includes the operations of #23 and #24, so: merge **#24 and
#23**, then **client 1.6.0** (tag it), then this.
`kits.json`/`package.json`/lock will conflict with #24 on the client
line; take `1.6.0`, then `npm install` to refresh the lock and `npm run
schema && npm run docs:reference && npm run llms`.

## Tests
`TEST_BACKENDS=pg npm test` with kits built from local checkouts: 16 new
`[updates]` checks pass (versions, changelog window, cache, refresh
limit, unknown dashboard version, UNAUTHENTICATED, FORBIDDEN for front
desk, 403 feed, `UPDATE_CHECK=false`, repo validation). The one failure
is the client-contract check against today's main, which lacks #23/#24's
fields (goes away in the merge order above). `docker compose config`
passes for both compose files; `nginx -t` passes; nginx routing checked
with stub containers. The image build itself runs for the first time in
CI on the first tag.

## After merging
- First release only: GitHub → org → Packages → `api` → change
visibility to Public (or `docker login ghcr.io` on servers).
- One-click updates are not included (the api never gets the Docker
socket); asked in the project thread.

---
_Generated by [Claude
Code](https://claude.ai/code/session_018jBohmSXdQwAG8EmEBKYkQ)_

---------

Co-authored-by: Claude <noreply@anthropic.com>
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.

2 participants