| name | audit-auth |
| description | Audit authentication flow completeness — login, refresh, logout, session, RBAC, middleware. |
| user-invocable | true |
| allowed-tools | Read, Glob, Grep, Bash, Agent |
| triggers | ["audit auth","check auth flow","review authentication"] |
| capabilities | {"model_tier":"opus","danger_level":"safe","effort_hint":"medium"} |
Authentication Flow Audit
Verify the auth system works end-to-end — every edge case, every flow.
Workflow
Phase 1: Auth Flow Inventory
Map every auth-related endpoint and middleware:
- Login paths: OAuth callbacks, wallet connect, API key auth, JWT exchange
- Token management: Issue, refresh, revoke, blacklist
- Middleware: What validates tokens? Where? Edge runtime vs server?
- Session storage: Cookies (httpOnly?), localStorage, in-memory
Build a flow diagram:
Login → Token Issued → Token Stored → Token Validated → Token Refreshed → Logout → Token Revoked
Phase 2: Login Flow Audit
For each login method:
- OAuth (GitHub/Google): Are tokens in redirect query strings? (They should be in fragments or server-side sessions)
- Wallet connect: Is the signature verified server-side? Can a replayed signature authenticate?
- API keys: Are they hashed/encrypted at rest? Is lookup constant-time (bcrypt)?
- JWT exchange: Is the external JWT (Dynamic Labs, etc.) verified via JWKS with clock tolerance?
Phase 3: Token Lifecycle Audit
- Access token: How long does it live? Where is it stored? Is it httpOnly?
- Refresh token: How long? Is it single-use (rotation)? Can it be revoked?
- Logout: Does logout actually blacklist the refresh token? Or just clear the cookie?
- Expiry handling: When access expires, does the refresh flow work? What if both expire?
The critical test: After a user clicks "logout", can a captured refresh token still be used to get a new access token? If yes, logout is broken.
Phase 4: Middleware Audit
For each middleware that gates access:
- What does it validate? Cookie existence? JWT structure? JWT signature? JWT expiry?
- What happens on failure? Redirect? 401? Silent pass-through?
- Edge vs server: Edge Runtime middleware can't do crypto operations (no Node.js crypto). Is JWT validation appropriate for this runtime?
- Bypass paths: Can any route be accessed without hitting the middleware? (Check matcher config)
Phase 5: RBAC Audit
- Role definitions: Where are roles defined? admin, super_admin, user, enterprise?
- Role checks: For each admin endpoint, what exactly is checked?
user.role? user.tier? user.isAdmin?
- Privilege escalation: Can a user change their own role? Can they access admin endpoints through a different path?
- Org membership: How is org membership checked? How often? Can it be spoofed?
Phase 6: Session Security
- Cookie attributes: Secure? SameSite? Domain? Path? Max-Age?
- CSRF protection: Is there CSRF validation on state-changing BFF routes?
- Token in URL: Are tokens ever placed in URLs (query strings, fragments)?
- Token in logs: Are tokens logged by any middleware or error handler?
Output
## Authentication Flow Audit
### Flow Map
[diagram of auth flows]
### Findings
| ID | Phase | Severity | Issue | File:Line | Fix |
|----|-------|----------|-------|-----------|-----|
### Token Lifecycle
| Token | Storage | Lifetime | Rotation | Revocation |
|-------|---------|----------|----------|------------|
### Middleware Chain
| Path | Middleware | Validates | Failure Mode |
|------|-----------|-----------|-------------|
### The Logout Test
- [ ] Capture refresh token before logout
- [ ] Click logout
- [ ] Try to use captured refresh token
- [ ] Expected: 401 (token blacklisted)
- [ ] Actual: [result]
Patterns From Real Audits
These patterns were found in production codebases (constructs.network, 2026-03-14):
- Middleware checks cookie existence, not JWT validity: Any non-empty cookie passes the gate. Fix: validate JWT structure + expiry at minimum.
- Logout doesn't send refresh token to API: BFF clears the cookie client-side but never tells the server to blacklist the token. Fix: include refresh_token in logout POST body.
- OAuth tokens in redirect query string: Tokens land in server logs, browser history, Referrer headers. Fix: use server-side session or one-time authorization code.
- Role check uses
tier instead of role: Enterprise-tier users get admin access. Fix: always check user.role, never user.tier, for authorization decisions.