QA How-To
Testing bearer token refresh (2026)
Learn testing bearer token refresh across expiry, rotation, replay, concurrency, scope, revocation, logout, and CI with safe, runnable Node.js OAuth tests.
21 min read | 3,907 words
TL;DR
Testing bearer token refresh requires more than a 200 from the token endpoint. Prove the new access token works only where authorized, then test expiry, scope, client binding, rotation, replay, concurrency, revocation, logout, and secret-safe client storage.
Key Takeaways
- A refresh flow exchanges a refresh token for a new access token, it does not modify the old access token.
- Use form-encoded token requests and the client authentication method registered for that client.
- A successful token response must be followed by allowed and forbidden resource-server assertions.
- Refresh cannot expand scope, audience, tenant, subject, or client authorization beyond the original grant.
- Rotation tests must verify old-token reuse, token-family policy, atomic persistence, and response-loss recovery.
- Client single-flight coordination prevents refresh storms and accidental replay during concurrency.
- Disposable token families, strict redaction, host allowlists, and grant revocation keep CI tests safe.
Testing bearer token refresh means proving that an authorized client can exchange a valid refresh token for a usable access token, while expired, revoked, replayed, wrong-client, and over-scoped requests fail safely. It also means verifying rotation, concurrency, storage, logout, and failure recovery, not merely checking that the token endpoint returns 200.
Strictly speaking, an OAuth client does not refresh the existing bearer access token. It presents a refresh token to the authorization server and receives a new access token, which may be another bearer token. That distinction matters because the two credentials have different audiences, lifetimes, storage rules, and replay risks.
TL;DR
| Scenario | Expected result | Security evidence |
|---|---|---|
| Valid refresh | New access token works at intended resource | Scope and audience remain authorized |
| Expired access token | Resource rejects it, refresh can recover if grant remains valid | No premature or unlimited access |
| Invalid refresh token | Token endpoint returns safe OAuth error | No token or account detail leaks |
| Rotated token reuse | Old token is rejected and replay policy activates | Token family cannot continue silently |
| Wrong client | Refresh is denied | Token remains bound to issued client |
| Scope expansion | Broader request is denied or reduced | Refresh cannot escalate privilege |
| Concurrent refresh | Documented single-flight or replay behavior | No race creates uncontrolled token family |
| Logout or revocation | Further refresh fails within policy | Residual access window is understood |
Test with disposable grants and short lifetimes in an approved environment. Never log access tokens, refresh tokens, client secrets, authorization codes, or complete token endpoint bodies.
1. Define Testing Bearer Token Refresh Precisely
OAuth separates the authorization server, which issues tokens, from the resource server, which accepts access tokens. A refresh token is sent only to the authorization server's token endpoint. An access token is sent to the resource server. Confusing those destinations creates both test defects and credential leakage.
Write the lifecycle as states: access token valid, access token near expiry, access token expired, refresh token valid, refresh in progress, rotated refresh token active, old refresh token invalidated, grant revoked, and reauthorization required. Add client identity, user or subject, authorized scope, resource or audience, and token family to the model.
Core invariants include:
- Refresh never increases the authorization originally granted.
- A refresh token is accepted only by the intended token endpoint and client.
- A newly issued access token works only at its intended resource and scope.
- Rotation invalidates or supersedes the prior token according to policy.
- Replay detection cannot be bypassed through concurrency or another endpoint.
- Revocation and security events stop future refresh within the documented objective.
- Credentials never enter URLs, logs, traces, browser history, or test artifacts.
RFC 6749 defines the refresh grant request. RFC 9700, published as OAuth 2.0 Security Best Current Practice, requires replay detection for refresh tokens issued to public clients through sender constraint or rotation. Use the profile and metadata published by the authorization server rather than assuming every deployment issues refresh tokens.
This precision gives testing bearer token refresh a clear oracle: new authorization is usable, no broader than approved, and the old credential state changes exactly as promised.
2. Understand the Standards-Based Refresh Exchange
The OAuth token request uses POST and application/x-www-form-urlencoded. The body contains grant_type=refresh_token, the refresh token, and optionally scope when the server's policy allows requesting a narrower or equal set. Confidential clients also authenticate using the registered method. Do not send a JSON body unless the provider explicitly defines a nonstandard profile.
An illustrative request is:
POST /oauth2/token HTTP/1.1
Host: identity.example.test
Content-Type: application/x-www-form-urlencoded
Authorization: Basic base64-of-client-id-and-secret
grant_type=refresh_token&refresh_token=opaque-secret-value
The successful response commonly contains access_token, token_type, expires_in, and possibly scope and a replacement refresh_token. The client must not assume a new refresh token is always returned. If rotation is enabled and one is returned, store it atomically before discarding the old value.
OAuth error responses use stable codes such as invalid_grant or invalid_client. The precise status and client authentication behavior depend on the specification and server profile. Tests should assert public codes and safe shape, not fragile descriptions. Token responses should include cache prevention appropriate to the server contract, and intermediaries must never cache secrets.
Bearer tokens are possession credentials. RFC 6750 requires protection from disclosure in transport and storage. Sender-constrained access tokens such as DPoP or mutual TLS add proof, but they need their own profile-specific tests and should not be treated as ordinary bearer tokens.
Discover the token endpoint and supported client authentication method through the provider's trusted configuration or metadata when applicable. Never accept an untrusted endpoint from a token, error message, or user-controlled field.
3. Design a Refresh Token State and Risk Matrix
Create disposable grants representing confidential and public clients, user and workload contexts if both exist, minimal and broader approved scopes, multiple resources, active and disabled users, and rotation-enabled policies. Avoid one shared refresh token across parallel tests because every successful rotation can invalidate another worker's fixture.
Cross state with expected behavior:
| Refresh token state | Same client | Wrong client | Requested broader scope |
|---|---|---|---|
| Active | Success | Deny | Deny or return only allowed scope |
| Rotated old value | Deny, activate replay policy | Deny | Deny |
| Expired | Deny | Deny | Deny |
| Revoked grant | Deny | Deny | Deny |
| Wrong resource grant | Follow resource indicator policy | Deny | Never expand resource access |
| Malformed or unknown | Safe denial | Safe denial | Safe denial |
Add timing states: well before access expiry, just before expiry, at expiry, and just after. Use controlled short lifetimes or an injected clock. Client clocks can differ from server clocks, so the client should rely on issued metadata and handle a resource-server rejection without an infinite retry loop.
Define success across three surfaces. First, token endpoint contract: correct status, media type, required fields, safe caching headers, and rotation result. Second, token semantics: scope, audience or resource, subject, lifetime, issuer where inspectable, and no unexpected privilege. Third, actual use: the new access token succeeds at an allowed endpoint and fails at a forbidden resource or operation.
For more token-specific negative coverage, use JWT authentication testing when access tokens are JWTs. Opaque tokens should be treated as opaque by clients.
4. Build a Runnable Node.js Refresh Test
The following example uses built-in Node.js APIs, form encoding through URLSearchParams, the stable test runner, and strict assertions. It targets an approved QA authorization server and resource server. It deliberately avoids decoding the access token because token format is not guaranteed.
import test from 'node:test';
import assert from 'node:assert/strict';
const tokenEndpoint = new URL(process.env.OAUTH_TOKEN_ENDPOINT ?? '');
const resourceUrl = new URL(process.env.OAUTH_RESOURCE_URL ?? '');
const refreshToken = process.env.OAUTH_QA_REFRESH_TOKEN;
const clientId = process.env.OAUTH_CLIENT_ID;
const clientSecret = process.env.OAUTH_CLIENT_SECRET;
const allowedHosts = new Set([
'localhost',
'127.0.0.1',
'identity.qa.example.test',
'api.qa.example.test',
]);
assert.ok(allowedHosts.has(tokenEndpoint.hostname), 'Unapproved token endpoint');
assert.ok(allowedHosts.has(resourceUrl.hostname), 'Unapproved resource host');
assert.ok(refreshToken, 'OAUTH_QA_REFRESH_TOKEN is required');
assert.ok(clientId, 'OAUTH_CLIENT_ID is required');
assert.ok(clientSecret, 'OAUTH_CLIENT_SECRET is required');
async function refresh(value, scope) {
const form = new URLSearchParams({
grant_type: 'refresh_token',
refresh_token: value,
});
if (scope !== undefined) form.set('scope', scope);
const basic = Buffer.from(`${clientId}:${clientSecret}`).toString('base64');
const response = await fetch(tokenEndpoint, {
method: 'POST',
headers: {
authorization: `Basic ${basic}`,
'content-type': 'application/x-www-form-urlencoded',
accept: 'application/json',
},
body: form,
signal: AbortSignal.timeout(8_000),
});
const payload = await response.json();
return { response, payload };
}
test('refresh issues an access token that can call the intended resource', async () => {
const issued = await refresh(refreshToken);
assert.equal(issued.response.status, 200);
assert.equal(issued.payload.token_type.toLowerCase(), 'bearer');
assert.equal(typeof issued.payload.access_token, 'string');
assert.ok(issued.payload.access_token.length > 0);
assert.ok(Number.isFinite(issued.payload.expires_in));
assert.ok(issued.payload.expires_in > 0);
const resource = await fetch(resourceUrl, {
headers: {
authorization: `Bearer ${issued.payload.access_token}`,
accept: 'application/json',
},
signal: AbortSignal.timeout(8_000),
});
assert.equal(resource.status, 200);
});
test('unknown refresh token is rejected without returning credentials', async () => {
const rejected = await refresh(`qa_unknown_${crypto.randomUUID()}`);
assert.equal(rejected.response.status, 400);
assert.equal(rejected.payload.error, 'invalid_grant');
assert.equal(Object.hasOwn(rejected.payload, 'access_token'), false);
assert.equal(Object.hasOwn(rejected.payload, 'refresh_token'), false);
});
OAUTH_TOKEN_ENDPOINT=https://identity.qa.example.test/oauth2/token \
OAUTH_RESOURCE_URL=https://api.qa.example.test/v1/me \
OAUTH_QA_REFRESH_TOKEN=secret-from-fixture \
OAUTH_CLIENT_ID=qa-client \
OAUTH_CLIENT_SECRET=secret-from-ci \
node --test bearer-token-refresh.test.mjs
This confidential-client example uses HTTP Basic client authentication because that is the test provider's assumed registration. Use the method registered for your client. Do not copy the pattern to a public client that cannot keep a secret.
5. Verify the Successful Exchange and New Access Token
A 200 response is the beginning of the oracle. Assert access_token is a non-empty string, token_type matches the supported scheme case-insensitively, and expires_in is a positive number if the provider includes it. If scope is returned, compare it as a space-delimited set rather than an ordered string unless order is contractually fixed.
Use the new access token against a safe protected resource. Assert the expected subject or tenant through the resource response, not by trusting the refresh response alone. Then call an operation outside the granted scope and expect denial. If the system supports several audiences or resource indicators, try the token at the intended and unintended resource servers.
When access tokens are JWTs and the test is authorized to inspect them, validate issuer, audience, subject, times, and scope through a trusted library or the same verification policy as the resource server. Do not write custom cryptography or treat decoded claims as authenticated. For opaque tokens, do not invent claims by parsing their characters.
Compare authorization before and after refresh. The client should not gain scope, role, tenant, or audience. If permissions were reduced since initial authorization, determine whether refresh reflects current policy immediately or after a documented delay. Test role removal and account disablement.
Check lifetime boundaries without asserting exact issuance timestamps across independent clocks. Allow only the documented tolerance. Ensure a new token does not accidentally retain the old access token's nearly expired timestamp.
If the response includes a refresh token, classify whether it is a new value and save it through a secure fixture mechanism. Do not print either value for equality diagnostics. Report only safe token-family identifiers when available.
6. Test Access Token Expiry and Client Refresh Timing
Clients often refresh proactively shortly before expiry and reactively after a 401. Test both paths. Use an access token with a short QA lifetime, call before expiry, cross the boundary with an injected clock or bounded wait, confirm the resource rejects it, refresh, and verify the new token succeeds.
Avoid exact wall-clock sleeps in routine CI. A test authorization server with configurable lifetime or clock control is more deterministic. If a real wait is unavoidable, keep it short, record server-issued expires_in, and allow for network latency without creating a large security grace assumption.
Client logic needs a single retry boundary. When a resource returns the specific invalid-token response, the client may refresh once and repeat the original idempotent or safely replayable request. It must not refresh forever if the token endpoint fails or the new access token is rejected. For non-idempotent business requests, the client must follow the API's idempotency contract before retrying.
Test clock skew. Set the client clock slightly ahead or behind through dependency injection and verify proactive timing remains within policy. The authorization and resource servers are authoritative for token validity. A client should not conclude a token is valid merely because its local timer has not expired.
Background refresh can extend sessions unexpectedly. Test browser sleep, mobile suspension, restored network, and long-running worker behavior. An absolute authorization or session lifetime must not be extended indefinitely by repeated access-token refresh unless the product explicitly permits it.
Record why refresh occurred: proactive threshold, resource rejection, application restart, or concurrent request. Safe metrics can reveal refresh storms without storing credentials or subject identifiers as unbounded labels.
7. Test Rotation, Reuse Detection, and Token Families
Refresh token rotation issues a new refresh token on each successful exchange and invalidates the presented one. The authorization server retains enough family state to detect later reuse. RFC 9700 requires public-client replay detection through rotation or sender-constrained refresh tokens.
Test rotation with disposable state:
- Exchange refresh token R1 and receive access token A1 plus R2.
- Use A1 successfully at the intended resource.
- Exchange R2 and receive A2 plus R3.
- Replay R1.
- Expect denial and the documented family response.
- Try R3 and verify whether the family was revoked, challenged, or otherwise handled by policy.
The server cannot reliably know whether the legitimate client or an attacker presented the reused token. A strong policy can revoke the active family and force fresh authorization. Test the exact product decision. Do not assert that only the old token fails if the security design intentionally revokes descendants.
Check atomicity. If the server returns R2 but fails to persist the transition from R1, both may work. If it persists invalidation but the response is lost, the legitimate client may retain only unusable R1. Some systems provide a short grace or idempotent result for immediate retry, but grace weakens replay detection and must be deliberate, bounded, and tested.
Rotation also needs storage atomicity in the client. Simulate a crash after receiving the response but before saving R2. Verify recovery behavior is understood. A client should not keep several refresh tokens as a casual fallback because that defeats rotation semantics.
Test revocation by family, device, and grant where supported. Revoking one mobile device should not necessarily end another device's grant unless policy says all sessions.
8. Exercise Concurrent Refresh Requests
Concurrency is one of the most important refresh tests. Multiple browser tabs, API calls, mobile workers, or server threads can observe an expired access token and all try to refresh with the same refresh token. With rotation, one request may win and the others may look like replay.
At the client layer, implement single-flight behavior: one refresh promise or lock per token family, with waiting requests consuming the result. Test ten simulated protected calls and assert the client makes one token request, then retries eligible resource calls with the same new access token.
At the authorization server layer, coordinate two exchanges of R1 as closely as practical. Acceptable behavior must be documented. Common policies are one success plus one invalid_grant, or a bounded idempotent response for an immediately repeated request. The dangerous outcome is two independent successor refresh tokens that both remain valid without family control.
Use a barrier in a component or integration test to make overlap deterministic. Timing two network calls does not guarantee both reach the critical section together. Inspect safe family state and attempt each returned successor. Avoid relying only on status arrays.
Then test response loss. Complete refresh server-side but make the client miss the response. A retry with R1 may trigger replay protection. The system needs a defined balance between security and recoverability, and the client may need reauthorization.
Do not solve refresh races by disabling rotation globally without risk review. Client coordination, sender-constrained tokens, family state, and carefully bounded server behavior provide more targeted options.
9. Test Scope, Audience, Client, and Subject Binding
A refresh token represents the authorization granted to a particular client. For confidential clients, authenticate the client at refresh. Present the token with the wrong client ID or wrong client authentication and expect denial. Test a changed redirect URI only if the provider profile includes it in refresh processing; do not invent parameters.
Request the same scope, a subset, an omitted scope, an unknown scope, and a broader scope. The refresh cannot silently expand beyond the original grant. The server may deny a broader request or issue only allowed scope according to its policy. Compare scope as a set and verify behavior at resource endpoints.
Audience and resource binding matter when one authorization server serves multiple APIs. A token for the profile API must not gain billing API access through refresh. If the server implements resource indicators, test only the parameters and rules in that profile. Otherwise assert the issued token's actual resource acceptance.
Subject changes are never a normal refresh result. Disable the user, move the user to another tenant, remove a role, or delete the service account in a controlled environment. Verify refresh denies or returns updated authorization according to policy. A refresh token for user A must not be combinable with client state for user B.
Do not trust client-supplied tenant or user headers at the token endpoint. Identity comes from the grant and authenticated client. Test conflicting metadata and ensure it is ignored or safely rejected.
For services that accept both OAuth bearer tokens and API keys, keep the schemes separate. Supplying two credentials should follow one documented precedence or be rejected. Permissions must not be accidentally unioned.
10. Test Errors, Revocation, Logout, and Security Events
Negative requests include missing grant_type, wrong grant type, missing refresh token, duplicate parameters, malformed form encoding, oversized value within safe bounds, unknown token, expired token, revoked token, wrong client, failed client authentication, and unsupported scope. Assert stable OAuth error codes, no returned credentials, cache-safe response handling, and no secret reflection.
Different errors can intentionally use different codes, but they should not expose token owner, subject, exact revocation reason, or verifier internals to an unauthenticated caller. invalid_grant commonly covers invalid, expired, revoked, or wrong-client grant conditions. Client authentication failures follow their registered method and server profile.
Test revocation through the supported mechanism. Prove refresh works, revoke the refresh token or grant, then poll through different authorization server nodes until the documented objective. Further refresh must fail. Existing access tokens may remain valid until their short expiration depending on architecture. State that residual window clearly.
Logout can be local client cleanup, authorization-server session logout, token revocation, or all three. Verify the product's promise. Clearing browser storage without revoking a reusable refresh token leaves server authorization intact. Conversely, revoking one device grant should not necessarily sign out every device.
RFC 9700 notes security events such as password change or logout as reasons an authorization server may revoke refresh tokens. Test password reset, account disablement, MFA reset, risk flag, and consent withdrawal where supported.
Audit and alert events should use safe token identifiers or family IDs, not credential values. Test reuse detection alerts, repeated invalid grants, unusual client behavior, and revocation. Keep event volume bounded in QA.
The API error handling and negative testing guide can help standardize assertions without overfitting error descriptions.
11. Protect Refresh Tokens in Clients, Logs, and Test Systems
Refresh tokens are high-value because they can mint new access tokens for a long period. Test where each client type stores them. A confidential backend can use a protected server-side secret store. Public native and browser clients need architecture-specific controls and should follow current OAuth guidance. A JavaScript test proving a value exists in local storage is not proof that storage is safe.
Search for a disposable canary token across access logs, application logs, reverse-proxy traces, error monitoring, distributed traces, metrics labels, browser history, URLs, analytics, screenshots, CI output, test reports, and dead-letter queues. Redaction should happen before export. UI masking does not erase a secret already retained by a collector.
Trigger error paths: token endpoint timeout, malformed response, JSON parse failure, client exception, resource 401, and test assertion failure. Client libraries often dump request bodies during these cases. The form body contains the refresh token and must never be printed.
Avoid placing tokens in query parameters or path segments. Use TLS and validate endpoint identity. Do not follow arbitrary redirects from a token endpoint with credentials attached. The test fetch call can use redirect: 'manual' when redirect behavior is being checked.
Backups, crash reports, mobile debug logs, browser sync, and support exports are less obvious sinks. Use a threat model appropriate to the client. Test token deletion on logout, account removal, application data clear, and device revocation.
In CI, provision one disposable grant per worker. Inject credentials only into trusted jobs, mask environment output, restrict artifact access, and revoke grants in teardown. If teardown fails, an external cleanup job should revoke by safe run identifier.
12. Operationalize Testing Bearer Token Refresh in CI
Separate provider and client responsibilities. Authorization-server component tests cover grant validation, client binding, rotation atomicity, replay policy, revocation, and errors. Resource-server tests cover access-token validation, audience, scope, and expiry. Client tests cover proactive timing, single-flight coordination, atomic storage, one-retry limits, and reauthorization fallback. End-to-end tests prove the complete journey with a small number of disposable grants.
Pull requests should run valid refresh, invalid grant, scope non-escalation, new-token resource access, and a deterministic client single-flight test. Main or scheduled jobs can add rotation families, concurrent exchanges, response loss, distributed revocation, short-lifetime boundaries, telemetry canaries, and identity-provider outages.
Do not let parallelism corrupt fixtures. Rotation changes state, so each test owns its refresh token family. Make cleanup idempotent and revoke the entire disposable grant after the suite. Avoid test retries that reuse a consumed token without resetting state.
Reports should include provider, client registration identifier, safe grant or family identifier, requested scope, result code, resource assertion, revision, node or region, and correlation ID. Never include token strings, client secrets, or raw form bodies.
Track useful health measures such as refresh success by safe client class, invalid-grant rate, reuse events, revocation latency, and client refresh amplification. Avoid user IDs and token values as metric labels.
Testing bearer token refresh becomes reliable when the suite treats tokens as stateful security credentials rather than interchangeable strings. Every success changes what can safely happen next.
Interview Questions and Answers
Q: What is actually refreshed in an OAuth refresh flow?
The client exchanges a refresh token for a new access token. The existing access token is not modified. The response may also rotate the refresh token, so the client must update stored state atomically.
Q: What do you assert after a successful refresh response?
I assert required response fields and safe caching behavior, then use the access token at the intended resource. I verify subject, scope, tenant, and audience behavior and confirm forbidden operations still fail. A 200 from the token endpoint alone is insufficient.
Q: How do you test refresh token rotation?
I exchange R1 for R2, then R2 for R3, and replay R1. I assert the old token is denied and verify the documented token-family response, which may revoke descendants and require reauthorization. I also test atomic state when a response is lost.
Q: Why are concurrent refresh requests risky?
Several callers can use the same refresh token before rotation state is visible. That can produce false replay signals or multiple valid successors. I test client single-flight coordination and deterministic server concurrency, then inspect which successor tokens remain valid.
Q: Can refresh request a broader scope?
It cannot gain authorization beyond the original grant. The server can reject a broader scope request or restrict the result according to policy. I compare scope as a set and verify actual resource permissions.
Q: What error do you expect for an invalid refresh token?
OAuth commonly uses invalid_grant for an invalid, expired, revoked, or otherwise unusable refresh token. I follow the provider's standards-based profile and assert that no credentials or sensitive state are returned. I avoid relying on detailed error descriptions.
Q: Does logout invalidate every access token immediately?
Not always. Logout may revoke the refresh token or server session while self-contained access tokens remain valid until short expiry. I clarify the architecture, test future refresh denial, and measure the maximum promised residual access window.
Q: How do you test refresh securely in CI?
I create a disposable grant per worker, inject secrets only into trusted jobs, allowlist hosts, redact all token endpoint data, and revoke the grant afterward. Rotation tests never share a family. Reports use safe identifiers and correlation IDs only.
Common Mistakes
- Saying an access token itself is extended or modified by refresh.
- Sending a JSON token request when the provider expects form encoding.
- Passing the refresh token to a resource server or access token to a refresh grant.
- Checking token endpoint status without using the new access token.
- Decoding JWT claims and treating them as validated.
- Assuming a replacement refresh token is always present.
- Saving the new refresh token non-atomically after rotation.
- Sharing one rotating token family across parallel CI tests.
- Allowing every expired request to trigger its own concurrent refresh.
- Retrying refresh indefinitely after
invalid_grant. - Logging raw form bodies, tokens, or client secrets on failure.
- Expecting logout to revoke self-contained access tokens instantly without an architectural mechanism.
Conclusion
Testing bearer token refresh is a stateful security exercise. Validate the standards-based exchange, use the new access token, prove scope and client binding, exercise expiry, rotation, replay, concurrency, revocation, logout, and telemetry handling, and understand the residual access window.
Start with one disposable grant and map every token state before automating. Add one test per transition, ensure each test owns its token family, and make reauthorization the explicit outcome when secure recovery from rotation or replay is no longer possible.
Interview Questions and Answers
What is refreshed in the OAuth refresh flow?
A refresh token is exchanged for a newly issued access token. The existing access token is not extended in place. A rotated refresh token may also be returned and must be stored atomically.
What do you assert after refresh succeeds?
I assert the standards-based response fields, then use the new access token at its intended resource. I verify subject, tenant, scope, audience, and forbidden access. The token endpoint status alone is not enough.
How do you test rotation and replay?
I advance through R1, R2, and R3, then replay R1. I verify denial and the documented family action, which may invalidate descendants. I also cover response loss and persistence atomicity.
Why do refresh races occur?
Several callers can observe expiry and present the same refresh token concurrently. With rotation, only one may be valid and others can trigger reuse handling. I test client single-flight behavior and deterministic authorization-server overlap.
How do you test scope during refresh?
I request the same scope, a subset, an unknown scope, and a broader scope. I compare returned scope as a set and call allowed and forbidden resources. Refresh must never expand the original grant.
What is the expected error for an invalid refresh token?
OAuth commonly uses invalid_grant for an invalid, expired, revoked, or unusable refresh token. I follow the provider profile and verify no credentials or sensitive token state are returned. I do not depend on detailed descriptions.
How do you test logout and refresh revocation?
I prove refresh works, perform the supported logout or revocation, and poll across authorization-server nodes until the stated objective. Further refresh must fail. I separately test any residual access-token lifetime.
How do you keep refresh tests secure in CI?
Each worker owns a disposable token family, secrets enter only trusted jobs, hosts are allowlisted, and logs never contain token endpoint bodies. Cleanup revokes the grant. Reports use safe family identifiers and correlation IDs only.
Frequently Asked Questions
What happens during bearer token refresh?
The client presents a refresh token to the authorization server and receives a new access token. The old access token is not modified. The response may also contain a rotated refresh token.
What content type does an OAuth refresh request use?
The OAuth refresh grant uses application/x-www-form-urlencoded with grant_type=refresh_token and the refresh token. Use the client authentication method registered for that client rather than assuming every client has a secret.
How do you test refresh token rotation?
Exchange R1 for R2, then R2 for R3, and replay R1. Assert safe denial and the documented token-family response, then test whether the active descendant remains valid or the family requires reauthorization.
What is refresh token reuse detection?
It identifies presentation of a rotated or otherwise invalidated refresh token. With rotation, reuse can indicate theft, so the authorization server may revoke the active token family and require fresh authorization.
How do you test concurrent token refresh?
At the client, trigger several expired requests and assert one token exchange through single-flight coordination. At the server, deterministically overlap two exchanges and verify the documented winner, denial, and successor-token state.
Can a refresh token request more scope?
It can request scope only within the original authorization, subject to server policy. It must never increase privilege. Tests should compare scope as a set and verify actual resource access.
Does logout revoke access tokens immediately?
Not necessarily. Logout may revoke refresh capability while a short-lived self-contained access token remains usable until expiry. Test the documented residual window and prove future refresh is denied.