Q30 of 40 · REST Assured
How do you handle OAuth 2.0 client credentials flow in REST Assured?
REST AssuredSeniorrest-assuredoauth2authenticationtoken-managementsecurity
Short answer
Short answer: POST to the token endpoint with client_id, client_secret, and grant_type=client_credentials in @BeforeAll. Cache the token and its expiry time; refresh before it expires using a lazy getter. Inject the token into the shared RequestSpecBuilder header so all tests get it automatically.
Detail
REST Assured has no built-in OAuth 2.0 token management — you implement the token fetch and caching yourself:
private static String accessToken;
private static Instant tokenExpiry;
@BeforeAll
static void fetchToken() {
refreshTokenIfExpired();
}
private static void refreshTokenIfExpired() {
if (accessToken != null && Instant.now().isBefore(tokenExpiry.minusSeconds(60))) return;
JsonPath jp = given()
.contentType("application/x-www-form-urlencoded")
.formParam("grant_type", "client_credentials")
.formParam("client_id", System.getenv("CLIENT_ID"))
.formParam("client_secret", System.getenv("CLIENT_SECRET"))
.formParam("scope", "api:read api:write")
.when()
.post(System.getenv("TOKEN_URL"))
.then()
.statusCode(200)
.extract().jsonPath();
accessToken = jp.getString("access_token");
tokenExpiry = Instant.now().plusSeconds(jp.getLong("expires_in"));
}
Injection: rebuild the RequestSpecification after a token refresh, or use a filter that calls refreshTokenIfExpired() before each request and updates the Authorization header dynamically — the filter approach handles token expiry mid-suite without rebuilding specs.
// EXAMPLE
OAuth2Filter.java
public class OAuth2Filter implements Filter {
private volatile String token;
private volatile Instant expiry = Instant.EPOCH;
@Override
public Response filter(FilterableRequestSpecification req,
FilterableResponseSpecification res,
FilterContext ctx) {
if (token == null || Instant.now().isAfter(expiry.minusSeconds(60))) {
fetchNewToken();
}
req.removeHeader("Authorization");
req.header("Authorization", "Bearer " + token);
return ctx.next(req, res);
}
private synchronized void fetchNewToken() {
if (token != null && Instant.now().isBefore(expiry.minusSeconds(60))) return;
JsonPath jp = given()
.contentType("application/x-www-form-urlencoded")
.formParam("grant_type", "client_credentials")
.formParam("client_id", System.getenv("CLIENT_ID"))
.formParam("client_secret", System.getenv("CLIENT_SECRET"))
.when().post(System.getenv("TOKEN_URL"))
.then().statusCode(200).extract().jsonPath();
token = jp.getString("access_token");
expiry = Instant.now().plusSeconds(jp.getLong("expires_in"));
}
}
// Register in RequestSpecBuilder:
new RequestSpecBuilder().addFilter(new OAuth2Filter()).build();// WHAT INTERVIEWERS LOOK FOR
Building the token fetch as a REST Assured request, caching with expiry (minus a buffer for clock skew), and the filter-based approach for transparent token refresh mid-suite. Thread-safe synchronized refresh is a senior signal.
// COMMON PITFALL
Re-fetching the token on every test method — this adds latency and hammers the auth server. One fetch per suite with a refresh window is the correct pattern.