Imagine you are visiting a company as a customer of the company you are visiting. Before you can enter the building, you show your ID card to a security guard, who verifies your identity and gives you a guest badge. This badge allows you to access certain areas of the building during your visit. In a similar way, access tokens serve as digital badges that grant users permission to access specific resources in an application. In this context, the ID card is used for authentication and the badge is used for authorization. You can read more about this in my article about authorization and authentication here:

Many types of access tokens exists. Amongst these are JSON Web Tokens (JWT), which have gained popularity due to their lightweight nature and ease of use. In this article, we will explore what JWTs are, how they work, and the important role of token refresh mechanisms in maintaining security and user experience.
What is an access token?
An access token is like a temporary pass that lets a user access certain parts of a system or application. Every time the user interacts with the server, they need to prove who they are and that they have permission to access what they’re asking for. But if they had to log in each time they called an API or visited a page, it would make for a frustrating experience. Instead, an access token—valid for a set period, like a few hours or even minutes—allows the user to prove their identity and permissions without logging in repeatedly. This keeps things secure and smooth for both the user and the system.

Besides improving user experience by only requiring a single sign-in, tokens are also more secure because users only enter their credentials once. Each time a user enters their password, there's a chance it could be stolen by a malicious third party. With tokens, although there’s still some risk of theft, the token expires after a set time, so any unauthorized access is limited. In contrast, if an attacker gets hold of a password, they can access the account indefinitely—or at least until the password is changed, which may not happen right away.
Unlike session-based authentication, where the server stores each user’s permissions in a dedicated session, JWTs allow permissions to be included within the token itself. This means that services don’t need to rely on a session or direct the user to the same server each time they make a request. In a session-based setup, the server that holds the session data has to be the one handling the request; otherwise, session data must be shared across servers, usually through a database or a distributed cache. This can introduce added complexity and potential bottlenecks.
With tokens, though, each request from the user includes all the necessary permission data, and the server can check these permissions on each call. As long as the token is verified as coming from a trusted source, the service can trust the permissions within it, making authentication and authorization more efficient and scalable.
Understanding JWT
The format of the JWT includes three sections:
Each section is Base64URL encoded, and each section is separated by a dot ( . ) character. We will take a look at the JSON representation first in this example, and take a look at what the full encoded token would like in the end, to make it easier to follow along.
The header contains metadata about the token, specifically the type of token (usually "JWT") and the signing algorithm (such as HS256). It looks like this in JSON:
{
"alg": "HS256",
"typ": "JWT"
}
The payload section in this example has a sub (subject) claim with a unique user ID, and name with the user’s name. The token also has a role claim with the writer role, as well as a time for when it was issued (iat) and when it expires (exp):
{
"sub": "user_id",
"name": "Daniel Madsen",
"role": "writer",
"iat": 1716539123,
"exp": 1716639123
}
Lastly, the token contains a signature, which ensures the token hasn’t been tampered with. It’s created by encoding the header and payload, then signing them using a secret key. The secret key is on the server, and is never shared with the client. If the user changes any part of the token, the validation will fail. This ensures that the token can be trusted. The service will use the algorithm specified in the alg field in the header to calculate the signature like this:
HMAC_SHA256(
secret,
base64urlEncoding(header) + '.' +
base64urlEncoding(payload)
)
When combined, these three parts make a JWT that can be used for secure, stateless authentication. In this example, the encoded token would like this:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ1c2VyX2lkIiwibmFtZSI6IkRhbmllbCBNYWRzZW4iLCJyb2xlIjoid3JpdGVyIiwiaWF0IjoxNzE2NTM5MTIzLCJleHAiOjE3MTY2MzkxMjN9.6PZSgcjPbri4ylqxjgOHtP6Vp0FyNW4e5YBh5nhm1l0
You can try to generate your own JWTs here:

Refresh Tokens
A refresh token is used to obtain new access tokens after the original access token expires. As explained above, a JWT access token includes an exp value, which sets an expiration time. Once it expires, the user would need to log in again to get a new access token, which is valid for a limited time (for example, 30 minutes). However, for a typical office worker, this would mean logging in multiple times a day to maintain access, which isn’t a great user experience. This is where refresh tokens help.
If the system supports refresh tokens, the user receives two tokens upon initial login: an access token and a refresh token. The access token is included with every request to the service, while the refresh token is securely stored on the client. When the access token expires, the client sends the refresh token to the server, requesting a new access token. The server then issues a fresh access token, allowing the user to continue seamlessly.
Using refresh tokens enhances security, even if it’s not immediately obvious. Why not simply issue an access token valid for the entire workday? The answer lies in control and verification.
Think of the process like an amusement park. With a season pass (the refresh token), you can get a daily wristband (the access token) each time you enter. The wristband’s color changes each day, allowing staff to verify your right to access the rides. If you show up with yesterday’s wristband, you can’t access the rides. Instead, you’ll need to go back to the entrance, show your season pass, and get today’s wristband.
Similarly, access tokens work for a limited time (like the daily wristband), while refresh tokens let you obtain new ones. If someone steals your wristband, they can only use it for that day. But without the season pass (refresh token), they can’t get a new wristband the next day.
In the same way, if a hacker steals your access token, they can only use it until it expires. Once it does, they’re locked out because they don’t have your refresh token. But what if they steal your refresh token? In that case, they could keep generating new access tokens, effectively maintaining access.
This is where token invalidation comes in. If the system detects a compromised refresh token, it can revoke that token, blocking any further access. In the worst case, an attacker might gain up to 30 minutes of access (if they had just generated a new access token), rather than hours or days of access with a long-lived token.
Pairing short-lived access tokens with refresh tokens keeps access limited to shorter windows, reducing risk. This setup allows for strong access control and better security overall, and it’s widely recommended for balancing security with usability.
Summery
Security tokens like JWTs streamline and secure user access in modern applications. By separating access tokens from refresh tokens, systems maintain both a smooth user experience and enhanced security. Access tokens offer efficient, stateless authentication, while short expiration times limit the impact of token theft. Meanwhile, refresh tokens allow users to seamlessly renew access without needing to re-authenticate constantly. Together, these tokens balance usability with strong security controls, making them a powerful approach for managing access in today’s digital environments.