Authorization Model Defined
Why it matters
Ad-hoc role checks scattered across route files — user.role === 'admin' repeated in a dozen places — are impossible to audit and trivially out of sync. When a new role is added or a permission is tightened, every scattered comparison must be found and updated manually. One missed file is a silent privilege escalation. CWE-284 (Improper Access Control) and OWASP A01 (Broken Access Control) both trace back to exactly this pattern: authorization decisions made without a single, auditable source of truth. NIST 800-53 AC-3 requires access enforcement to be consistently applied — string comparisons in individual handlers fail that standard by design.
Severity rationale
High because a missing or stale inline role check grants unauthorized capabilities to any authenticated user who reaches that route.
Remediation
Create lib/auth/permissions.ts as the single source of truth for all role definitions and permission checks. Export a typed hasPermission(user, action) helper and import it from every route handler — never compare role strings inline again.
// lib/auth/permissions.ts
export const ROLES = { ADMIN: 'admin', MEMBER: 'member', VIEWER: 'viewer' } as const;
export type Role = typeof ROLES[keyof typeof ROLES];
export function hasPermission(user: { role: Role }, action: string): boolean {
const permissions: Record<Role, string[]> = {
admin: ['read', 'write', 'delete', 'manage_users'],
member: ['read', 'write'],
viewer: ['read'],
};
return permissions[user.role]?.includes(action) ?? false;
}
After adding this file, grep for user.role === across the codebase and replace every hit with a hasPermission() call.
Detection
-
ID:
auth-model-defined -
Severity:
high -
What to look for: Count all relevant instances and enumerate each. Before evaluating, extract and quote any relevant configuration or UI text found. Look for a central authorization configuration file (e.g.,
lib/auth/permissions.ts,config/roles.ts), usage of RBAC libraries (casbin, accesscontrol, permit.io, casl), or a defined TypeScriptenumorconstobject for roles and permissions. Check whether API routes and middleware import from a single source for authorization decisions rather than comparing raw strings inline. -
Pass criteria: A centralized, documented authorization model exists — a specific file or library defines all roles and their permissions, and route handlers reference it rather than using ad-hoc string comparisons. At least 1 implementation must be verified. A partial or placeholder implementation does not count as pass.
-
Fail criteria: Authorization logic is purely ad hoc — raw string comparisons like
user.role === 'admin'oruser.isAdmin === trueare scattered across route files with no central definition source. -
Skip (N/A) when: Project has no authentication system detected (no auth library in dependencies, no session handling, no user concept in the data model).
-
Detail on fail:
"Authorization logic is ad hoc. Found inline role comparisons in multiple route files with no central permissions definition."(Include the count of affected files if known.) -
Remediation: Define a central source of truth for authorization. Create a
lib/auth/permissions.tsfile that exports your role definitions and ahasPermission(user, action)helper function. All route handlers should import from this file rather than comparing raw strings. This makes it easy to audit who can do what and ensures changes propagate everywhere.// lib/auth/permissions.ts export const ROLES = { ADMIN: 'admin', MEMBER: 'member', VIEWER: 'viewer' } as const; export type Role = typeof ROLES[keyof typeof ROLES]; export function hasPermission(user: { role: Role }, action: string): boolean { const permissions: Record<Role, string[]> = { admin: ['read', 'write', 'delete', 'manage_users'], member: ['read', 'write'], viewer: ['read'], }; return permissions[user.role]?.includes(action) ?? false; }After the change, verify by searching for raw role string comparisons (
user.role ===) and converting them tohasPermission()calls.
External references
- cwe · CWE-284 — Improper Access Control
- cwe · CWE-285 — Improper Authorization
- owasp:2021 · A01
- nist:rev5 · AC-3 — Access Enforcement
Taxons
History
- 2026-04-18·v1.0.0·Initial import from saas-authorization·automated