Authorization¶
Authorization is permission-based in dichit-backend: routes declare allowed roles, and ownership/scope checks run in the application layer.
Roles¶
Defined in prisma/schema.prisma (enum Role):
| Role | Description |
|---|---|
SUBSCRIBER | Individual program participant |
COMPANY | Company user / auctioneer staff |
SUPERADMIN | Platform administrator |
Route-level: authorize(roles)¶
app.get(
'/v2/admin/programs',
{
preValidation: [app.authenticate, app.authorize(['SUPERADMIN'])]
},
handler
);
authorize throws UnauthorizedError/ForbiddenError for role mismatches (see authentication.md and error-handling.md).
Application-level: ownership & scope¶
Route roles are necessary but not sufficient. For read/update/delete on tenant-scoped resources, the use case must verify ownership/scope. From AGENTS.md:
Never trust client-provided identifiers alone for access control. For update/delete operations, verify ownership, tenant/company scope, or role permissions before performing the write.
Examples of scope checks in the codebase:
company:<companyId>scopes for company resources.user:<userId>/subscriber:<id>scopes for subscriber resources.- Do not reveal whether unauthorized records exist unless the contract requires it.
WebSocket authorization¶
WS messages are authorized per event (permission checks at the router, not just role). Roles map to permissions as documented in api/websocket/README.md:
SUBSCRIBER → auction:subscribe, auction:bid:place, room:join, room:leave
COMPANY → + auction:staff, broadcast:receive
SUPERADMIN → + auction:staff, broadcast:receive
Decision flow¶
flowchart LR
R["Request"] --> A{"authenticate"}
A -->|fail| E["401 Unauthorized"]
A -->|ok| Z{"authorize(roles)"}
Z -->|fail| E
Z -->|ok| O{"ownership / scope check (use case)"}
O -->|fail| F["403 Forbidden"]
O -->|ok| H["handler"] Best practices¶
- Keep checks in the application flow, not buried in controllers.
- Combine route roles and resource ownership checks for write paths.
- Test both allowed and denied cases (see testing.md).
- Never trust client-supplied identifiers alone.