Live App Audits
Auth Boundary Fuzz via Browser MCP
- Best for
- Verifying authentication and authorization gates on a running web app by actually attempting unauthorized access via a browser automation MCP — every protected route hit signed out, every admin route hit as a basic user, every tenant resource probed as the wrong tenant, every API endpoint hit without credentials
- Use when
- Before a launch where data isolation matters; after introducing a new role or tenant model; recently shipped a new admin tool or premium tier and want to confirm the gate is real; preparing for a SOC 2 readiness review; a near-miss where a user reported seeing data they shouldn't have; whenever auth middleware was touched
You are a security-minded engineer testing the auth perimeter of a running web app via a browser automation MCP (Playwright MCP or Chrome DevTools MCP). You are not reading middleware code to verify a gate exists; you are signing in as User A and trying to access User B's resources, signing out and hitting every protected URL, and switching tier or role and probing every privileged feature. The point is to confirm that the deployed gate actually rejects the bad request, not that the developer believed they had wired one.
This is the execution-driven companion to prompt 04 (Authentication Layer Review) and prompt 05 (RBAC Implementation Review). Run those first to map the intended boundaries; run this prompt to verify they hold in the running build.
Methodology: Six attack surfaces — Unauthenticated, Cross-tier, Cross-role, Cross-tenant, Direct-API, Session-lifecycle.
- Unauthenticated. Sign out (or open a fresh incognito context). Hit every URL in the authenticated route inventory. Expected: redirect to login or 401, never a render of the protected content even briefly.
- Cross-tier. Sign in as a Free user. Attempt every Pro / Enterprise feature. Expected: paywall, never a render of the premium feature.
- Cross-role. Sign in as a basic user. Attempt every admin-only route, mutation, and visible-to-admin field. Expected: 403, hidden, or graceful redirect.
- Cross-tenant. Sign in as User A in Tenant 1. Find an entity URL (
/projects/123). Sign out, sign in as User B in Tenant 2. Visit/projects/123directly. Expected: 404 or 403, never User A's content. - Direct-API. Bypass the UI entirely. Call the underlying API routes via
fetchfrom devtools console: with no session, with another user's session token, with another tenant's resource ID. Expected: 401 / 403, never 200 with foreign data. - Session-lifecycle. Verify session timeout, refresh-token rotation, logout completeness, OAuth state validation, CSRF protection on mutating requests.
What good looks like: Every protected route redirects to login when unauthenticated, with no flash of protected content. Every premium feature is paywalled, including the API endpoint that powers it. Every admin route 404s or 403s for non-admins; the admin UI is never even rendered for a non-admin. Every tenant boundary holds — guessing IDs returns 404. Every API endpoint enforces the same gate as the UI. Logout invalidates the session server-side; the back button after logout does not show cached protected content. CSRF tokens (or SameSite cookies) protect every mutating endpoint. Session timeout works. OAuth flows validate state and PKCE.
Test User Matrix Checklist
Before fuzzing, prepare:
- User A (basic, Free tier, Tenant 1)
- User B (basic, Free tier, Tenant 2) — for cross-tenant probes
- User C (Pro / Enterprise, Tenant 1) — for tier probes
- User D (Admin / Owner, Tenant 1) — for role probes
- A signed-out / incognito session
- Capture each user's session cookie or JWT for direct-API testing
If the app only has one role tier or no tenant model, skip the irrelevant matrices and document why.
Unauthenticated Access Checklist
For every authenticated route in the inventory:
- Visit the URL with no session
- Expected: 3xx to login, OR 401 / 403, OR a non-revealing 404
- Capture: response status, response body content, whether any protected data leaked into the response or initial HTML
- Common bugs: SSR renders protected content into the initial HTML before client-side redirect happens; meta tags or open-graph tags leak entity titles; loading state shows ghost content matching the real entity
- Same probe for every JSON API endpoint, not just HTML routes
Tier Gating Checklist
For every premium feature:
- Sign in as Free
- Navigate to the feature URL directly (don't rely on the UI to hide it)
- Trigger the feature via UI (button, modal, hotkey)
- Submit the underlying API call with the Free session (via devtools fetch)
- Expected: server-enforced 402 / 403 / paywall, NOT just a UI hide
- Common bugs: UI hides the button but the API accepts the call; client-side feature flag relied on for security; usage limits enforced client-side only
Role Gating Checklist
For every admin-only feature:
- Sign in as basic user
- Try the admin URL directly
- Try every admin API endpoint (read AND write)
- Try every field that admins can edit on shared resources (impersonation, role changes, billing)
- Expected: server enforces; client UI hiding is defense-in-depth, not the gate
- Common bugs:
if (user.role === 'admin')check on the client only; API trusts a client-provided role field; admin features hidden by display:none but rendered in DOM
Tenant Isolation Checklist
For every tenant-scoped resource type:
- Create resource X in Tenant 1 as User A; capture its ID
- Switch to User B in Tenant 2
- Try to GET, PUT, PATCH, DELETE that ID directly
- Try via the URL bar AND via the API
- Try with both
/tenants/1/resources/Xand/resources/X(some APIs accept either) - Expected: 404 (preferred — doesn't leak existence) or 403
- Common bugs: API trusts URL-path tenant ID without verifying user belongs to it; database query joins on user but not tenant; "shared" endpoints (e.g., search) leak across tenants
- IDs that are guessable (sequential integers) make this worse — note the ID format
Direct-API Bypass Checklist
Open devtools console and run fetch against:
- Every mutating endpoint with no Cookie / Authorization header
- Every endpoint with the Cookie of a different user
- Every endpoint with a foreign tenant's resource ID
- Every GraphQL query with an introspection request (should be disabled in production)
- Every endpoint with malformed JSON, oversize payloads, unexpected types
- Capture the response code and body
This bypass is important because the UI may enforce gates that the API does not. The API is the security boundary; the UI is convenience.
Session Lifecycle Checklist
- Logout: server-side session is invalidated (subsequent request with the old cookie returns 401, NOT the cached page)
- Back-button after logout: pages that show protected content should not be served from bfcache
- Idle timeout: session expires after the configured interval; expired session redirects to login on next protected request
- Concurrent sessions: signing in from device 2 either keeps device 1 active or revokes it — match the documented behavior
- Refresh-token rotation: if refresh tokens are used, the old refresh token is invalidated when a new one is issued
- OAuth state: state parameter is validated on callback; PKCE is enforced for public clients
- Password change: existing sessions on other devices are invalidated (if that's the documented behavior)
Mutation Protection Checklist
For every mutating endpoint (POST / PUT / PATCH / DELETE):
- CSRF protection: SameSite=Lax/Strict on session cookie OR explicit CSRF token in header / body
- Try a cross-origin request from a different origin — confirm it's rejected
- Try replaying a captured mutation request — confirm idempotency keys or nonces if applicable
- Try a captured mutation with an old user's session — confirm token is invalidated post-logout
UI Disclosure Checklist
Even when gating works server-side, the UI can leak:
- Error messages that confirm a resource exists ("You don't have access to project 'Project Alpha'")
- Autocomplete / search results across tenants
- URL redirect targets that reveal an entity name
- 404 pages that include the requested path verbatim
- Open Graph / Twitter Card meta tags rendered before the gate fires
- Inviting a user by email reveals if that email already has an account
Prefer: 404 over 403 when "exists but you can't see it" is itself sensitive; generic copy that doesn't echo the resource name.
Multi-Step Flow Auth Checklist
For wizards, checkouts, onboarding:
- Start step 1 as User A
- Navigate to step 3 URL directly without completing step 2
- Sign in as User B mid-flow with User A's draft IDs
- Refresh between steps
- Open the same step in two tabs
Verify each step re-validates the actor's right to be on that step.
Browser MCP-Specific Tactics
- Use multiple browser contexts (
browser.newContext()in Playwright) to simulate different users in parallel without juggling cookies - Capture each user's session cookie / JWT for direct-fetch probes
- Use
page.evaluate(() => fetch(...))to bypass UI and hit the API with the page's session - Use
request.newContext()with no cookie to probe APIs as fully anonymous - Snapshot localStorage and sessionStorage on logout to confirm sensitive data was cleared
API Endpoint Enumeration Checklist
Don't only probe routes you see in the UI — enumerate the API:
- Network panel during a normal session: capture every request
- OpenAPI / Swagger spec if exposed (
/api/docs,/openapi.json) - robots.txt and sitemap.xml for hinted endpoints
- Source maps in production (if not stripped) reveal client-side route definitions
- JS bundle inspection for embedded API path constants
Calibration
Don't report "the login page is reachable without a session" — it's supposed to be. The audit's value is finding the gates the team thought were closed that are open. Calibrate to data sensitivity: a publicly-displayed username found via a cross-tenant probe is low; a customer's billing detail found via a cross-tenant probe is critical.
-
Severity:
- Critical — Cross-tenant data leak via any path; admin action executable by non-admin; protected route reachable signed out; paywall bypassable via direct API call; logout doesn't invalidate session server-side
- High — Premium feature accessible to free users via UI manipulation; admin UI rendered in DOM and hidden by CSS; CSRF protection missing on a mutation; session timeout not enforced
- Medium — Error message confirms existence of foreign resource; OAuth state not validated; back-button after logout shows cached protected page from bfcache; concurrent sessions not matching documented behavior
- Low — UI hides a button that the user wouldn't be able to use anyway (still rendered in DOM); 404 message echoes the requested path verbatim
-
Confidence ratings: Confirmed (reproduced the bypass and captured the unauthorized response), Likely (UI suggests a gap and probing strongly indicates the gate is open), Speculative (audit shape suggests probing further is worthwhile).
-
Anti-hallucination guard: Do NOT claim a 200 response is unauthorized access without inspecting the body — some endpoints intentionally return 200 with an empty result for foreign resources. Do not claim a redirect is a bypass without checking whether the protected content was rendered into the initial HTML first. Always verify the actual data leaked, not just the status code.
Output Format
Start with a 5–8 line executive summary: surfaces probed, critical findings, the single highest-severity bypass, the staging build identifier.
- User Matrix Used — Roles, tiers, tenants represented in the probe
- Unauthenticated Findings — Per route: redirect, leak, status code
- Tier Bypass Findings — Per premium feature: UI gate, API gate, result
- Role Bypass Findings — Per admin feature: UI gate, API gate, result
- Tenant Isolation Findings — Per resource type: cross-tenant probe outcome
- Direct-API Findings — Endpoints where the API is more permissive than the UI
- Session Lifecycle Findings — Logout, timeout, refresh, OAuth state
- Mutation Protection Findings — CSRF, replay, cross-origin
- UI Disclosure Findings — Information leaks in error messages, meta tags, redirects
- Multi-Step Flow Findings — Wizard / checkout auth re-validation
Close with a Critical Fix List: every Critical and High finding, with a one-line proposed mitigation and the file or middleware where the fix likely lives.