v1.4.1 - API token verification cost
A performance fix on the API's authentication path, and test coverage for a
security control that had none.
API token verification: 151 ms → 7.6 ms
Argon2 is deliberately slow. That is correct for a password typed once and wrong
for a credential presented on every call — verifying the bearer token was most
of a ~150 ms API request.
A short-lived per-process cache now skips the hash comparison for a token that
has already verified. Measured back to back in one process on /api/v1/me:
| Median | |
|---|---|
| Argon2 every request (previous) | 150.8 ms |
| With the verified-token cache | 7.6 ms |
What is cached is the verification, never the authorization decision. Every
request still loads the row and re-checks revocation and expiry, so revoking a
token takes effect on the very next call. There are tests for revocation,
expiry and deletion specifically, because that is the property a later refactor
would quietly break.
The raw token is never a cache key — it is hashed with BLAKE2b keyed on
SECRET_KEY, so a memory dump yields nothing usable. The cache is bounded and
entries expire after five minutes.
last_used_at now writes at a five-minute resolution rather than on every
request. It answers "roughly when was this token last seen", which does not
justify a database write per API call.
Rate limiting is now actually tested
The entire suite ran with RATELIMIT_ENABLED = False — rate limits and fixtures
that sign in dozens of times do not mix — which left the only thing standing
between a password guesser and unlimited attempts with no coverage at all. An
upgrade could have turned it into a no-op and nothing would have failed.
tests/test_rate_limiting.py builds its own application with limiting on and
asserts the eleventh login POST inside a minute is refused, that a correct
password does not bypass an exhausted limit, and that GET is untouched.
Dependencies
actions/checkout v4→v7, actions/setup-python v5→v7,
actions/configure-pages v5→v6, actions/deploy-pages v4→v5,
docker/setup-buildx-action v3→v4, and Flask-Limiter widened to allow 4.x —
safe to take now that the limiter has real coverage. Verified against both 3.12
and 4.1.1.
Upgrading
No migration. No configuration change.
Full changelog: https://github.com/ibuilder/scopemaker/blob/main/CHANGELOG.md