Add refresh-tokens module: JWT refresh token rotation, token families and reuse detection on Spring Security 7.1
Co-Authored-By: Claude Sonnet 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_017ic4b6XzpJxZ25cDyuw1Uk
This commit is contained in:
@@ -0,0 +1,27 @@
|
||||
# refresh-tokens
|
||||
|
||||
Companion code for [JWT Refresh Token Rotation in Spring Security 7 (With Reuse Detection)](https://ankurm.com/spring-security-jwt-refresh-token-rotation-reuse-detection/).
|
||||
|
||||
Short-lived access JWT, opaque rotating refresh token, token families, reuse detection, logout on one device and everywhere. H2 in memory, no external services.
|
||||
|
||||
Run it: `mvn test` (JDK 25, Spring Boot 4.1.2, Spring Security 7.1.2). Every test writes what it observed to [`output/`](output), and `scripts/run-all.sh` regenerates all of it, including a real HTTP walkthrough.
|
||||
|
||||
| file | what it shows |
|
||||
|---|---|
|
||||
| `01-rotation.txt` | login, two rotations, one family; the access token carries the family id as `sid` |
|
||||
| `02-storage.txt` | the table holds the SHA-256 of the token, never the token |
|
||||
| `03-reuse-detection.txt` | a replayed token revokes the family, including the innocent holder's newest token |
|
||||
| `04-families-are-independent.txt` | a replay on the phone does not touch the laptop's login |
|
||||
| `05-concurrent-refresh-strict.txt` | 8 simultaneous requests with one token: 1 wins, the family is then revoked |
|
||||
| `06-concurrent-refresh-grace.txt` | the same stampede with `reuse-grace=5s`: the family survives, the late replay still kills it |
|
||||
| `07-logout.txt` | single-device logout, logout everywhere, and the access token that outlives both |
|
||||
| `08-check-sid.txt` | `demo.check-sid=true` makes the access token die with its family |
|
||||
| `09-expiry.txt` | idle timeout versus the absolute family cap |
|
||||
| `10-clock-skew.txt` | Spring Security's default validator accepts a token 30 seconds past `exp` |
|
||||
| `11-identical-refusals.txt` | unknown, reused, revoked and expired answer with the same bytes |
|
||||
| `12-http-walkthrough.txt` | the same story over real HTTP with curl |
|
||||
|
||||
Settings in `application.yaml`: `demo.jwt.access-ttl` (5m), `demo.refresh.token-ttl` (7d), `demo.refresh.family-max-age` (30d), `demo.refresh.reuse-grace` (0s), `demo.check-sid` (false).
|
||||
|
||||
Token fingerprints and family ids differ on every run because the tokens are random. Statuses, counts and HTTP codes do not.
|
||||
The signing secret in `application.yaml` is a demo value. Read yours from a secret manager.
|
||||
Reference in New Issue
Block a user