---
name: auth-flow-review
description: "Reviews an authentication or session flow against the standard auth-weakness set — session fixation, missing rotation, weak reset tokens, JWT algorithm confusion, and unthrottled login. Use when auditing a login, session, JWT, or password-reset flow for security weaknesses."
metadata:
  decimalai-skill-id: "3c9bf3a5-b7d9-4d3b-9fd6-a650c71fa344"
  decimalai-version-id: "c3bceeb0-8e75-4d58-a330-b2823801fcac"
  decimalai-version: "1"
  decimalai-body-sha256: "81647a13cf373fe7eb11321610b14927c834ed1e8e7298a98560fb69c7534b3e"
  decimalai-name: "auth-flow-review"
  decimalai-format: "1"
  decimalai-invocation: "model"
  decimalai-category: "security"
  decimalai-source: "https://app.decimal.ai/s/auth-flow-review@1/SKILL.md"
---
# Auth Flow Review

Reviews a login, session, or credential-recovery flow for the specific weaknesses that
generic code review misses. A strong reviewer will catch a plaintext password or an SQL
injection on its own; the failures below are the ones that get praised-and-shipped because
the surrounding code looks correct — the login handler hashes with bcrypt but never
rotates the session, the JWT is "verified" but the algorithm is attacker-controlled, the
reset token is random-looking but reusable forever. Walk the checklist; do not free-form.

## When to Activate

Activate when someone asks you to review, audit, or check an **authentication,
session-management, or password-reset flow** — a login handler, session/cookie issuance,
JWT verification, a "forgot password" flow, an MFA/OTP check — for security weaknesses.

Do **not** activate for: writing a new auth flow from scratch (that is authoring, not
review), explaining how a mechanism works (e.g. "how does JWT signing work"), a broad
non-auth code review, general OWASP scanning of unrelated code, or live incident response.

## The auth-weakness checklist

Walk every item that the reviewed code touches. For each hit, name the weakness, the
concrete way it is exploited, and the fix. Skip an item only if the flow does not involve
it.

### 1. Session fixation

The session identifier is **not regenerated at authentication**. An attacker plants a
known session ID in the victim's browser before login; after the victim authenticates, the
same ID is now privileged and the attacker rides it.

- Look for: a login handler that validates the password but keeps the pre-login session
  object / cookie value, or sets user data onto the existing session without issuing a new
  ID.
- Fix: **regenerate the session ID on every successful login** (and destroy the old one) —
  `session.regenerate()`, `request.session.cycle_key()`, a fresh token, etc.

### 2. Missing session rotation and invalidation

The session survives privilege and credential changes when it should not.

- Look for: password change or role/privilege elevation that leaves the existing session
  (and any parallel sessions) valid; logout that only clears the client cookie without
  invalidating server-side state; no expiry / idle timeout on sessions.
- Fix: **rotate the session on privilege change**, and on password change **invalidate all
  of that user's other active sessions**. Logout must destroy server-side session state.

### 3. Weak or mishandled reset tokens

The password-reset (or email-verification / magic-link) token is guessable, reusable, or
stored/transmitted unsafely.

- Look for: tokens that are short, sequential, timestamp-derived, or built from a
  non-cryptographic source (`random`, `uuid1`, an incrementing id); tokens with **no
  expiry**; tokens that are **not single-use** (still valid after the password is changed);
  tokens **stored in plaintext** at rest or written to logs; a token in the reset URL that
  leaks via the `Referer` header or a user-controlled `Host` header.
- Fix: generate from a **cryptographically secure RNG (>= 128 bits of entropy)**, give a
  **short TTL**, **invalidate on first use** and on password change, and **store only a
  hash** of the token.

### 4. JWT algorithm confusion and skipped verification

The JWT is decoded but not properly verified, letting an attacker forge one.

- Look for: verification that **accepts `alg: none`**; a verifier that does **not pin an
  algorithm allowlist** and so honors the token's own `alg` header (letting an attacker
  swap RS256 for HS256 and sign with the public key as the HMAC secret); **decoding without
  verifying the signature** at all (`jwt.decode(..., verify=False)`, `jwt.get_unverified*`,
  base64-decoding the payload directly); and **not checking `exp` / `nbf` / `aud` / `iss`**.
- Fix: **pin the expected algorithm(s) server-side**, reject `none`, verify the signature
  against the correct key, and validate `exp`, `nbf`, and `aud`/`iss`.

### 5. Unthrottled authentication attempts

Credential and code-guessing endpoints accept unlimited tries.

- Look for: a login endpoint with **no rate limit and no lockout** (credential stuffing /
  brute force); an **MFA/OTP verify** with no attempt cap (a 6-digit code is trivially
  brute-forced without one); reset-token or session-token guessing with no throttle.
- Fix: **rate-limit per account and per source IP**, apply lockout / exponential backoff
  after repeated failures, and cap MFA/OTP attempts.

## Adjacent checks (include when the flow touches them)

- **Non-constant-time comparison** of a token, signature, or code (`==` / `!=`) leaks it
  via timing — use a constant-time compare (`hmac.compare_digest`).
- **User enumeration**: login or reset returning a **different message, status, or response
  time** for "no such account" vs "wrong password" reveals which accounts exist — keep the
  response uniform.
- **Session cookie hardening**: the session cookie missing `HttpOnly`, `Secure`, or
  `SameSite` widens session theft — set all three.

## Output shape

Go through the checklist in order. For each weakness found, state: the **weakness name**,
**where** it is in the reviewed flow, **how it is exploited** in one line, and the
**concrete fix**. If the flow correctly defends an item (e.g. it does regenerate the
session on login), you may say so briefly. Close by calling out the single
highest-severity finding. If the flow is clean against the whole set, say so plainly rather
than inventing a finding.
