auth: make access tokens revocable

verify_access_token was a pure signature + expiry check, so an access token could
not be revoked. Sign-out and logout revoked the DB session row, and an erasure
request revoked all of them, but the bearer token kept working for the rest of its
24h life on every authenticated route. The refresh token has always carried a sid
for exactly this; the access token, which actually guards the API, did not.

Adds sid to AccessTokenClaims and checks it against the sessions table on each
authenticated request (one indexed primary-key lookup).

sid is Option so tokens minted before this deploy still verify rather than logging
every user out mid-session; they simply remain unrevocable until they expire,
which is the pre-existing behaviour.
This commit is contained in:
jwinterm
2026-08-01 08:00:38 -04:00
parent f26b432999
commit e2589d4835
3 changed files with 44 additions and 0 deletions
+10
View File
@@ -36,6 +36,16 @@ pub async fn extract_user_id_from_token(
) -> Result<Uuid, AppError> {
let token = bearer_token(headers)?;
let info = state.sessions.verify_access_token(token)?;
// Signature + expiry alone is not enough: it made access tokens
// unrevocable, so sign-out and even a completed erasure request left the
// bearer token working for the rest of its 24h life. Tokens minted before
// `sid` shipped carry no session and keep the old behaviour until they
// expire, rather than logging everyone out on deploy.
if let Some(sid) = info.session_id {
if !state.db.is_session_live(sid).await? {
return Err(AppError::AuthError("Invalid or expired token".into()));
}
}
Ok(info.user_id)
}