Authentication
JWT Authentication for a Football Booking App: Login, Refresh, and Logout
JWT authentication needs a complete login, renewal, and logout design. Signing a token covers only one part of that flow.
FindYourTurf is a hypothetical football booking app I would like to build. I use Rooney, a player looking for a pitch, to walk through an example design.
This example uses a browser app and API on the same origin. Tokens travel in cookies. It describes a design, rather than a deployed implementation.
1. Understand what the token proves
A JSON Web Token, or JWT, carries claims about a user or request context. A signed JWT lets the API check that those claims came from a trusted signer.
Signing does not hide the payload. Keep passwords and other secrets out of a signed token. The JWT specification defines its structure and claims.
For this example, an access JWT would contain:
sub: Rooney's stable user ID.iss: the expected token issuer.aud: the API allowed to accept it.iatandexp: issue and expiry times.jti: a token identifier, if the revocation design uses one.
2. Issue tokens after login
Rooney submits his credentials over HTTPS. The server checks the password against its stored password hash.
After successful authentication, the server issues two credentials:
- A short-lived access JWT for API requests.
- A refresh token that allows renewal without another password entry.
The refresh token in this design is an opaque random value. The server stores its hash, user association, expiry, and revocation state. The browser holds the token itself.
A JWT does not remove that server-side state. A conventional server-side session is also a valid choice for this browser app.
3. Choose one browser storage design
For this same-origin example, both credentials use HttpOnly, Secure cookies with an explicit SameSite setting.
Set-Cookie: __Host-access=<jwt>; Path=/; Secure; HttpOnly; SameSite=Lax
Set-Cookie: __Host-refresh=<refresh-token>; Path=/; Secure; HttpOnly; SameSite=Lax
HttpOnly prevents JavaScript from reading the cookie.
Secure restricts transmission to HTTPS.
The __Host- prefix
requires a root path and no Domain attribute.
These cookies use the API's cookie authentication path, rather than a JavaScript-generated bearer header. Token expiry and cookie lifetime must be set consistently.
Tokens in local storage can be read by JavaScript, including malicious scripts running in the app. Cookies also cannot stop malicious scripts from making requests while running in the app.
OWASP's session guidance explains cookie protections and their limits.
4. Protect cookie-authenticated requests
Browsers attach eligible cookies automatically. Another site can try to trigger an authenticated action; this is cross-site request forgery, or CSRF.
Use POST or another appropriate non-GET method for state changes. Validate request origins and require a CSRF token bound to the session.
Apply these checks to booking changes, refresh, and logout.
SameSite adds protection, but it is not a replacement for the complete CSRF design.
See OWASP's CSRF guidance.
5. Validate identity and permission separately
The API uses an established JWT library to verify the signature and required claims. The accepted algorithm and trusted keys come from server configuration.
Check the issuer, audience, and expiry. Validate any additional claims required by the chosen token profile. Decoding a token alone does not verify it.
A valid JWT identifies Rooney for this example. It does not give him access to every booking.
For a booking request, the API must also check ownership or another explicit permission. Changing a booking ID in the URL must not grant access to another player's booking.
OWASP's API guidance covers token validation and access control.
6. Renew through the refresh endpoint
When the access token expires, the browser sends a protected refresh request. The server checks the refresh token's stored record and expiry.
On successful renewal, rotate the refresh token: invalidate the old value and issue a new one. Update the stored state atomically so the old token cannot be accepted twice.
Keep related refresh tokens linked so reuse can trigger revocation of that renewal chain. The client should coordinate refresh requests to avoid accidental concurrent renewals.
OAuth security guidance describes refresh token rotation and replay protection. Those controls also inform this example's renewal design.
7. Define what logout guarantees
On logout, revoke the refresh record and expire both browser cookies using their original names and paths.
This stops renewal and removes those credentials from that browser. It does not automatically invalidate an access JWT that someone already copied.
With signature-only validation, that JWT can remain usable until its expiry. Immediate revocation requires another check, such as a token denylist or server-side session state.
That check introduces server-side work on requests. Choose the required logout guarantee explicitly and implement the API checks that enforce it.
Keep the example's boundaries clear
The football scenario makes the flow easier to follow, but the security decisions are specific. Cookie storage, CSRF checks, refresh state, and access-token revocation must work together.
For FindYourTurf, I would choose between this design and a server-side session based on the actual API and client requirements.