Skip to content

[Phase 1] RBAC & Scoped API Tokens (Multi-Tenant Authorization) #67

@syed-reza98

Description

@syed-reza98

Priority: P0 (Critical)

Phase: 1 - E-Commerce Core
Estimate: 3 days
Type: Story

Context

Introduce Role-Based Access Control with per-organization scoping and scoped API tokens to prevent privilege escalation and cross-tenant data exposure.

Scope

  • Roles: OWNER, ADMIN, MANAGER, STAFF, VIEWER (enum)
  • Membership: extend existing Membership model (ensure unique (userId, organizationId))
  • Permission Matrix (draft):
    • OWNER: all + billing + destructive
    • ADMIN: all minus billing destructive
    • MANAGER: products, inventory, orders, limited staff management
    • STAFF: orders fulfillment + inventory adjust
    • VIEWER: read-only
  • API Tokens: ApiToken model (tokenHash, organizationId, scopes[], lastUsedAt, expiresAt, createdByUserId)
  • Scopes (initial): products:rw, inventory:rw, orders:rw, payments:rw, webhooks:rw, billing:r, analytics:r
  • Middleware: server-side guard builder requireScope(scopes: string[]) & requireRole(minRole) utilities
  • Prisma query pattern enforcement: ALWAYS filter by organization and authorized scope

Acceptance Criteria

  • Server actions reject access without required role/scope
  • API Token can be created by OWNER/ADMIN only
  • Token hashed (SHA-256) stored, raw shown once
  • Invalid/expired token returns 401 (never ambiguous 404 for auth boundaries)
  • Attempts logged with structured context (orgId, userId/tokenId, route, scopes)
  • Scopes enforced on at least 3 critical endpoints (product create, inventory adjustment, order read)
  • Role downgrade prevents previous elevated actions immediately

Data Model (Draft)

model ApiToken {
  id              String   @id @default(cuid())
  organizationId  String
  tokenHash       String   @unique
  name            String
  scopes          String[] // store as text[] in Postgres
  expiresAt       DateTime?
  lastUsedAt      DateTime?
  createdByUserId String
  createdAt       DateTime @default(now())
  updatedAt       DateTime @updatedAt

  @@index([organizationId])
}

Dependencies

  • Builds on: existing Membership & Organization models
  • Enables: future audit trail (#TBD Observability) & external integrations security

Metrics

  • Unauthorized access attempts captured 100%
  • Token scope mismatch error rate baseline recorded
  • Mean authorization check overhead < 2ms

Testing Checklist

  • Token with missing scope denied
  • Role downgrade reflects immediately (simulate OWNER→STAFF)
  • Expired token fails
  • Organization isolation enforced (attempt cross-org access fails)

Risk

High integrity & security impact (score: 18). Prevents cross-tenant leakage & abuse.

References

  • docs/GITHUB_ISSUES_COMPARISON_ANALYSIS.md (RBAC gap section)

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

Status

In progress

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions