Repository navigation
Description
Phase 1 — Foundations. Gates the Integrations model, the management UI and subject namespacing. This is the epic's schedule risk.
Today
A token is turned into an account by looking up its sub claim alone (oidc.ts), and that lookup then filters to individual accounts. So no other account type can authenticate at all, and two issuers using the same subject string would be indistinguishable. The issuer is already pinned — to the data proxy, not Ory — so this is a subject-namespace problem, not an unvalidated-issuer one.
Do
- Add a table of identity bindings:
(issuer, subject) -> account. - Make the account lookup take both claims, for identities the platform does not own (the proxy's own issuer for API keys, GitHub Actions). The existing single-argument lookup stays as it is: an individual's Ory identity lives on the account row as
identity_id, and is not a binding. - Enforce uniqueness on the pair — two rows claiming the same
(issuer, subject)is a hard error, not last-writer-wins.
Decided 2026-09-22 (PR #565): no backfill of Ory identities, no dual-read, no index retirement. identity_id can't leave the row (session, email lookup, proxy credentials read it), so a backfill would be a second copy of the same fact; the Ory "issuer" would be a config value no token carries; and uniqueness only matters for attacker-chosen subjects, which an Ory subject never is. The bindings table holds only other issuers.
Done when
- The lookup takes
(issuer, subject)for non-Ory issuers, uniqueness enforced by the key. - Deleting an account removes its bindings.
- An Ory identity still resolves through
identity_id, untouched.
Watch
- The individual-only filter is applied in two places: the data layer and
getPageSession. Fixing only the data layer leaves cookie sessions broken for any new account type. - A new table is declared in four places, not two:
deploy/lib/database-construct.ts,scripts/init-local.ts,fixtures/, andsrc/lib/api/utils.mock.ts(whichinit-local.tsimports for seed data). - A binding's issuer must never be an empty string from unset config; the table client refuses one.
Part of #491.
Activity
feat(accounts): add the Service Account type (#563)
feat(accounts): resolve accounts by (issuer, subject)