executing-plans
executing-plans
Use when executing an implementation plan in the current session as the implementer yourself — your human partner chose inline execution, or no subagent tool is available
audit-auth-flowsRead-only audit of app-layer auth: route×gate matrix, session lifecycle, OAuth, provider traps (getSession vs getUser, middleware-only gates, CVE-2025-29927). Use when "audit our auth" or "check middleware protection". RLS → plan-rls-audit.
Independent GitHub snapshot · Automated classification · Original documentation governs
Degree of freedom: MIXED — declared per phase
Read-only. Map the auth surface and prove, route by route, that identity is verified where it matters. Then stop. Hand fixes to a follow-up session.
2026 consensus (official, post CVE-2025-29927): Next.js middleware
runs before cache and routing. Vercel: "We do not recommend Middleware
to be the sole method of protecting routes in your application." An
audit that only reads middleware.ts and declares the app safe has
missed the point. Defense in depth: edge + route handler / server action
Every finding is recorded in this shape, in this order:
If you cannot complete Interpret from static code, it is a needs-a-probe item for Phase 5, not a conclusion.
Observe:
middleware.tsmatcher is['/dashboard/:path*']. The route/api/orders/[id]has no check in the handler. Interpret: API routes are not under the matcher, so middleware never runs on them; the order endpoint is reachable without auth. Layout-level checks don't cover direct API calls. Classify: real exposure (not merely a defense-in-depth gap — data is actually reachable). Severity: critical — unauthenticated data access. Finding row:/api/orders/[id]| edge+route | critical | matcher excludes /api; handler has nogetUser()check | probe P5-a confirmed 200 while logged out.
| Skill | Owns |
|---|---|
| audit-auth-flows (this) | Session lifecycle, route×gate matrix, OAuth, provider traps |
audit-security | OWASP checklist (injection, headers, deps) — may fix inline |
plan-security-audit | Plan-only hardening burndown |
plan-rls-audit | Data-layer who-can-read-which-rows |
plan-secrets-audit | Where service_role / keys live |
plan-privacy-compliance | Consent / policy vs collection — not session gates |
backend-patterns | Building the middleware / guard fixes after this audit |
test-exploratory | Live guest vs logged-in probe (runtime complement to this static matrix) |
Identify: provider (Supabase Auth / NextAuth / Clerk / Auth0 / custom),
framework protection (Next.js middleware + matcher, route guards,
server-action checks), session transport (cookie vs localStorage vs
Authorization header), token model (JWT vs server session). Enumerate
pages, API routes, server actions, edge functions, admin areas. Record
the pinned framework version — auth CVEs are version-gated.
Every protected route / action, one row. Apply the reasoning chain per row.
| route/action | intended access | gate that protects it | verified server-side? | data-layer backstop? |
Hunt the gaps this exposes:
config.matcher misses a protected path. API
routes and server actions often bypass page middleware — each needs
its own check.Supabase (check first on this stack):
grep -rn "getSession(" <server-code-paths>.
Treat every server-side hit as a finding until proven client-side-only.
Official JS docs: cookie/storage values may not be authentic;
getSession() is strongly advised against for authorization.
getUser() performs a network request to the Auth server and can be
used to base authorization rules. Newer SSR guides also use
getClaims() to refresh cookies — that is not a substitute for
verifying identity at the handler.plan-secrets-audit.
RLS assumed but not enforcing → plan-rls-audit (data-layer
backstop; this skill is the app layer above it).autoRefreshToken disabled without manual refresh; JWT expiry
absurdly short (<300s) hammering refresh.Next.js CVE-2025-29927 (GHSA-f82v-jwr5-mffw, critical): bypass
authorization if the check occurs only in middleware, via the internal
x-middleware-subrequest header. Patched: 12.3.5 / 13.5.9 /
14.2.25 / 15.2.3. Impacted: self-hosted next start and
output: 'standalone'. Vercel-hosted apps were incidentally
protected; Netlify and Cloudflare Workers were not affected. Workaround
if unpatched: drop x-middleware-subrequest before it hits Next.
If below the fix → P0 and a defense-in-depth finding. Also sweep
later Next.js auth CVEs against the pinned version — query the GitHub
advisory database rather than listing CVEs from memory; 29927 is not the
only one.
General rule: any "the framework enforces auth at the edge" assumption is a finding, whatever the framework.
NextAuth / Clerk / Auth0 — session strategy (JWT vs database),
callback / redirect validation, and whether auth() / session checks
actually run server-side per protected operation.
Cookies — HttpOnly, Secure, SameSite (Lax min, Strict for
sensitive); scoped path/domain; not readable by JS. Token in
localStorage is XSS-exfiltratable — flag it; CSRF-vs-XSS must be a
deliberate choice.
Logout that actually logs out — a stateless JWT stays valid until
expiry if logout only clears the client. Confirm server-side
invalidation (Supabase signOut revokes the refresh token). Probe it
(Phase 5). Official sessions FAQ: access tokens can still be presented
after sign-out unless the app also checks revocation / uses
getUser().
Rotation & timeouts — rotate on privilege change (login, role elevation) to kill fixation; idle timeout and absolute-max lifetime both present. Current norm: short access tokens, longer rotated refresh tokens with reuse detection, capped absolute session.
Refresh-token rotation + reuse detection — official Supabase sessions docs: a refresh token is single-use, with two exceptions (default 10s reuse interval — do not widen; parent-token retry for lost responses). Outside those exceptions the whole session family is revoked. Disabling that in Advanced Settings is "generally not recommended" — flag if widened or disabled.
OAuth / OIDC — state (CSRF on the callback), PKCE for public
clients, redirect-URI allowlist exact-match, provider email_verified
checked before trust, account-linking collisions handled.
Credential flows — reset tokens single-use + expiring + not leaked in URL/referrer; user enumeration via differing error text or timing on login/reset/signup; rate limiting on auth endpoints specifically; MFA state cannot be skipped mid-flow.
Authorization above the data layer — not just "is logged in" but
"is allowed"; IDOR on API params (/api/orders/:id scoped to caller?);
admin routes gated by role server-side. Name which layer each control
lives in. plan-rls-audit owns the data-layer half.
Force the failures rather than only reading code. Log each as
P5-x | expected | actual | evidence:
x-middleware-subrequest and confirm WAF/edge and a route-handler
check still stop it.Do not run these against production. Do not write exploit PoCs into the repo.
Challenge every finding against this rubric; drop or downgrade any that fail:
plan-rls-audit)?getSession()-for-authz grepped; getUser() confirmed on every
server gategetSession →
getUser, etc.)plan-rls-audit,
plan-secrets-audit, plan-security-audit, backend-patterns.
Present and stop.