Skip to content

feat(http-utils): fan out payload scopes[] into AuthInfo with namespace + reserved-name guards (SITES-46454)#1676

Open
ravverma wants to merge 2 commits into
mainfrom
feat/jwt-scopes-fanout
Open

feat(http-utils): fan out payload scopes[] into AuthInfo with namespace + reserved-name guards (SITES-46454)#1676
ravverma wants to merge 2 commits into
mainfrom
feat/jwt-scopes-fanout

Conversation

@ravverma

@ravverma ravverma commented Jun 15, 2026

Copy link
Copy Markdown
Contributor

Summary

Phase 1 of multi-product login support (SITES-46453). Extends JwtHandler.checkAuth to map payload-side scopes[] strings (minted by spacecat-auth-service for allow-listed IMS client_ids) into first-class AuthInfo scope objects, so consumers can authorise via authInfo.hasScope(...) / checkScopes(...) — the same surface used today for admin / read_only_admin / per-tenant scopes.

Companion PRs:

  • spacecat-auth-service — mints the scope (PR)
  • spacecat-api-service — consumes the scope on GET /organizations/{id}/sites (PR; waits on this release)

Design ADR: adobe/mysticat-architecture#139
Jira: SITES-46454 (parent: SITES-46453)

Discipline (the JWT signature already prevents forgery — this is shape hygiene)

  • ALLOWED_PAYLOAD_SCOPE_NAMES — canonical allow-list. Entries outside it are dropped and warn-logged. Starts with sites:list:cross_product (the only Phase 1 scope). Grows by ADR amendment only.
  • RESERVED_SCOPE_NAMES (admin / read_only_admin / user) — NEVER sourced from payload.scopes[]. Those scopes come exclusively from the dedicated boolean / tenant claims. Prevents an accidental future mint from synthesising a privileged scope.
  • Non-string entries are dropped defensively.

Both constants are exported as the single source of truth shared with the minter.

Test plan

  • npm test -w packages/spacecat-shared-http-utils — 463 passing, 100% lines/statements coverage
  • Lint clean
  • Merge first so spacecat-shared-http-utils gets a release the api-service can pick up

🤖 Generated with Claude Code

…ce + reserved-name guards (SITES-46454)

Phase 1 of multi-product login support. Extends JwtHandler.checkAuth
to map payload-side scopes[] strings (minted by spacecat-auth-service
for allow-listed IMS client_ids) into first-class AuthInfo scope
objects, so consumers can authorise via authInfo.hasScope(...) and
checkScopes(...) — the same surface used today for admin /
read_only_admin / per-tenant scopes.

Discipline (the JWT signature already prevents forgery — this is
shape hygiene to keep the minter and consumer well-defined):

  - ALLOWED_PAYLOAD_SCOPE_NAMES is the canonical allow-list; entries
    outside it are dropped and logged at warn-level. Starts with
    sites:list:cross_product (the only Phase 1 scope).
  - RESERVED_SCOPE_NAMES (admin / read_only_admin / user) are NEVER
    sourced from payload.scopes[] — those scopes come exclusively
    from the dedicated boolean / tenant claims. This prevents an
    accidental future mint from synthesising a privileged scope.
  - Non-string entries are dropped defensively.

Both constants are exported as the single source of truth shared
with the minter (spacecat-auth-service login.js).

Design ADR: adobe/mysticat-architecture#139
Jira: SITES-46454

Tests:
- allow-listed scope flows into AuthInfo.hasScope
- unknown scopes are dropped and warn-logged
- reserved names (admin, read_only_admin, user) cannot be elevated
  via payload.scopes[]
- missing/empty scopes claim is a no-op
- non-string entries are dropped defensively

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@ravverma ravverma requested a review from MysticatBot June 15, 2026 08:39
@github-actions

Copy link
Copy Markdown

This PR will trigger a minor release when merged.

@ravverma ravverma marked this pull request as ready for review June 15, 2026 08:39
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.

1 participant