mig: add issuance selection profiles for headless callers

Merged
DA
4d ago
+176-1

AIM-597 (1 of 2)

Stacked on #8040.

Summary

  • New table issuance_selection_profiles: one profile per user session issuer and headless caller. The caller is either a client (user_session_client_id) or a workload subject (workload_issuer_id + workload_subject). Each is unique among live rows. Application code enforces that exactly one is set; no cross-column CHECK, per the repo's schema rules.
  • Tenancy is pinned with composite foreign keys:
    • (organization_id, project_id) → projects (organization_id, id).
    • (organization_id, workload_issuer_id) → workload_issuers (organization_id, id).
    • (user_session_issuer_id, user_session_client_id) → user_session_clients (user_session_issuer_id, id), so a profile's client always belongs to its issuer.
  • The issuer itself keeps a plain FK, because project-scoped issuers do not always carry organization_id. The next PR's API checks the issuer is the organization's before writing.
  • The client FK needs a new unique index, user_session_clients (user_session_issuer_id, id). Atlas emits it CONCURRENTLY, which makes this migration -- atlas:txmode none. The new table is empty and its constraints are declared inside CREATE TABLE, so there is no PG305/PG306 scan.
  • The profile's choices are upstream_client_selections rows of credential kind issuance_profile that name the profile, so there is no second choices table. In the next PR, issuance copies the choices that still validate onto each new session.
  • Schema, migration, atlas.sum and sqlc models only. Nothing reads or writes the table yet.

Motivation

RFC section 7.7, "Standard grant compatibility": unmodified jwt-bearer clients and workloads can't send per-server choices. Instead, an admin configures a profile scoped to the authenticated client or workload and the target sign-in, and its validated choices are copied onto each issued session.