Access Token vs Refresh Token
Introduction
Access tokens and refresh tokens are central concepts in OAuth 2.0 and OpenID Connect. Modern APIs avoid sending usernames and passwords with every request. Instead, a user or client authenticates through an authorization server, receives tokens, and uses those tokens to access protected resources. This token-based model improves security, user experience, scalability, and control, but it also creates new testing responsibilities. API testers must know what each token is for, where it is sent, how long it lives, and how the system should behave when a token is invalid, expired, revoked, or misused.
An access token is used to call protected APIs. It is the credential presented to the resource server, usually through the `Authorization: Bearer` header. A refresh token is used to obtain a new access token after the current access token expires. It is sent to the authorization server, not to protected resource APIs. This simple distinction is one of the most important points in OAuth testing. If a refresh token is sent to a resource server, that is usually wrong. If an expired access token is still accepted by an API, that is usually a security defect.
Using short-lived access tokens with longer-lived refresh tokens balances security and usability. Short-lived access tokens reduce the damage if an access token is stolen. Refresh tokens allow the application to obtain new access tokens without forcing the user to log in again every few minutes. This is why users can stay signed in to web and mobile applications while the APIs still enforce token expiration behind the scenes.
For API testers, understanding access token vs refresh token is not optional. Real OAuth-based applications require tests for protected API access, token expiry, refresh behavior, token revocation, scope validation, permission enforcement, refresh token rotation, logout behavior, account disablement, secure storage, and safe logging. A system can appear to work correctly in the happy path while still having serious token lifecycle defects. Strong testing makes those defects visible.
What Is an Access Token?
An access token is a credential issued by the authorization server that allows a client application to access protected resources on the resource server. In practical API testing terms, the access token is the token sent with API requests. The resource server validates the token before returning data or performing the requested action. If the token is valid and has the required permission, the request continues. If not, the request fails.
A protected API request commonly looks like this:
GET /employees
Authorization: Bearer eyJhbGciOiJIUzI1NiIs...
The token value may be a JWT or an opaque token. A JWT can contain claims such as issuer, audience, subject, expiry, scopes, roles, tenant, and other details. An opaque token is a random-looking string that the resource server or gateway must validate by lookup or introspection. Testers should not assume every access token is a JWT. The token format depends on the authorization server and API architecture.
Access tokens are usually short-lived. A token may be valid for minutes or hours, depending on the security design. The short lifetime limits the exposure window if the token is stolen. It also allows permission changes, account changes, and revocation policies to take effect more predictably. Once the access token expires, the API should reject it, usually with `401 Unauthorized`, and the client should obtain a new access token through the correct flow.
Purpose of Access Tokens
The purpose of an access token is to authorize API access. The client presents the token to the resource server. The resource server checks whether the token is valid, whether it was issued by a trusted authorization server, whether it is intended for that API, whether it has expired, and whether it carries the necessary scopes or permissions. Only then should protected data or actions be allowed.
Access tokens reduce the need to send long-term credentials to many resource servers. Instead of giving every API endpoint a user's password, the client sends a token that can be limited in lifetime and scope. This is much safer than repeatedly sending a username and password to every API call. It also works better in distributed systems where many services need to validate requests.
In many systems, access tokens carry authorization context. A token may include a `read` scope, an `orders.write` scope, an admin role, a tenant ID, or a user ID. The resource server uses these details to decide whether the caller can perform the requested operation. A valid access token without the right permission should not be enough. Authentication and authorization must both be enforced.
What Is a Refresh Token?
A refresh token is a credential used to obtain a new access token after the current access token expires. It is not used to call protected APIs directly. Instead, it is sent to the authorization server's token endpoint. If the refresh token is valid, the authorization server returns a new access token. Depending on the implementation, it may also return a new refresh token. This is known as refresh token rotation.
A refresh-token request may look like this:
POST /oauth/token
Content-Type: application/x-www-form-urlencoded
grant_type=refresh_token
refresh_token=def50200b5...
The server response may return a new access token:
{
"access_token": "newAccessToken",
"expires_in": 3600,
"token_type": "Bearer"
}
Refresh tokens usually live longer than access tokens. Because they can be used to obtain new access tokens, they are highly sensitive. A stolen refresh token may allow continued access even after an access token expires. For that reason, refresh tokens must be stored securely, rotated when supported, revoked when needed, and kept out of logs and client-side exposure.
Purpose of Refresh Tokens
The main purpose of a refresh token is to keep the user experience smooth while access tokens remain short-lived. Without refresh tokens, every access token expiry could force the user to log in again. That would be frustrating in normal applications. With refresh tokens, the application can request a new access token silently or with minimal user interruption, depending on the security policy.
Refresh tokens also centralize renewal at the authorization server. The resource server does not need to issue new tokens. It only validates access tokens. The authorization server controls whether a refresh token is still valid, whether it belongs to the correct client, whether it has expired, whether it has been revoked, and whether the user or client still has permission. This separation keeps token lifecycle management in the correct place.
Refresh tokens are especially useful for mobile apps, long-running web sessions, desktop apps, and applications where users expect to stay signed in. They are also used in some server-side applications. However, not every OAuth flow returns refresh tokens. Client Credentials flows often do not need refresh tokens because the client can request a new access token using its own credentials. The availability of refresh tokens depends on the grant type and authorization server configuration.
Access Token vs Refresh Token
| Feature | Access Token | Refresh Token |
|---|---|---|
| Purpose | Access protected APIs | Obtain a new access token |
| Sent to | Resource server | Authorization server |
| Included in API requests | Yes | No |
| Typical lifetime | Short | Longer |
| Used frequently | Yes, with protected API calls | Only when renewal is needed |
| Permission data | May include scopes or claims | Used for renewal, not normal API authorization |
| Main risk if stolen | Attacker can access APIs until expiry or revocation | Attacker may obtain new access tokens |
| Testing focus | API access, expiry, scopes, authorization | Renewal, rotation, revocation, secure storage |
The easiest way to remember the difference is this: the access token goes to the API, while the refresh token goes to the authorization server. The access token is used often. The refresh token is used only when a new access token is needed. The access token should be short-lived. The refresh token may be longer-lived but must be protected more carefully.
OAuth Token Response
After a successful OAuth flow, the authorization server may return both tokens in a response like this:
{
"access_token": "eyJhbGciOiJIUzI1NiIs...",
"refresh_token": "def50200b5...",
"expires_in": 3600,
"token_type": "Bearer"
}
The `access_token` is used in calls to protected APIs. The `refresh_token` is used later at the token endpoint. The `expires_in` value tells the client how long the access token is valid, usually in seconds. The `token_type` tells the client how the token should be presented, commonly as a bearer token.
Testers should validate that token responses include the required fields and exclude unsafe information. The response should not expose user passwords or secrets. The token type should match the API expectation. Expiration should be reasonable. Refresh tokens should be present only when the flow and policy allow them. For example, a Client Credentials flow may return only an access token.
Token Lifecycle
The token lifecycle begins when the user or client authenticates. The authorization server issues an access token and, when applicable, a refresh token. The client uses the access token to call protected APIs. The resource server validates the access token for each request. After the access token expires, the resource server rejects it. The client then sends the refresh token to the authorization server to obtain a new access token. The cycle continues until the refresh token expires, is revoked, or can no longer be used.
In systems with refresh token rotation, each refresh request returns a new refresh token along with the new access token. The old refresh token becomes invalid. This reduces risk because a stolen old refresh token cannot be reused after rotation. It also allows the authorization server to detect reuse of a refresh token, which can indicate compromise. Testing rotation is important because incorrect implementation can allow old refresh tokens to keep working.
Revocation can happen at several points. A user may log out. An administrator may disable the account. A user may change password. A security system may detect suspicious activity. The user may revoke application consent. The client may be disabled. The authorization server should then prevent further token use according to policy. Testers should verify how quickly revocation affects access tokens and refresh tokens.
Access Token Expiration
Access token expiration is a core security feature. A token that never expires creates long-term risk if it is leaked. By keeping access tokens short-lived, the system limits how long a stolen token can be used. Once the token expires, the protected API should reject it, commonly with `401 Unauthorized`. The client should then use the refresh token flow if available.
Testing expiration can be challenging because waiting for a long expiry in automated tests is inefficient. Teams may provide short-lived test tokens, a test authorization server configuration, or a way to generate expired tokens. For JWTs, tests may use a token with an expired `exp` claim if the system allows controlled test tokens. The important point is to verify actual API behavior, not simply inspect the token visually.
Clock skew should also be considered. Distributed systems may allow a small tolerance around expiry to handle time differences between servers. That tolerance should be controlled and documented. A token that expired a few seconds ago may be handled differently from one that expired days ago, depending on design. Testers should understand the expected behavior before reporting failures.
Refresh Token Expiration
Refresh tokens also expire, although they usually live longer than access tokens. A refresh token may be valid for days, weeks, or months depending on policy, client type, risk, and user activity. Some systems use absolute expiration, where the refresh token expires after a fixed time. Others use sliding expiration, where activity extends the session until a maximum lifetime is reached. Some systems rotate refresh tokens on each use.
When a refresh token expires, the client should not be able to obtain a new access token. The user usually needs to authenticate again. The authorization server may return an OAuth error such as `invalid_grant`. The exact status code and response body depend on the provider. Testers should validate expired refresh token behavior because accepting an expired refresh token can allow sessions to continue longer than intended.
Refresh token expiration is also tied to logout and account lifecycle. If the user logs out, should the refresh token be revoked? If the password changes, should old refresh tokens stop working? If the user is disabled, should refresh attempts fail immediately? These are policy decisions, but they must be tested once defined.
Access Token Testing
Access token testing verifies whether protected APIs correctly accept valid access tokens and reject bad ones. A valid access token with the required permission should allow the request. A missing token should fail. An invalid token should fail. An expired token should fail. A revoked token should fail if the design supports revocation. A token issued for the wrong audience should fail. A token from an untrusted issuer should fail. A token with insufficient scope should be denied.
Scope and permission validation are crucial. A token with `read` scope should not perform write operations. A token for one user should not access another user's resource. A token for one tenant should not access another tenant's data. A token issued for one API should not be accepted by a different API unless the audience and trust design explicitly allow it. These tests catch real authorization defects.
Testers should also verify response status codes. Missing, invalid, or expired access tokens commonly return `401 Unauthorized`. Valid tokens with insufficient permissions commonly return `403 Forbidden`. Some systems use `404 Not Found` to hide resource existence from unauthorized users. The expected behavior should be documented and consistent.
Refresh Token Testing
Refresh token testing verifies whether the authorization server correctly issues new access tokens and rejects invalid renewal attempts. A valid refresh token should return a new access token. An invalid refresh token should fail. An expired refresh token should fail. A revoked refresh token should fail. A refresh token belonging to one client should not work for another client. A refresh token should not be accepted at a resource API endpoint.
If refresh token rotation is enabled, testers should verify that the old refresh token becomes invalid after use. Reusing an old refresh token should fail and may trigger additional security behavior. If rotation is not enabled, testers should still verify expiry, revocation, and client binding. Refresh tokens should be treated as highly sensitive because they can extend access.
Error handling should be validated. OAuth servers often return `invalid_grant` for invalid, expired, revoked, or reused refresh tokens. Some return `400 Bad Request`. Invalid client authentication may return `401 Unauthorized`. The tester should validate both status and response body according to the provider's contract.
Example Test Cases
| Scenario | Expected Result | Purpose |
|---|---|---|
| Valid access token calls protected API | 200 OK or expected success status | Confirms protected access works |
| Missing access token | 401 Unauthorized | Confirms anonymous access is rejected |
| Expired access token | 401 Unauthorized | Confirms expiry is enforced |
| Valid access token with insufficient scope | 403 Forbidden | Confirms authorization is enforced |
| Valid refresh token requests new access token | New access token returned | Confirms renewal works |
| Invalid refresh token | 400 Bad Request or invalid_grant | Confirms bad renewal requests are rejected |
| Revoked refresh token | Rejected with documented OAuth error | Confirms revocation is enforced |
| Refresh token sent to resource server | Rejected | Confirms token type separation |
These test cases should be executed with controlled users and clients. Avoid using production tokens in test automation. Store secrets securely. Mask token values in reports. Keep tests clear about whether they are validating access token behavior or refresh token behavior.
REST Assured Example
A protected API call with an access token can be written in REST Assured like this:
given()
.header("Authorization", "Bearer " + accessToken)
.when()
.get("/employees")
.then()
.statusCode(200);
A refresh token request can be sent to the token endpoint using form parameters:
given()
.formParam("grant_type", "refresh_token")
.formParam("refresh_token", refreshToken)
.when()
.post("/oauth/token")
.then()
.statusCode(200);
In a real framework, the response should be parsed and the new access token should be extracted. The test should also verify that the new token can access the protected API and that the expired old token remains rejected. For rotation, the test should confirm whether the old refresh token is invalid after use.
Postman Example
In Postman, access tokens are usually sent through the Authorization tab by selecting Bearer Token and supplying the token value. Postman then adds the `Authorization: Bearer` header. For refresh token testing, the tester can send a POST request to the token endpoint with `grant_type=refresh_token` and the refresh token value in the body, according to the provider's contract.
Postman environments can store access tokens and refresh tokens as variables, but this must be handled carefully. Exported environments may expose secrets. Shared workspaces may make tokens visible to more people than intended. Testers should avoid storing real production refresh tokens in casual collections. When testing negative scenarios, use controlled test tokens.
Postman can also automate token renewal through pre-request scripts, but testers should not hide security defects behind automatic refresh. A test for expired access token behavior should intentionally send an expired access token and assert the expected failure. A separate test should verify that refresh works.
Karate Example
Karate can send an access token to a protected API like this:
Given header Authorization = 'Bearer ' + accessToken
When method GET
Then status 200
A refresh request can be written with form fields:
Given form field grant_type = 'refresh_token'
And form field refresh_token = refreshToken
When method POST
Then status 200
Karate can store the returned token for later requests. It can also make negative token tests readable. For example, one scenario can use `expiredAccessToken` and expect `401`, while another uses `readOnlyAccessToken` and expects `403` for a write operation. Clear token names make the test intent obvious.
Real-World Examples
Google APIs use access tokens to call APIs such as Google Drive, Gmail, and Calendar. A refresh token may allow the application to obtain new access tokens without repeatedly asking the user to log in, depending on the OAuth flow and consent configuration. Scopes control what the token can access.
Microsoft Graph uses access tokens to access Outlook, OneDrive, Teams, SharePoint, users, groups, and directory resources. Refresh tokens can help maintain sessions or renew access depending on the client type and policy. Tokens are tied to permissions, tenants, audiences, and identity platform rules.
GitHub applications use access tokens to call GitHub APIs for repositories, issues, pull requests, packages, and organization data. Depending on the app type and flow, token renewal may be supported. Enterprise applications often use access tokens for microservice calls and refresh tokens for long-lived user sessions in web or mobile apps.
Security Best Practices
Use short-lived access tokens. Short access token lifetime reduces the damage from token theft. Store refresh tokens securely because they can obtain new access tokens. Never expose tokens in logs, URLs, screenshots, browser history, source code, shared API collections, or downloadable reports. Always use HTTPS for token issuance, token refresh, and protected API calls.
Rotate refresh tokens if supported. Refresh token rotation limits reuse risk and helps detect compromise. Revoke compromised tokens immediately. Request only necessary scopes. Validate token expiration and revocation. Bind refresh tokens to the correct client. Do not allow refresh tokens to be used as access tokens. Do not issue refresh tokens to clients that do not need them.
For automation, keep tokens out of committed files. Use secure configuration, CI/CD secrets, or test identity providers. Mask authorization headers and token endpoint responses in logs. If test reports include request or response data, ensure tokens are redacted. Token security is part of the test framework's responsibility, not only the application team's responsibility.
Practical Testing Mindset
A practical tester should think of token testing as lifecycle testing. The question is not only whether a token works immediately after login. The stronger question is whether the token works only during the allowed time, only for the allowed client, only for the allowed user, only for the allowed scope, and only until the security policy says it should stop working. This mindset helps testers find defects that simple happy-path API checks miss.
When a defect occurs, identify which token layer failed. If a protected API accepts no token, the access-token requirement is broken. If an expired access token still works, expiry validation is broken. If a refresh token works after revocation, renewal control is broken. If a read-only access token performs writes, authorization is broken. Clear diagnosis helps developers fix the right part of the OAuth implementation quickly.
Common Mistakes
A common mistake is sending refresh tokens to resource servers. Refresh tokens belong at the authorization server's token endpoint. Protected APIs should receive access tokens. If a resource server accepts a refresh token as if it were an access token, the design is unsafe.
Another mistake is using long-lived access tokens. Long-lived access tokens increase risk because stolen tokens remain useful for a long time. If long-lived access is required, a better design is usually short-lived access tokens plus secure refresh tokens. This allows the system to renew access while still limiting direct API token exposure.
Logging tokens is a serious mistake. Access tokens and refresh tokens are credentials. Logs, CI output, error reports, screenshots, and shared Postman exports should not contain full token values. If a token leaks, it should be revoked. Refresh tokens are especially sensitive because they can maintain access beyond one access token lifetime.
Ignoring token expiration is another common issue. Applications should detect expired access tokens and refresh them correctly. APIs should reject expired tokens. Test suites should not rely only on non-expiring test tokens because that hides lifecycle defects. Testers should validate both expiry failure and successful renewal.
Interview Questions
A common interview question is: what is an access token? A strong answer is that an access token is a credential issued by the authorization server and used by a client to access protected APIs on the resource server. It is usually sent in the `Authorization: Bearer` header and is typically short-lived.
Another question is: what is a refresh token? A refresh token is a credential used to obtain a new access token after the current access token expires. It is sent to the authorization server, not to protected APIs. It is usually longer-lived than an access token and must be stored securely.
Interviewers may ask which token is sent to APIs. The answer is the access token. They may ask which token is sent to the authorization server for renewal. The answer is the refresh token. They may also ask why both are used. The answer is that short-lived access tokens improve security, while refresh tokens improve user experience by allowing new access tokens without repeated login.
For testing questions, explain that testers should validate valid access tokens, missing access tokens, invalid access tokens, expired access tokens, revoked access tokens, scope enforcement, valid refresh tokens, expired refresh tokens, invalid refresh tokens, revoked refresh tokens, refresh token rotation, token revocation, secure storage, HTTPS, and token redaction in logs.
Interview-Ready Explanation
An access token is a credential issued by the authorization server that allows a client application to access protected resources on a resource server. It is included in API requests, usually in the `Authorization: Bearer` header, and is typically short-lived. The resource server validates the access token before returning data or performing an operation. The token may include or reference scopes, roles, claims, audience, issuer, and expiry.
A refresh token is a separate credential used only with the authorization server to obtain a new access token after the current access token expires. It is not sent to protected APIs. Refresh tokens are usually longer-lived and must be stored more securely because they can extend access by producing new access tokens. Some systems rotate refresh tokens so each refresh returns a new refresh token and invalidates the previous one.
Using both tokens improves security and usability. Access tokens can be short-lived to reduce risk if they are stolen, while refresh tokens allow users to remain signed in without repeatedly entering credentials. During API testing, testers should validate token generation, protected API access, token expiration, refresh behavior, refresh token rotation, revocation, invalid token errors, scope enforcement, secure storage, HTTPS usage, and token masking in logs and reports.
Key Takeaway
Access tokens and refresh tokens serve different purposes. The access token is used to call protected APIs. The refresh token is used to obtain a new access token from the authorization server. The access token is usually short-lived and sent frequently. The refresh token is usually longer-lived, sent only during renewal, and must be protected carefully.
For API testers, the practical rule is simple: validate where each token is used and how each token fails. Access tokens should be accepted only when valid, unexpired, trusted, and sufficiently scoped. Refresh tokens should renew access only when valid, unexpired, not revoked, and bound to the correct client. Good token testing proves that API access is secure, renewal is controlled, and sensitive token values are never exposed unnecessarily.