Skip to main content

Throttling and the switch

Register the API's routes through the package:

// routes/api.php
use Illuminate\Support\Facades\Route;
use Pushery\SanctumHardening\SanctumHardening;

SanctumHardening::routes(function () {
Route::get('/orders', fn () => ['orders' => []]);
});

The routes are registered under routes.prefix, api by default, behind routes.middleware, api and auth:sanctum by default, and behind the package's limiter. routes.middleware takes a list or a single middleware as a string. A setting that names none, an empty list for instance, keeps api and auth:sanctum, so the routes never run without the token guard by accident.

Two stages​

Each token has its own limit, throttle.per_token requests a minute, and each user has one over all of their tokens, throttle.per_user. Without the second, a user gets past every limit of a token by minting another one. A request over either is answered with 429 as a problem document (RFC 9457), with the Retry-After header the limiter computed:

{
"type": "about:blank",
"title": "Too Many Requests",
"status": 429,
"detail": "Too many requests. Another one can be sent once the time in the Retry-After header has passed.",
"instance": "/api/orders"
}

A request authenticated by the session rather than a token counts as one token of its user. A request nobody is signed in for is counted by its address, kept as a keyed hash, because a rate limiter's key is a cache key and error trackers record cache keys.

The switch​

SANCTUM_HARDENING_ENABLED=false switches the API off. SanctumHardening::routes() then registers nothing, so the routes do not exist: a request meets no 403 from a route that refuses but the 404 of a route that is not there, and route:list names none. Minting is not affected.

The last use of a token​

Sanctum writes last_used_at on every request a token makes, so a busy token turns each request into a write on the same row. The package lets that write through once every last_used_at.every seconds, a minute by default. A save of a token that changes anything else goes through as it is.