Skip to content

fix(oauth): bake tenantId and tier into grant props at mint (closes #34)#41

Open
stackbilt-admin wants to merge 1 commit intomainfrom
fix/oauth-grant-tenant-tier-props-34
Open

fix(oauth): bake tenantId and tier into grant props at mint (closes #34)#41
stackbilt-admin wants to merge 1 commit intomainfrom
fix/oauth-grant-tenant-tier-props-34

Conversation

@stackbilt-admin
Copy link
Copy Markdown
Member

Summary

  • Both completeAuthorization call sites in src/oauth-handler.ts now call provisionTenant once at grant mint and bake tenantId + tier into grant props.
  • resolveAuth in src/gateway.ts reads tenantId/tier directly from props when present, skipping the per-request provisionTenant round trip. Legacy grants (neither field in props) keep falling through to the existing provisionTenant path.
  • On provisionTenant failure at mint time, the helper logs and returns {} so the grant is still minted — resolveAuth's legacy fallback covers the drop.

Why this matters

Closes #34 (severity:medium). Surfaced during the img-forge stackbilt_credits investigation: D1 showed tenant-admin-default at team tier while fresh scaffold_create receipts returned tier: "free". Root cause was grant-time vs. session-time drift — with only {userId, email, name, scopes} in props, every request re-resolved tier via provisionTenant and any divergence silently downgraded pro/enterprise callers to blessed engine templates at src/rest-scaffold.ts:148.

Test plan

  • npm run typecheck — clean
  • npm run test — 181 / 181 passing (5 new)
    • oauth-handler.test.ts — auto-approve bakes fields / consent-approve bakes fields / graceful degradation on provisionTenant failure
    • gateway.test.tsresolveAuth skips provisionTenant when props carry tenantId+tier / falls back for legacy cohort
  • After merge: verify on staging that a fresh OAuth grant's scaffold_create receipt returns the user's actual tier (not free), and that provisionTenant call volume drops (observability signal for the fix landing)

🤖 Generated with Claude Code

Both completeAuthorization call sites (auto-approve + consent-approve)
now call provisionTenant once at grant mint and thread tenantId + tier
through props, so resolveAuth reads them directly instead of re-calling
AUTH_SERVICE on every request. Eliminates the grant-time vs. session-time
tier drift that silently downgraded pro/enterprise callers to blessed
templates on scaffold_create.

Legacy grants that predate this change keep working — resolveAuth falls
through to the provisionTenant call when props carry neither tenantId
nor tier. A provisionTenant failure during mint is logged and the grant
is minted without the fields, leaving the legacy fallback to cover it
so a transient AUTH_SERVICE blip can't block OAuth consent.

Regression guards: oauth-handler.test.ts asserts both mint sites bake
the fields in and survive provisionTenant failure; gateway.test.ts
asserts resolveAuth skips the provisionTenant call when props carry the
baked values and still invokes it for the legacy cohort.

Co-Authored-By: Claude Opus 4.7 (1M context) <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.

OAuth grant props omit tenantId/tier, forcing re-resolution on every session init

1 participant