Auth Flow Reviewer
Read-only review of authentication AND authorization flows — session/token model, cookie flags, CSRF, token rotation, password-reset/email-verification, OAuth redirect/state, and per-route object-level access checks — for exploitable gaps. Use before shipping login/session/token code, when adding a protected route or sharing-by-URL feature, or during a security pass. Reports findings by severity with location, impact, and the concrete fix; never edits code.
npx agentscamp add skills/auth-flow-reviewerInstall to ~/.claude/skills/auth-flow-reviewer/SKILL.md
Read-only review of auth code: session/token model, cookie flags, CSRF, token expiry/rotation, password-reset and email-verification flows, and OAuth redirect_uri/state. Its sharpest focus is authorization — confirming every protected route enforces an object-level check, since the common real bug is a logged-in user reaching another user's data. Output: findings by severity, each with a fix.
Review authentication and authorization code for exploitable gaps without touching a line of it. The skill walks the session/token model, cookie flags, CSRF defenses, token lifecycle, password-reset and email-verification flows, and OAuth parameter validation — then spends most of its effort on the part teams routinely skip: confirming that every protected route enforces an object-level access check. A login form that works tells you nothing about whether user A can read user B's invoice by editing an ID. Output is a findings list grouped by severity, each with a location, the concrete impact, and the fix.
When to use this skill
- Before shipping login, signup, session, JWT, or refresh-token code.
- When adding a new protected route, an admin action, or a "share by link / by ID" feature — anywhere a request carries an object identifier.
- Reviewing a password-reset, email-verification, or OAuth/SSO integration.
- During a scheduled security pass, or after a pentest/bug report mentioning broken access control.
WARNING
Authentication ≠ authorization. The most common, highest-impact real bug is a fully logged-in, legitimate user accessing another user's object (IDOR) — GET /api/orders/123 returning order 123 to whoever asks, regardless of owner. If you only verify that login works, you will miss it. Audit the per-object check on every route, not just the session.
Instructions
- Map the surface first. Glob for routers, middleware, controllers, and guards (
**/routes/**,**/middleware/**,**/*controller*,**/*guard*,**/auth/**). Build a list of every endpoint and tag each as public, authenticated-only, or authorized (requires ownership/role). You cannot review what you have not enumerated — an unlisted route is the one that ships unprotected. - Classify the session model. Determine whether the app uses server-side sessions (cookie holds an opaque session id) or stateless tokens (cookie/header holds a JWT). The two have different failure modes: sessions need a server store and explicit invalidation on logout; JWTs cannot be revoked before expiry without a denylist. Flag any hybrid where a JWT is treated as revocable but no denylist exists.
- Audit cookie flags on every auth cookie. Confirm
HttpOnly(blocks JS/XSS theft),Secure(HTTPS-only), and an explicitSameSite(Laxminimum;Strictfor the session cookie when feasible;NonerequiresSecureand a CSRF defense). Grep for cookie-setting calls (set-cookie,res.cookie,Set-Cookie, framework session config) and report any auth cookie missing a flag, with the exact line. - Verify CSRF protection on state-changing requests. Any cookie-authenticated
POST/PUT/PATCH/DELETEneeds a CSRF defense: a per-session synchronizer token, double-submit cookie, or strictSameSiteplus origin checking. Token/Authorization: Bearerflows in headers are not CSRF-prone, but cookie flows are. Flag every mutating, cookie-authed endpoint with no token check or origin/referer validation. - Trace the token lifecycle. For JWTs/access tokens, check: signing algorithm is pinned (reject
alg: noneand confirm the verifier does not accept attacker-chosen algorithms), expiry is short (minutes, not days), the secret/key is not hardcoded, and the payload carries no secrets. For refresh tokens, require server-side storage, rotation on use (old token invalidated when a new one is issued), and reuse-detection that revokes the family on replay. Flag tokens stored inlocalStorage(XSS-readable) where a cookie would be safer. - Review password reset and email verification as untrusted token flows. For each, confirm: the token is high-entropy (CSPRNG, not a sequential id, timestamp, or weak hash), single-use (consumed/invalidated after first use), short-lived (minutes to an hour), and bound to the target user. Critically, confirm the flow does not enumerate users — the "reset email sent" and "verify" responses must be identical for existing and non-existing accounts (same body, same status, same timing). Flag any reset that returns "no such user".
- Validate OAuth/SSO parameters. Confirm
redirect_uriis checked against an exact-match allowlist (not a prefix/substring/regex that an attacker can satisfy withevil.com?x=trusted.com), and that thestateparameter is generated, stored, and verified on callback to stop CSRF/login-fixation. For authorization-code flows, confirm PKCE is used for public clients and the code is exchanged server-side. - Enforce object-level authorization on every protected route — the core check. For each endpoint that accepts an object id (path param, query, or body), confirm the handler verifies the current principal is allowed to act on that specific object (e.g.
WHERE owner_id = session.user.id, or an explicit policy/ability check), not merely that someone is logged in. Look for the anti-pattern: authentication middleware present, but the query fetches by id alone. Also check privilege escalation: role/permission read from the request body or a client-supplied field instead of the server-trusted session; missingisAdmingates on admin endpoints; mass-assignment that lets a user set their ownrole. - Report; do not modify. Produce the severity-grouped findings (see Output). The skill is read-only — propose the fix, leave the change to the author.
NOTE
Test the negative path in your reasoning, not just the happy path: for every "user can see their data" check, ask "what stops them from seeing someone else's?" and "what happens if I delete the auth header / swap the id / replay the token?". Findings come from the requests the code forgot to reject.
Output
A findings report grouped by severity, with a one-line scope header (what was reviewed) and, for each finding, a file:line location, the impact in attacker terms, and the concrete fix. Nothing is edited.
Auth flow review — scope: src/routes/**, src/middleware/auth.ts, src/auth/oauth.ts
12 endpoints enumerated (3 public, 5 authenticated, 4 authorized)
CRITICAL
- IDOR on invoice fetch — src/routes/invoices.ts:41
Impact: any logged-in user reads any invoice; `findById(req.params.id)` has no
owner check. GET /api/invoices/123 returns 123 to anyone.
Fix: scope the query — findOne({ id, ownerId: req.session.userId }); 404 on miss.
- Privilege escalation via body — src/routes/users.ts:88
Impact: PATCH /api/users/me accepts { role } from the request body (mass assignment);
a user can set role: "admin".
Fix: whitelist updatable fields; derive role only from server state, never the body.
HIGH
- Refresh token not rotated — src/auth/tokens.ts:53
Impact: a stolen refresh token works until expiry and is never invalidated on reuse.
Fix: rotate on each use, persist the new token, and revoke the family on replay.
- User enumeration on password reset — src/routes/auth.ts:120
Impact: reset endpoint returns 404 "no such user", letting attackers harvest valid emails.
Fix: return an identical 200 "if an account exists, an email was sent" in all cases.
MEDIUM
- Session cookie missing SameSite — src/middleware/session.ts:17
Impact: cookie sent on cross-site requests; widens CSRF exposure.
Fix: add SameSite=Lax (or Strict) alongside HttpOnly and Secure.
- OAuth redirect_uri prefix match — src/auth/oauth.ts:34
Impact: startsWith() allows https://trusted.com.evil.com — open redirect / token theft.
Fix: exact-match redirect_uri against a registered allowlist.
Summary: 2 critical, 2 high, 2 medium. No code modified.Frequently asked questions
- Why does this skill keep flagging authorization when my login works fine?
- Authentication (proving who you are) and authorization (what you may touch) are separate. A working login does nothing to stop user A from requesting user B's resource by changing an ID in the URL — that IDOR is the single most common real-world auth bug, so the skill audits every protected route's object-level check, not just the login form.
- Will it change my code?
- No. It is restricted to Read, Grep, and Glob. It produces a severity-grouped findings report with file:line locations and concrete fixes; you apply the changes.
Related
- Secret ScannerScan a repo or a diff for committed secrets — API keys, tokens, private keys, .env files, and high-entropy strings — then triage real leaks from fixtures. Use before pushing, in review, or when a credential may have leaked.
- Dependency AuditAudit project dependencies for known vulnerabilities and turn the raw scanner output into a triaged, prioritized upgrade plan. Use when an audit is noisy, a CVE was reported, or you need to know which advisories actually matter.
- Rate Limiter DesignerDesign and implement API rate limiting that actually holds under load — pick the algorithm (token bucket vs sliding-window-counter vs fixed window) and justify it, choose the limiting key and per-tier limits, use cross-instance atomic storage, and return standard 429 signals. Use when protecting an API from abuse or scrapers, enforcing per-tier quotas, or replacing an in-memory limiter that breaks behind multiple replicas.
- Webhook Handler ScaffolderScaffold a robust inbound webhook handler that verifies the signature on the raw body first, dedupes on the provider's event id, acknowledges fast, and processes asynchronously — the four things naive handlers get wrong. Use when wiring up events from a third party (Stripe, GitHub, Shopify, Slack, Twilio), when a provider keeps retrying because your endpoint times out or 500s, or when duplicate events are double-charging or double-creating records.
- RBAC DesignerDesign the authorization model itself — fine-grained permissions on resources composed into roles, with the right amount of resource/tenant scoping — instead of scattering role-name checks through handlers. Use when building multi-user or multi-tenant authorization, when `if user.isAdmin` checks are sprawling across the codebase, or when 'who can do what' needs a real model rather than ad-hoc gates.
- Security Headers HardenerAudit and harden a web app's or API's HTTP security headers — Content-Security-Policy, HSTS, X-Content-Type-Options, frame-ancestors, Referrer-Policy, Permissions-Policy, and CORS — and produce a staged rollout that won't break the site. Use before a launch, during a security pass, or when a scanner (Mozilla Observatory, securityheaders.com, a pentest) flags missing or weak headers. Audits and edits header config; rolls CSP out Report-Only first.
- Threat Model BuilderBuild a practical threat model for a feature or system using STRIDE — diagram the data flow, mark trust boundaries, enumerate concrete threats where data crosses them, and prioritize by likelihood × impact so security is reasoned about before shipping instead of bolted on after. Use when designing a feature that touches auth, money, or sensitive data, running a security design review, or hardening before a launch.
- Trace Data FlowTrace how a value, field, or variable flows through the codebase from source to sink.