asf-tooling opened a new issue, #1029:
URL: https://github.com/apache/tooling-trusted-releases/issues/1029
**ASVS Level(s):** [L1, L2]
**Description:**
### Summary
JWTs issued through the web UI (`/tokens/jwt`) are not bound to any PAT and
remain valid for their full 30-minute TTL regardless of logout or
authentication factor changes. Only JWTs with the `atr_th` (PAT hash) claim are
validated against the PAT database. The `jwtoken.issue()` function generates a
`jti` (JWT ID) claim for all JWTs, but this claim is never checked against any
denylist or revocation registry. When `jwtoken.verify()` validates a JWT, it
only checks the PAT hash (`atr_th`) if present - web-issued JWTs skip this
check entirely. This creates inconsistency: PAT-issued JWTs are immediately
revoked when PAT is deleted, but web-issued JWTs continue working for up to 30
minutes, violating ASVS 7.4.1 and 7.4.3 principles of immediate invalidation.
### Details
Affected locations:
- `atr/post/tokens.py` lines 33-41: jwt_post() issues JWT without PAT binding
- `atr/jwtoken.py` lines 42-58: issue() generates jti but no denylist check
- `atr/jwtoken.py` lines 92-126: verify() only checks PAT hash if present
Web-issued JWTs have no revocation mechanism and survive for full 30-minute
TTL.
### Recommended Remediation
**Option A (recommended):** Extend the per-user revocation timestamp
approach from SESSION-001 to cover JWTs. Update `jwtoken.verify()` to check the
user's `sessions_invalid_before` timestamp against the JWT's `iat` (issued at)
claim:
```python
def verify(token: str) -> dict:
claims = jwt.decode(token, signing_key, algorithms=['HS256'])
# Check user's revocation timestamp
user = get_user(claims['sub'])
if user.sessions_invalid_before and claims['iat'] <
user.sessions_invalid_before:
raise JWTTokenInvalid("JWT issued before revocation timestamp")
# Existing PAT hash check
if 'atr_th' in claims:
# ... existing PAT validation
return claims
```
This provides unified revocation for both web sessions and JWTs using a
single database column.
**Option B:** Require all JWTs to be issued through PATs - modify
`jwt_post()` to check if user has at least one PAT, raise BadRequest if none
exist, bind JWT to first PAT using pat_hash parameter.
**Option C:** Add JTI denylist using Redis for granular JWT revocation.
**Option D (minimal):** Reduce web-issued JWT TTL from 30 minutes to 5
minutes to limit exposure window.
### Acceptance Criteria
- [ ] Web-issued JWTs can be revoked
- [ ] Revocation is immediate or near-immediate
- [ ] Consistency with PAT-issued JWT revocation
- [ ] Test cases verify JWT revocation
- [ ] Unit test verifying the fix
### References
- Source reports: L1:7.4.1.md, L2:7.4.3.md
- Related findings: FINDING-005, FINDING-036
- ASVS sections: 7.4.1, 7.4.3
### Priority
Medium
---
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]