Files
marketplaces/docs/backend/TRACK-S-SECURITY-RBAC-CONTRACT.md
sdarbinyan 14c72d1a6a feat: extract auth into @marketplaces/auth package, add backoffice admin provisioning spec
- ADR-0001: decision to extract auth/payment into shared @marketplaces/* packages
- Scaffold packages/auth, packages/payment; @marketplaces/auth now holds the real
  telegram (customer+admin QR/session) and ed25519 (future admin challenge/response)
  auth implementation, pushed to sources.vitanova.network/sdarbinyan/vitanovaPackages
- Rewire ~30 call sites to import from @marketplaces/auth; delete migrated originals
  from core/auth, core/admin-auth, services/, models/
- Replace environment coupling with AUTH_API_URL/TELEGRAM_BOT_USERNAME injection
  tokens and isDevMode(); wired as file:packages/auth pending registry publish
- Add TRACK-S §8: bootstrap per-marketplace admin login + marketplace-scoped
  sub-admin invite/role endpoints
- Build, arch:check:boundaries, and full test suite (103/103) all green

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-18 01:05:16 +04:00

5.8 KiB

Track S Backend Contract — RBAC, Audit, Secrets, Rate Limiting

Companion to PRODUCT-PLAN-v3.1-DELIVERY-PLAN.md Track S. Covers plan §4.4, §10.

Status: ready to build. Gates the launch — this is the single most serious security gap identified in this session's audit. Today the admin role model is decorative: AdminRole and permissions exist as types, but nothing gates any button, page, or action anywhere in the app. Any authenticated admin has full access.


1. Roles (17 total, 3 scopes, per plan §4.4)

type PlatformRole = 'PLATFORM_OWNER' | 'TECH_ADMIN' | 'SECURITY_ADMIN' | 'DOMAIN_MANAGER' | 'VIEWER';

type MarketplaceRole =
  | 'MARKETPLACE_ADMIN' | 'CONTENT_MANAGER' | 'CATALOG_MANAGER' | 'ORDER_MANAGER'
  | 'FINANCE_MANAGER' | 'SUPPORT_MANAGER' | 'VIEWER';

type SellerRole =
  | 'SELLER_OWNER' | 'SELLER_CATALOG_MANAGER' | 'SELLER_ORDER_MANAGER'
  | 'SELLER_FINANCE_VIEWER' | 'SELLER_VIEWER';

SellerRole is already specified in Phase 5's contract §4 — this doc adds the platform and marketplace scopes around it.

2. Enforcement (backend-side, non-negotiable)

Every /api/admin/v2/* and /api/platform/v1/* endpoint must check (role, tenantScope) against the acting user's session — before touching data, not as a post-hoc filter. tenant scope here means: a MARKETPLACE_ADMIN for marketplace A must get a 403 (not an empty result) querying marketplace B's data, never a silently-scoped response that looks like "there's just nothing here."

GET /api/identity/v1/session/permissions   -> { role, scopes: string[], marketplaceIds: string[] }

Frontend route/action guards derive from this endpoint's response — never hardcode role logic client-side beyond hiding UI affordances (which is convenience, not security).

3. Audit log

interface AuditEvent {
  id: string;
  actor: string;
  action: string;         // e.g. 'role.changed', 'offer.price_updated', 'refund.approved'
  entityType: string;
  entityId: string;
  before?: unknown;
  after?: unknown;
  reason?: string;
  occurredAt: string;
  ip?: string;
}

Mandatory coverage (plan §10.1): permission changes, seller status changes, catalog moderation actions, price changes, payment/refund actions, manual order overrides, integration credential changes, production launch actions.

GET /api/admin/v2/audit?marketplaceId=&entityType=&actor=&from=&to=

4. Secrets

All provider/connector credentials (payment providers, external marketplace connectors, VK/MAX/Telegram bot tokens, FX source keys) live in dedicated secret storage, referenced by opaque credentialRef strings in every other contract in this series — never returned in any API response body, never logged in plaintext.

5. Rate limiting

429 response: { error: { code: 'RATE_LIMITED', retryAfterSeconds: number } }

Applies to storefront/auth/provider endpoints. Frontend currently has zero 429 handling anywhere — see BACKEND-API-REFERENCE.md §5 for the full error-envelope contract this should follow.

6. Step-up authentication

Required before: bank/payment detail changes (Phase 5 §5), production launch (Phase 9 §3 step 8), role grants at PLATFORM_OWNER/MARKETPLACE_ADMIN level, and any manual financial override (refund approval outside normal flow, price override on a live order).

7. PII minimization

Customer/seller PII is exposed only to roles that need it for their scope (e.g. FINANCE_VIEWER sees payout totals, not raw bank account numbers unless FINANCE_MANAGER+). Export endpoints (GET .../export) are themselves audit-logged actions per §3.

8. Initial admin provisioning & self-service admin management

Each marketplace ships with one bootstrap MARKETPLACE_ADMIN account, seeded at provisioning time (Phase 9 launch step):

  • login = marketplace slug (projectName)
  • password = {projectName}2026$, flagged mustChangePassword: true
  • Login succeeds but every non-auth request 403s with PASSWORD_CHANGE_REQUIRED until password is changed.
POST /api/identity/v1/session/change-password   { currentPassword, newPassword }

A MARKETPLACE_ADMIN can then provision sub-admins scoped to their own marketplace only — mirrors the seller-team invite pattern in Phase 5 (POST /api/seller/v1/team/invite):

POST /api/admin/v2/team/invite   { email, role: MarketplaceRole, marketplaceId }
GET  /api/admin/v2/team?marketplaceId=
PATCH /api/admin/v2/team/{userId}   { role }
DELETE /api/admin/v2/team/{userId}

Invariants:

  • role must be one of the MarketplaceRole set (§1) — never PlatformRole. Backend rejects any attempt to grant a platform-scope role through this endpoint (403 SCOPE_ESCALATION_DENIED).
  • marketplaceId is forced server-side to the caller's own tenant scope — request body value is ignored/validated, never trusted.
  • Every invite/role-change/removal is an audit-logged action (§3, action: 'admin_team.invited' | 'admin_team.role_changed' | 'admin_team.removed').
  • Role grants at MARKETPLACE_ADMIN level require step-up auth (§6).
  • Invited admins get their own credentials (email + set-password flow), not the shared bootstrap login — the bootstrap account is for first login only and should be rotated/retired once real admins exist.

9. What the frontend will start doing once this ships

  • Route guards and action-level permission checks across the entire backoffice — currently none exist.
  • Backoffice Audit & Security section (missing from admin nav today): role changes, sensitive actions, login/security events, exports.
  • Reconcile AdminRole (already de-duplicated to one canonical type this session) against the real 17-role table from §1.
  • 429 interceptor + retry-after UI.