Problem
The OTP auth plugin issues JWTs with a fixed ~1 day (86400s) exp claim, and there's no working refresh path. Apps consuming SonicJS auth see users logged out every 24h even when their session cookie is set for 30 days, because the JWT inside the cookie expires long before the cookie does.
Observed in @sonicjs-cms/core@2.16.0.
Repro
- Log in via
/auth/otp/verify
- Decode the returned JWT —
exp is ~24h out
- Wait >24h (or fast-forward clock)
- Any authed request 401s; user is forced through OTP again
Expected
- JWT TTL is configurable (env var, e.g.
JWT_EXPIRES_IN, or plugin option)
- A real
/auth/refresh endpoint that re-signs and returns a new token (sliding session), so a long-lived cookie can keep a user logged in until they explicitly log out
Proposed fix
- Configurable TTL — read
JWT_EXPIRES_IN (or equivalent plugin option) and pass it to the signer. Sensible default of 30 days.
- Working refresh endpoint —
POST /auth/refresh accepts a still-valid (or recently-expired within a grace window) token, validates the user, and issues a fresh token with a new exp.
- Document both in the auth plugin README.
Happy to PR — wanted to track the bug first.
Context
- Web app side:
verify-token middleware checks payload.exp and rejects, so the 30-day session cookie is moot once the JWT inside dies.
- App-side
/api/auth/refresh only re-validates the existing token (can't extend it without SonicJS support).
Problem
The OTP auth plugin issues JWTs with a fixed ~1 day (
86400s)expclaim, and there's no working refresh path. Apps consuming SonicJS auth see users logged out every 24h even when their session cookie is set for 30 days, because the JWT inside the cookie expires long before the cookie does.Observed in
@sonicjs-cms/core@2.16.0.Repro
/auth/otp/verifyexpis ~24h outExpected
JWT_EXPIRES_IN, or plugin option)/auth/refreshendpoint that re-signs and returns a new token (sliding session), so a long-lived cookie can keep a user logged in until they explicitly log outProposed fix
JWT_EXPIRES_IN(or equivalent plugin option) and pass it to the signer. Sensible default of 30 days.POST /auth/refreshaccepts a still-valid (or recently-expired within a grace window) token, validates the user, and issues a fresh token with a newexp.Happy to PR — wanted to track the bug first.
Context
verify-tokenmiddleware checkspayload.expand rejects, so the 30-day session cookie is moot once the JWT inside dies./api/auth/refreshonly re-validates the existing token (can't extend it without SonicJS support).