Postman Token Handling: Auth Scripts, Refresh & Negative Tests
Almost every real API is secured, so almost every Postman collection needs to get a token, send it with each request, and cope when it expires.
This practice set covers that end to end: extracting and passing tokens, logging in automatically only when needed, the negative tests every secured API should pass, role-based access, logout and token invalidation, and keeping credentials out of shared files.
The scripts were run with Newman against a local API — the token was fetched once and reused across requests, and all assertions passed.
1. Extract and Store the Token
Add this script to the Tests or Post-response tab of the login request:
// Tests (Post-response) tab of POST {{baseUrl}}/auth
pm.test('login succeeded', () => {
pm.response.to.have.status(200);
});
const token = pm.response.json().token;
pm.test('token returned', () => {
pm.expect(token)
.to.be.a('string')
.and.not.empty;
});
pm.collectionVariables.set('token', token);
Collection variables are useful for values created and reused during a collection run.
Environment variables are useful for values that differ between environments.
For more information about variable scopes, see Postman Environments & Variables.
2. Send the Token
Different APIs use different authentication mechanisms.
| Scheme | How It's Sent | In Postman |
|---|---|---|
| Bearer token — JWT or OAuth access token | Authorization: Bearer <token> |
Authorization → Bearer Token → {{token}} |
| Cookie-based authentication | Cookie: token=<token> |
Headers tab |
| API key | Header such as x-api-key, or a query parameter |
Authorization → API Key |
| Basic authentication | Authorization: Basic base64(user:pass) |
Authorization → Basic Auth |
For Bearer authentication, you can set the authorization at the collection level and select Inherit auth from parent on individual requests.
This means the authentication configuration can be changed in one place instead of updating every request.
For example:
Authorization: Bearer {{token}}
This approach becomes particularly useful when a collection contains dozens or hundreds of secured endpoints.
3. Log In Automatically — Only When Needed ⭐
Instead of adding a separate login request before every secured API request, you can use a collection-level pre-request script.
The script checks whether an existing token is still valid and performs authentication only when necessary.
// Collection-level pre-request script
const token = pm.collectionVariables.get('token');
const expiresAt = Number(
pm.collectionVariables.get('tokenExpiresAt') || 0
);
if (
token &&
Date.now() < expiresAt - 60_000
) {
return;
}
pm.sendRequest({
url: pm.collectionVariables.get('baseUrl') + '/auth',
method: 'POST',
header: {
'Content-Type': 'application/json'
},
body: {
mode: 'raw',
raw: JSON.stringify({
username: pm.environment.get('username'),
password: pm.environment.get('password')
})
}
}, (err, res) => {
if (err || res.code !== 200) {
throw new Error(
'Login failed: ' + (err || res.code)
);
}
pm.collectionVariables.set(
'token',
res.json().token
);
pm.collectionVariables.set(
'tokenExpiresAt',
Date.now() + 15 * 60 * 1000
);
});
Every request in the collection can now use a valid token without requiring a separate login step.
In the tested run, the login request happened once, and subsequent requests reused the token.
If your API returns an expires_in value, use that value rather than hard-coding a 15-minute lifetime.
For example, if the API returns:
{
"access_token": "abc123",
"expires_in": 900
}
you can calculate the expiration time using the returned value.
Refreshing slightly before expiration helps avoid a token expiring in the middle of a request.
Refresh Tokens
OAuth-style APIs commonly use two types of tokens:
- Access token — short-lived and used to access protected resources.
- Refresh token — longer-lived and used to obtain a new access token.
A typical refresh flow looks like this:
- Login returns an access token and refresh token.
- The access token is used for API requests.
- The access token approaches expiration.
- The client sends the refresh token to the token endpoint.
- The server returns a new access token.
- The client stores the new token pair.
- Subsequent requests use the new access token.
A refresh request may look like:
grant_type=refresh_token
refresh_token={{refreshToken}}
Test the following scenarios:
- A valid refresh token produces a new access token.
- An expired refresh token is rejected.
- A revoked refresh token is rejected.
- A refresh token is rotated when the API requires rotation.
- The old refresh token stops working when rotation is enabled.
- The new access token can access protected resources.
For more information, see API Authentication: OAuth & JWT.
4. Negative Token Tests
Authentication testing should not stop after verifying that a valid token works.
You should also verify how the API behaves when authentication information is missing, invalid, expired or associated with the wrong authorization level.
| Scenario | Typical Response | Verified in Tested Run |
|---|---|---|
| No token | 401 Unauthorized | ✓ 401 |
| Invalid or tampered token | 401 Unauthorized | ✓ 401; token not echoed back |
| Expired token | 401 Unauthorized | Not tested in the local run |
| Valid token, wrong role | 403 Forbidden | Not tested in the local run |
| Token used after logout | 401 if revocation is supported | Not tested in the local run |
| Token from another environment | 401 | Not tested in the local run |
Invalid Token Example
For example, send:
Authorization: Bearer not-a-real-token
Then validate the response:
pm.test(
'401 with an invalid token',
() => pm.response.to.have.status(401)
);
pm.test(
'no token echoed in the error',
() => pm.expect(
pm.response.text()
).to.not.include('not-a-real-token')
);
401 vs 403
The distinction is important:
- 401 Unauthorized generally indicates that the request is not successfully authenticated.
- 403 Forbidden generally indicates that the caller is authenticated but does not have permission for the requested resource.
However, API implementations can differ.
The Restful Booker practice API, for example, uses cookie-based authentication and returns 403 Forbidden for certain update and delete operations without a valid token.
Therefore, always assert the response documented by the specific API rather than blindly assuming a status code.
5. Roles and Multiple Users
Authentication and authorization should be tested separately.
A collection can maintain credentials for different roles, such as:
adminUser
viewerUser
editorUser
You can then organize requests by role and verify the access matrix.
| Request | Admin | Editor | Viewer |
|---|---|---|---|
GET /users |
200 | 200 | 200 |
PATCH /users/{id} |
200 | 200 | 403 |
DELETE /users/{id} |
204 | 403 | 403 |
GET /users/{another user's id}/orders |
200 | 403/404 | 403/404 |
The final scenario is particularly important.
A user should not be able to simply change an ID in the URL and retrieve another user's private information.
For example:
GET /users/101/orders
should not automatically become accessible to another user by changing the identifier to:
GET /users/102/orders
when user 102's data is not authorized for the caller.
This type of authorization test is especially important for APIs that expose object identifiers directly.
A data-driven run can cover an entire access matrix.
For example, a CSV file could contain:
role,endpoint,expectedStatus
admin,/users,200
editor,/users,200
viewer,/users,200
admin,/users/101,200
editor,/users/101,403
viewer,/users/101,403
For more information, see API Security Testing.
6. Logout and Token Invalidation
When an API supports token invalidation or logout, test whether the token remains usable after logout.
A basic sequence is:
- Log in and save the token.
- Call a secured endpoint.
- Verify that the request succeeds with
200. - Call the logout endpoint.
- Reuse the same token.
- Verify that the API rejects the token when revocation is expected.
Example flow:
Login
↓
Save token
↓
GET secured endpoint → 200
↓
Logout
↓
Reuse same token
↓
Expected result → 401
However, stateless JWTs often remain valid until they expire even after logout.
That may be intentional depending on the application's architecture.
For sensitive applications, document the expected behavior clearly and test it against the application's security requirements.
7. Keeping Secrets Safe
Authentication testing involves sensitive credentials, so the Postman collection should be designed carefully.
Recommended Practices
- Store credentials as environment variables with the secret type.
- Keep real values in current values, not shared initial values.
- Never commit exported environments containing real passwords.
- Inject secrets at runtime in CI.
- Never print access tokens in logs.
- Use separate test accounts for each environment.
- Never use production credentials in test automation.
For Newman, secrets can be injected at runtime:
newman run collection.json \
--env-var "password=$API_PASSWORD"
Avoid code such as:
console.log(pm.environment.get('password'));
or:
console.log(pm.collectionVariables.get('token'));
because CI logs may be accessible to many users.
Plan positive, negative and role-based authentication cases for your own endpoints in the API Testing Scenario Lab.
For more scripting techniques, see Postman Scripting & Assertions.
From Real Projects
I've worked with APIs at the level this page covers: CRUD operations, HTTP methods, JSON and XML formats, and extracting and validating values with JSON path. A good habit from that work: test the full create–read–update–delete cycle for a resource and check each response in detail. Testsigma supports web, mobile and API test automation, so understanding APIs was part of understanding the product I was testing. Store tokens in variables so every request picks up the latest one. See also: The Complete API Testing Tutorial.
FAQs
How do you extract a token in Postman?
In the login request's Tests or Post-response script, extract the token from the response and store it as a collection variable:
const token = pm.response.json().token;
pm.collectionVariables.set(
'token',
token
);
Later requests can reference it using:
{{token}}
How do you avoid logging in before every request?
Store the token and its expiration time.
Then use a collection-level pre-request script to check:
- Whether a token exists.
- Whether the token is still valid.
- Whether it is close to expiration.
If the token is missing or about to expire, call the authentication endpoint with pm.sendRequest().
This avoids unnecessary login requests.
What should missing or invalid tokens return?
Typically:
- Missing or invalid authentication →
401 Unauthorized - Authenticated user without sufficient permission →
403 Forbidden
However, the exact response depends on the API specification.
Always test against the documented behavior.
How do you test role-based access?
Maintain credentials for different roles and execute the same API requests under each role.
For example:
- Admin
- Editor
- Viewer
Then verify which endpoints each role can access.
Also test whether one user can access another user's resources by modifying identifiers in request paths.
How do you test logout?
Log in, save the token, call a secured endpoint, log out, and then attempt to reuse the same token.
If the API supports token revocation, the reused token should be rejected.
For stateless JWT implementations, the token may remain valid until expiration depending on the application's design.
How do you keep credentials secure in Postman?
Use secret-type environment variables and keep real values only in current values.
For CI execution, inject credentials at runtime rather than committing them to Git.
Never print passwords or access tokens to CI logs.