Skip to content

security(backend): enforce token scopes consistently across transfer and admin routes - #143

Open
woahwhattheheck wants to merge 1 commit into
RemitFlow:mainfrom
woahwhattheheck:latch/remitflow-125-token-scopes
Open

woahwhattheheck wants to merge 1 commit into
RemitFlow:mainfrom
woahwhattheheck:latch/remitflow-125-token-scopes

Conversation

@woahwhattheheck

Copy link
Copy Markdown

Summary

Closes #125.

Enforces the documented token-scope contract consistently across transfer, user, audit, and admin surfaces so a token can perform only the actions it is granted, admin paths require an explicit scope, and unauthorized responses stay non-enumerating.

What

  • Canonical action/resource catalog in src/config/scopes.js (transfers:*, users:*, audit:read, admin:read) with a frozen route matrix.
  • Shared authz helpers (src/utils/authz.js) re-check scopes at service boundaries; route middleware remains the first gate.
  • Admin diagnostics requires explicit admin:read. Legacy ADMIN_API_KEY / X-Admin-Token is mapped onto [admin:read] so the path stays scope-gated without breaking existing operators.
  • POST /api/transfers/bulk for claim/cancel/archive/unarchive (max 50 ids) with per-id results.
  • Malformed and missing transfer ids share one 404 Transfer not found envelope (direct and bulk) so callers cannot enumerate identifiers by status or message shape.
  • Docs: docs/SCOPE_MATRIX.md, README scope table, CHANGELOG.

Acceptance criteria mapping

Criterion How addressed
Token can perform only documented actions Catalog + route middleware + service assertScopes
Unauthorized responses consistent and non-enumerating Stable 401/403 messages; identical 404 for malformed/missing transfer ids
Admin paths require explicit scopes admin:read via scoped API token or legacy key mapped to that scope
Direct / list / bulk / admin coverage Matrix rows + bulk route + admin diagnostics
Scope matrix, cross-account, bulk, malformed-ID, audit auth tests test/tokenScopes.test.js
Regression for original failure mode Transfers write token cannot reach admin; under-scoped service calls throw 403
Existing tests remain green; no skips Full npm test — 275 pass

Design tradeoffs / compatibility

  • No tenancy rewrite: transfer records stay globally readable to any transfers:read token (preserves cursor actor-binding tests and the existing shared-data demo model). Cross-surface isolation (transfers token ↛ admin/audit/users writes) is what closes the exposure called out in security(backend): enforce token scopes consistently across transfer and admin routes #125.
  • Legacy admin key kept: still accepted, but treated as admin:read rather than a parallel auth system.
  • Service auth is opt-in via context: seed/unit callers omit auth and stay trusted; HTTP controllers always pass authFromRequest(req).

How tested

npm test
# 275 pass / 0 fail (includes new test/tokenScopes.test.js)

Coverage added: scope matrix, cross-surface isolation, bulk auth + non-enumerating per-id errors, malformed vs missing id parity, audit authorization (HTTP + service), service-boundary under-scope rejection, admin:read via API token and legacy key.

Add a canonical scope catalog, re-check scopes at service boundaries, gate
admin diagnostics on explicit admin:read (while mapping the legacy admin key),
and introduce bulk transfer mutations with non-enumerating per-id errors so
malformed and missing ids cannot be told apart.

Closes RemitFlow#125
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.

security(backend): enforce token scopes consistently across transfer and admin routes

1 participant