HOW TO TEST
How to Test Role-Based Access Control.
Security & Access A complete testing guide for RBAC: role definitions, admin/user/guest/read-only access, page and feature restrictions, data-level permissions, API authorization, direct-URL access, role changes, disabled users, and audit logging.
Role-based access control is the boundary that separates data between users, protects administrative functions, and enforces business rules about who can do what. A single RBAC flaw — an API endpoint missing an authorization check, a frontend route guard not enforced server-side, a role assignment that survives account deactivation — can expose every user's data to every other user. This guide covers the full RBAC surface: verifying role definitions, enforcing page/feature/action restrictions per role, testing data-level permission scoping (not just feature flags), API authorization for every sensitive endpoint, direct-URL access bypasses, role change propagation, disabled-user access, and audit log completeness. All test cases are written for a Cypress/Playwright engineer who understands that frontend route guards are cosmetic — only server-side enforcement counts.
Risks
API endpoints missing authorization checks
Frontend route guards prevent navigation to restricted pages, but direct API calls bypass them entirely. Any endpoint that performs a sensitive operation (delete, update, export) without verifying the requester's role exposes that operation to every authenticated user.
Horizontal privilege escalation via IDOR
A user who knows (or guesses) another user's resource ID can access or modify it if the API does not verify that the requesting user owns or has permission to access that specific record. BOLA (Broken Object Level Authorization) is the most common API security flaw.
Vertical privilege escalation via role parameter manipulation
If the role is sent by the client in the request body or JWT claims and the server trusts it without re-verifying against the database, an attacker can self-promote to admin by modifying the request.
Role change not immediately propagated
If a user's role is changed (demoted from admin) but their existing JWT or session remains valid and is not re-checked against the current role on each request, the demoted user retains admin access until their token expires.
Disabled accounts retaining API access
Disabling a user in the admin panel revokes UI access, but if the backend does not check the account's active status on every API request, the user can continue making API calls with their existing token indefinitely.
Data scoping failure in multi-tenant environments
In multi-tenant applications, API queries that filter by userId but not by tenantId expose data across tenant boundaries. A low-privilege user in Tenant A should never see any data from Tenant B.
Audit logs missing or forgeable
Without an immutable audit trail of who performed which sensitive operation (and when), forensic investigation after a breach is impossible, and regulatory compliance (SOC 2, GDPR, HIPAA) requirements cannot be met.
Test Scenarios
Admin role can access all pages, features, and data
CriticalfunctionalFully automatedAuthenticate as an admin. Assert all pages load without 403, all management features are visible, and all API endpoints return authorised responses.
Standard user cannot access admin-only pages or API endpoints
CriticalsecurityFully automatedAuthenticate as a standard user. Assert admin pages return 403 in the UI. Assert direct API calls to admin endpoints return 403. Assert no admin-only UI elements are rendered.
Guest/read-only role cannot perform write operations
CriticalsecurityFully automatedAuthenticate as a guest or read-only user. Assert create, update, and delete operations are blocked at both the UI (buttons hidden or disabled) and the API (403 returned for POST/PUT/DELETE requests).
Unauthenticated requests to protected endpoints return 401
CriticalsecurityFully automatedSend API requests without any authentication token to every sensitive endpoint. Assert HTTP 401 Unauthorized is returned — not 200, not 403.
User cannot access another user's resources via ID
CriticalsecurityFully automatedUser A owns resource-123. User B knows the ID. As User B: GET /api/resources/resource-123. Assert 403 or 404 — not the resource data. Test for read, update, and delete.
Direct navigation to a restricted URL is blocked server-side
CriticalsecurityFully automatedNavigate directly to /admin/users as a standard user (not via the UI navigation). Assert the page returns 403 or redirects to an unauthorized page — not a blank/partially-rendered admin page.
Role demotion is enforced on the next API request
CriticalsecurityFully automatedAuthenticate as admin (capture the token). Demote the user from admin to a standard role via the admin panel. Immediately use the same token to call an admin-only API endpoint. Assert 403 is returned.
Disabled user's token is rejected on all API endpoints
CriticalsecurityFully automatedAuthenticate as a user (capture the token). Disable the account via admin. Use the captured token to make an API call. Assert 401 or 403 — not 200.
User can only see their own data in list endpoints
CriticalsecurityFully automatedUser A creates 5 resources. User B creates 3. As User B: GET /api/resources. Assert exactly 3 resources are returned — none of User A's. As admin: assert all 8 are visible.
Sensitive operations are recorded in the audit log
HighfunctionalFully automatedAs admin: create a user, delete a resource, and change a role. Assert each action appears in the audit log with: actor (admin user ID), action, target (affected resource/user), and timestamp.
UI elements for restricted features are not rendered for lower-privilege roles
HighsecurityFully automatedAuthenticate as a standard user. Assert admin-only buttons (delete all, export all, manage roles) are not present in the DOM — not just hidden via CSS, but absent from the HTML.
Modifying the role claim in a JWT is rejected
CriticalsecurityFully automatedDecode a standard user's JWT, change the role claim to 'admin', re-sign with a different or invalid secret, and call an admin API endpoint. Assert 401 (invalid signature) is returned.
Detailed Test Cases
Preconditions
- List of all admin API endpoints documented (e.g. GET /api/admin/users, DELETE /api/admin/users/:id, POST /api/admin/roles)
- Standard user authenticated; token captured
Steps
- 1.For each admin endpoint in the list:
- 2.Send the request using the standard user's auth token
- 3.Assert HTTP 403 Forbidden
- 4.Assert response body does not contain any admin data
- 5.Assert response body does not leak admin route structure (no 'you need admin role to access /api/admin/...')
Expected result
Every admin endpoint returns 403 for a standard user; no data or route information leaked.
Evaluation criteria
- HTTP status 403 — not 200 with empty data, not 401
- Response body must not contain any records from the admin resource
- No stack trace or internal path disclosed in the error response
Edge Cases
User with multiple roles — most permissive wins?
Some systems assign multiple roles to a user. Ensure the permission model is explicit: does the user get the union of all roles' permissions, or only the most restrictive? The behaviour must be documented and enforced consistently.
Role assignment race condition
Two admins simultaneously assign conflicting roles (one promotes to admin, one demotes to read-only). The final role state must be deterministic — last-write-wins with a timestamp, not arbitrary based on query arrival order.
Accessing a resource that was shared with a deleted user
If User B was granted access to User A's resource and User B's account is deleted, what happens to that grant? The grant record must be cleaned up — otherwise a re-registered account using the same email gets inherited access.
JWT not re-validated on role change when using long-lived tokens
With JWT expiry set to 24 hours, a user whose role was changed has 23:59 of continued access at the old role level. Server-side role checks (comparing the role claim against the current DB value on each request) eliminate this window.
Read-only role can access data export endpoint
A bulk CSV export endpoint that bypasses the normal data-scoping query can expose all records to any authenticated user, not just the user's own data — particularly dangerous for read-only service accounts.
Permission check on creation endpoint does not scope the parent resource
POST /api/projects/{projectId}/tasks — the endpoint checks that the user can create tasks (role permission) but does not verify that the user has access to {projectId}. Any authenticated user can add tasks to any project by guessing the project ID.
Super-admin role not tested separately from admin
Systems with super-admin and admin roles often have subtle differences in which operations each can perform. If both roles are treated as 'admin' in tests, the super-admin-only operations are never verified.
SSRF via admin-only URL fetch feature
An admin feature that fetches a URL (screenshot, webhook test) can be exploited via SSRF if it does not restrict internal IP ranges. A compromised admin account could use it to probe internal services.
Automation Ideas
Role-endpoint permission matrix test
Define a matrix of {role, endpoint, method, expectedStatus}. Drive every combination through the API in a parameterised test. This makes permission regressions immediately visible as a row in a failing test table.
Tools: playwright, postman, rest-assured
IDOR sweep across all resource endpoints
Create resources for User A, capture all IDs, then authenticate as User B and attempt GET/PUT/DELETE for each ID. Assert 403 or 404 for every attempt. Run as a security regression on every release.
Tools: playwright, cypress
Role demotion propagation test
Capture an admin token, demote the user via the admin API, then immediately replay the admin API call with the old token. Assert 403. Automatable as a pair of sequential API calls with a < 1 s gap.
Tools: playwright, postman
JWT signature tampering test
Use Node.js (or Python) in the test runner to decode the JWT, modify the role claim, and re-encode without the correct signing key. Assert 401. Integrate as a security smoke test in CI.
Tools: playwright, cypress
DOM accessibility and role-element audit
For each user role, load the main pages and assert that role-specific DOM elements are either present or absent using specific data-testid selectors. Combine with axe-core for a full accessibility check.
Tools: playwright, axe-core
Audit log completeness assertion
After each sensitive operation (delete, role change, export), query the audit log API and assert the entry exists with all required fields. Run as a contract test to catch regressions in audit coverage.
Tools: playwright, postman
Common Bugs
Authorization check on the wrong property
The API checks if the requesting user is the owner of the resource using resource.creatorId === userId, but the resource also has a separate ownerId field. Resources where the owner differs from the creator are accessible to any user.
Impact: Silent IDOR for all resources where creatorId ≠ ownerId.
Role stored in a client-accessible cookie and trusted by the server
The user's role is stored in a non-HttpOnly cookie. JavaScript can read and modify it. If the server reads the role from the cookie without re-validating against the database, the user can self-promote.
Frontend route guard fires after the component renders
The route guard is implemented as a useEffect hook that runs after the initial render. Admin data is briefly visible before the guard fires and redirects — a flash of unauthorised content that can be captured by a screenshot.
Deleted user's API key still works
Disabling or deleting a user account does not revoke their API keys. Long-lived API keys continue to authenticate requests indefinitely, bypassing the account disable entirely.
List endpoint returns all records sorted by ID — zero data scoping
GET /api/resources returns all records in the database because the WHERE clause was forgotten during a refactor. Every authenticated user sees every other user's data.
Permission check missing on bulk delete endpoint
DELETE /api/resources correctly checks ownership per resource ID, but DELETE /api/resources?ids=1,2,3 (bulk delete) does not check ownership — any user can delete any set of resources by passing their IDs in the query.
CORS allows all origins on admin API
The admin API returns Access-Control-Allow-Origin: * instead of restricting to trusted origins. A malicious page open in the admin's browser can make cross-origin API calls to the admin endpoints using the admin's browser session.
Impact: Any website the admin visits can silently exfiltrate data or perform admin operations.
Useful Tools
End-to-end RBAC tests, role-switching between sessions, DOM element absence assertions, and JWT tampering via request interception.
API permission matrix testing — parameterised collections per role, environment-switching for auth tokens.
Intercept and replay requests with modified role claims, headers, and resource IDs. Authorisation scanner for IDOR detection.
Active scan for BOLA/IDOR, missing authorization headers, and SSRF in admin endpoints.
Verify that role-restricted UI elements have appropriate aria-hidden or are removed from the DOM, not just visually hidden.
Build the role-endpoint permission matrix as a parameterised JUnit test — readable, data-driven, and CI-friendly.
// Related resources
Glossary terms
- RBAC
- Authentication
- OAuth 2.0
- JWT
- BOLA (Broken Object Level Authorization)
- OWASP
- SSRF (Server-Side Request Forgery)
- XSS (Cross-Site Scripting)
- DAST (Dynamic Application Security Testing)
- Penetration Testing
- Accessibility
- Exploratory Testing
- Regression Test
- Smoke Test
- Status Code
- Schema Validation
- Authorization
- Access Control
- Permission
- IDOR (Insecure Direct Object Reference)
- Session
- Token
- Negative Testing