OAuth Security Review skill for Codex

Catch the takeover before it ships. Codex reviews an OAuth or OIDC integration against the OAuth 2.1 baseline: correct flow for the client type, PKCE, exact redirect_uri matching, verified state and nonce, full ID token validation against JWKS, and tokens kept out of localStorage and URLs. It greps for legacy flows and disabled verification, traces the real account-takeover chains, and writes findings with fixes.

  • Security
  • $oauth-security-review
  • 125 lines
  • 2 files
  • ~1,600 tokens
  • MIT
  • Updated

Install

curl -fsSL codexskills.dev/install.sh \  | sh -s -- oauth-security-review

Installs to ~/.agents/skills/oauth-security-review/ for every project on this machine.

104-line POSIX script. No sudo, no telemetry. Read install.sh

Use it in Codex

Start a prompt with $oauth-security-review, or pick it from /skills. You can also just describe the task: Codex reads the description below and loads the playbook when your request matches.

What Codex reads before loading it

Reviews OAuth 2.0 and OpenID Connect implementations for the flaws that cause account takeover - wrong grant choice, missing PKCE, weak redirect_uri matching, missing or unverified state and nonce, token leakage and storage, audience and issuer validation, scope creep, and refresh-token handling. Covers clients (SPA, mobile, server) and the relying-party side of an IdP integration. Use when adding "Sign in with", wiring an OAuth/OIDC client or provider, reviewing an auth integration PR, or responding to a finding about redirect URIs, state or tokens.

Use it when

  • You are adding a social or enterprise sign-in flow
  • An auth integration PR needs a security pass
  • A finding mentions redirect URIs, state, nonce or token storage
  • You are moving off the implicit or password grant

Use something else when

How to check the result

A tampered or missing state is rejected, a lookalike redirect_uri is refused, an ID token with the wrong aud or signature is rejected, and no tokens appear in localStorage, URLs or logs.

The mistake it prevents

Decoding a JWT without verifying its signature, issuer and audience, or matching redirect_uri with startsWith so an attacker path on the same host captures the code.

~/.agents/skills/oauth-security-review/SKILL.md

--- name: oauth-security-review description: Reviews OAuth 2.0 and OpenID Connect implementations for the flaws that cause account takeover - wrong grant choice, missing PKCE, weak redirect_uri… license: MIT ---

Done well means: the implementation uses the flow current guidance recommends for its client type, every redirect and token is validated against spoofing and replay, and tokens live somewhere script and logs cannot read them. OAuth is authorization; OIDC adds authentication. Mixing them up (using an access token as proof of identity) is itself a bug.

Establish the shape first

Read the code and config before judging. Identify:

  1. Client type: SPA/browser, native/mobile, confidential server-side, or machine-to-machine.
  2. Flow in use: authorization code (+PKCE), client credentials, device code, or a legacy flow (implicit, ROPC) that should be replaced.
  3. Role: are you the client (relying party) or operating the authorization server? This review centres on the client / relying-party side.
  4. Library: prefer a maintained, certified library over hand-rolled endpoints. Hand-rolled OAuth is where most of these bugs live.
Client typeUseNot
SPA / browserAuthorization Code + PKCE, tokens handled by a backend-for-frontend where possibleImplicit (removed in OAuth 2.1), tokens in localStorage
Native / mobileAuthorization Code + PKCE, system browser (AppAuth), claimed https redirectEmbedded webview, custom-scheme without PKCE
Server web appAuthorization Code + PKCE, client secret, tokens server-sideTokens in the browser
Service-to-serviceClient credentialsA user flow with a shared service user

OAuth 2.1 consolidates current best practice: PKCE for all authorization-code clients, exact redirect-URI matching, no implicit or password grant. Review against that baseline.

Checklist

Authorization request and redirect

  • state present, unguessable, bound to the user’s session, and verified on return (CSRF defence). Reject if missing or mismatched.
  • PKCE: code_challenge with S256 (not plain) sent, code_verifier checked. Required even for confidential clients now.
  • redirect_uri registered and matched exactly by the server (scheme, host, path, no wildcards, no trailing-slash or subpath tolerance). Open-redirect-style matching is the classic account-takeover bug.
  • OIDC: nonce sent in the request and verified in the ID token to bind it to this session (replay defence).
  • No tokens or codes in the URL fragment reaching third-party scripts; no sensitive data in query strings that land in logs/referrers.

Token handling (relying party)

  • Authorization code is exchanged server-side (or via BFF), single-use, short-lived.
  • ID token validated fully: signature against the IdP’s JWKS (kid, rotate-aware), iss exact match, aud equals this client ID, exp/iat/nbf, and nonce. Never trust an unverified JWT or accept alg: none.
  • Access tokens are treated as opaque by the client unless it is the intended audience; do not read claims from someone else’s access token to make decisions.
  • The userinfo/ID-token sub (stable, per-issuer) is the account key, not email (email is mutable and can be unverified). Check email_verified before trusting email.
  • Storage: web — HttpOnly, Secure, SameSite cookies, not localStorage/sessionStorage; mobile — Keychain/Keystore, not plaintext prefs.

Scopes, refresh, logout

  • Request the least scope needed; review any admin/offline scope.
  • Refresh tokens for public clients are rotated (new refresh token each use) with reuse detection that revokes the chain; confidential clients store them server-side.
  • Token revocation on logout; back-channel logout or short access-token lifetime so revocation is effective. Clearing a cookie is not revocation.

Provider side (if you operate the AS)

  • Exact redirect-URI registration, PKCE enforced, codes single-use and short TTL, consent for third-party clients, and no token leakage via error redirects.

High-severity patterns to grep for

rg -n -i "response_type=token|grant_type=password|implicit" .          # legacy flows
rg -n -i "localStorage|sessionStorage" . | rg -i "token|access|id_token|refresh"
rg -n -i "redirect_uri" . | rg -i "startswith|includes|indexOf|regex|\\*"  # loose matching
rg -n -i "state" . | rg -i "oauth|authorize" | rg -vi "verify|compare|===|equals"  # unchecked state
rg -n -i "alg.{0,10}none|verify=false|skipVerification|insecure"       # disabled validation
rg -n -i "decode(?!.*verify)|jwt\\.decode\\(" .                         # decode without verify

Each hit is a lead; confirm against the code path before calling it a finding.

Common account-takeover chains

  • Loose redirect_uri matching + attacker-controlled path on the same host → code/token sent to the attacker.
  • Missing state → login CSRF, attacker logs victim into the attacker’s account (or vice versa).
  • Accepting an ID token whose aud is a different client, or skipping signature verification → forged identity.
  • Trusting unverified email from the IdP to match an existing local account → takeover by registering that email at a sloppy IdP.
  • Access token from one app replayed at another that does not check aud.

Verification

  • Replay an authorization response with a tampered or absent state: the client must reject it.
  • Register a lookalike redirect_uri (extra path, different case, trailing slash): the AS must refuse it.
  • Present an ID token signed by a different key or with aud set to another client: rejected.
  • Confirm tokens are absent from localStorage, URLs, and server/access logs (rg "access_token|id_token" logs).
  • For refresh rotation: use a refresh token twice and confirm the second use revokes the session.

Output template

## OAuth/OIDC review: web app "Sign in with Acme" (authorization code + PKCE)
| ID | Severity | Finding | Location | Fix |
|---|---|---|---|---|
| O-1 | Critical | ID token signature not verified (decode only) | auth/callback.ts:38 | Verify against JWKS; check iss/aud/exp/nonce |
| O-2 | High | redirect_uri matched with startsWith | auth/config.ts:12 | Exact-match against the registered set |
| O-3 | Medium | Access token in localStorage | hooks/useAuth.ts:21 | Move to HttpOnly cookie via BFF |
Baseline: OAuth 2.1 / current BCP. PKCE: present (S256). Not reviewed: the IdP's own config.

What the zip contains

SKILL.md plus the interface file below. Nothing runs on install.

agents/openai.yamlgenerated from this page

Gives the Codex app a display name, a one-line description and a starter prompt for this skill.

interface:
  display_name: "OAuth Security Review"
  short_description: "Catch the takeover before it ships"
  default_prompt: "$oauth-security-review Review our Sign in with Google integration for security issues"

About this skill

Written and maintained by the Codex Skills editors to the same editorial standard as every skill here: a selective description, a real workflow, known pitfalls and a way to verify the work. First published , last updated . MIT licensed. Something wrong or out of date? Tell us.

esc
Just launched

Codex Skills Pro is live

79 premium skills for Codex, each with scripts, references and an evidence report that proves the result.

Bought one by one$882.21
All 79 skills$49
94% off

That's $0.62 a skill, paid once.

Get all 79 for $49Or pick single skills from $4.99 →

Secure checkout by Creem