Repository navigation
fix(auth): scope bearer callers to their token's tenant (RIG-4066) - #1375
Merged
Merged
Conversation
|
😎 Merged successfully - details. |
|
Compass engineering docs preview: https://compass-managed-rig-4066-bea.compass-eng-docs.pages.dev Deployed from Changed pages: |
rigel-mintaka
force-pushed
the
compass-managed/rig-4066-bearer-tenant
branch
from
September 28, 2026 23:50
69d906c to
fa12f83
Compare
rigel-mintaka
marked this pull request as ready for review
September 29, 2026 04:07
This was referenced Oct 2, 2026
mattwilkinsonn
approved these changes
Oct 3, 2026
mattwilkinsonn
approved these changes
Oct 3, 2026
The bearer interceptors resolved token -> account but never set the request tenant, so every network-door RPC ran under the bootstrap tenant's RLS scope. A caller in any other tenant could not see its own account. `accounts` is FORCE-RLS, so the tenant cannot be read from the account row before the GUC is set. `tokens` stays outside RLS, so it now records the issuing tenant: PutTokenHash stamps the issuing request's tenant, ResolveTokenHash returns it on Subject.Tenant, and both bearer interceptors wrap the ctx with store.WithTenant. A token without a tenant fails closed with the same CodeUnauthenticated as any other rejection. TestBearerTokenScopesCallerToIssuingTenant drives a tenant-B token through the real network door. It fails on main (ListAccounts returns only the bootstrap admin) and passes here. Spec-impact: pending RIG-4067 (option 1, token carries tenant). Refs RIG-4066 Co-authored-by: Matt Wilkinson <matt@rigel.build>
RIG-4077 froze 0001_init.sql, and live databases never re-run it. The column now arrives in 0002: it is added nullable, existing tokens are backfilled to the bootstrap tenant (the only tenant any request path could issue under), and then it is set NOT NULL. A new pgtest upgrades a v1 database that holds a live token, and checks that the token still resolves with its tenant. Spec-impact: pending RIG-4067. Refs RIG-4066, RIG-4077 Co-authored-by: Matt Wilkinson <matt@rigel.build>
…-4066) The upgrade test seeded only the bootstrap tenant, so a backfill that ignored the slug still passed. It now seeds decoy tenants that sort before and after the bootstrap tenant by id and by age. Four slug-less selectors (by id or age, ascending or descending) now fail the test. Spec-impact: none. Refs RIG-4066 Co-authored-by: Matt Wilkinson <matt@rigel.build>
Matt ruled RIG-4067 option 1. Annotate the multitenancy record's tenant-identity bullet with the post-freeze amendment. Append DL-382: the token row carries its issuing tenant, and the interceptor scopes the request from it without BYPASSRLS. Spec-impact: docs/designs/infra/runtime/compass-managed-multitenancy/design.md, docs/designs/DECISIONS.md. Refs RIG-4066, RIG-4067 Co-authored-by: Matt Wilkinson <matt@rigel.build>
…ed id (RIG-4066) DL-382 was never claimed from the ledger allocator; the allocator issued DL-381 to RIG-4066 for this row. Spec-impact: renumbers one DECISIONS.md row and its citation in the multitenancy record; the ruling is unchanged. Refs RIG-4066 Co-authored-by: Matt Wilkinson <matt@rigel.build>
…ok 0002 (RIG-4066) main landed `0002_backfill_empty_tenant.sql` and `0003_token_usage.sql`. `loadMigrations` rejects a duplicate version, so the token tenant migration moves to the next free number. The SQL is unchanged. Spec-impact: none. Refs RIG-4066 Co-authored-by: Matt Wilkinson <matt@rigel.build>
rigel-mintaka
force-pushed
the
compass-managed/rig-4066-bearer-tenant
branch
from
October 3, 2026 18:28
a129708 to
66a33cf
Compare
mattwilkinsonn
approved these changes
Oct 3, 2026
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.
Fixes RIG-4066. Option 1 of RIG-4067 (ruled 09-28, DL-382).
Problem
BearerInterceptor/BearerStreamInterceptorresolved token → account and attached the caller, but never setstore.WithTenant.resolveTenantthen fell back to the bootstrap tenant, so every network-door RPC ran under the bootstrap tenant's RLS scope. A caller in any other tenant could not even see its own account.Change
accountsis FORCE-RLS, so the caller's tenant can't be read from the account row before the GUC exists.tokensstays outside RLS, so it now records the issuing tenant:tokens.tenant_id(NOT NULL, FKtenants) as migration0004_token_tenant.sql, backfilled to the bootstrap tenant by slug.0001_init.sqlis frozen (RIG-4077), and main already holds 0002 and 0003.Store.PutTokenHashstampsresolveTenant(ctx)(the issuing request's tenant; bootstrap at boot).ResolveTokenHashreturns it onSubject.Tenant.store.WithTenant. An empty tenant fails closed with the sameCodeUnauthenticated.No request-path BYPASSRLS read (OQ-4/N5 unchanged).
Tests
TestBearerTokenScopesCallerToIssuingTenant(server pgtest): a tenant-B token through the real network door →ListAccountsreturns the tenant-B caller and not the bootstrap admin. Red on main and red with the unaryWithTenantremoved (ListAccounts did not return issuing-tenant account).TestResolveTokenHashReturnsIssuingTenant(store pgtest).TestTokenTenantMigrationBackfillsV1Tokens(store pgtest): a v1 database with a live token upgrades through every later migration and the token resolves to the bootstrap tenant; decoy tenants pin the slug-keyed backfill.go test -tags pgtestforinternal/store,internal/auth,server/...,internal/runnerhub: all ok.go vet -tags pgtest, gofmt,sqlc diff, golangci-lint on the changed files: clean.Deploy note
store.migrateapplies 0004 on the next boot, so live databases gain the column with no recreate.