JWT token minting / refresh endpoints: hardening¶
Why this matters¶
If an attacker can mint (create) or refresh JSON Web Tokens (JWTs) without proving they already hold valid credentials, they effectively gain a universal “log in as anyone” capability.
This shows up in real systems when a “heartbeat”, “refresh”, or “session keepalive” endpoint:
- accepts a user/role in a request body and returns a signed JWT
- uses a weak/guessable signing key (or lets the client choose the algorithm)
- lets an incoming token choose its own issuer / JWKS discovery URL
- fails open when verification fails
- allows privilege escalation by trusting unvalidated claims (e.g.,
role=admin)
Failure modes to look for¶
1) Refresh endpoint issues tokens based on untrusted input¶
Red flags:
POST /auth/refreshreturns a new JWT when given only a username / email / user id- a “heartbeat” endpoint returns an admin token if
isAdmin: trueis in the payload
Rule: Token issuance must be tied to a server-side session state (or a refresh token) that is itself protected.
2) Refresh endpoint accepts any valid JWT and upgrades privileges¶
Common bug: the server verifies signature but then re-mints a token using request parameters (or DB fields) without re-checking authorization.
Rule: Refresh should preserve (or reduce) privilege — never increase it.
3) JWT verification weaknesses¶
Checklist:
- disallow
alg=none - ensure algorithm is fixed server-side (don’t accept algorithm from token header)
- verify signature before reading claims
- validate
iss,aud,exp,nbf(andiatwhere relevant) - bind each trusted
issto an explicit JWKS URI / key set; do not discover trust roots from untrusted token claims - reject tokens with missing required claims
4) Long-lived admin JWTs + “automation” APIs¶
If your product has administrative scripting/automation features:
- issuing a high-privilege JWT is equivalent to remote code execution (RCE) in many deployments
- a token refresh bug becomes a full-system compromise
Rule: Automation endpoints should require explicit, high-assurance auth and be segmented from normal user auth.
Safer patterns¶
Use refresh tokens (or server sessions), not “mint-by-API”¶
- Refresh token should be random, unpredictable, and stored/validated server-side.
- Bind refresh tokens to:
- user id
- device/session id
- optional client properties (risk-based)
- Rotate refresh tokens on use and revoke on suspicion.
Minimize JWT content and trust¶
- Treat JWT claims as assertions to be verified, not as a source of truth.
- Consider looking up user privileges server-side for sensitive operations.
Separate admin auth from user auth¶
- Distinct keys for different token classes (admin vs user vs service)
- Distinct audiences and issuers
- Least privilege scopes
Logging and monitoring¶
- alert on refresh/mint endpoints called without a known session
- detect sudden minting of many tokens, or minting for privileged roles
- rate limit token issuance endpoints
Quick tests for auditors¶
- Can you obtain a JWT without first completing a login flow?
- Can you set
role=admin(or similar claim) in any request and get an admin token back? - Can you replay a stale/expired token at refresh to get a new valid token?
- Does
alg=noneor algorithm confusion work?
References¶
- GitHub Advisory example: unauthenticated admin JWT minting via “heartbeat refresh” endpoint (class of issue)
- GitHub Advisory (Unity Catalog token-exchange issuer/JWKS trust-root bypass): https://github.com/advisories/GHSA-qqcj-rghw-829x