For these protected API calls, the credential is an OAuth 2 access token. Think of it as a ticket: the API checks whether the client may perform an action without asking for the user’s password on every request. An authorization server issues the token with the user’s permission; the client sends it when calling the API. section 8.1
In the authorization code flow:
- The client redirects the user to the authorization server for login and consent. section 8.4
- The authorization server returns an authorization code, which the client exchanges for an access token. section 8.4
- The client sends the token as an Authorization: Bearer header on API requests. section 8.2
{
"access_token":"eyJraWQ...",
"refresh_token":"HOzHA5s...",
"scope":"deleteIngredients writeIngredients",
"token_type":"Bearer",
"expires_in":"299"
}
The token response includes an access_token, its Bearer token type, granted scopes, and an expiry time. It also includes a refresh_token for requesting another access token later. section 8.2
The resource server checks the token before allowing protected requests. In the ingredient API, POST requires the writeIngredients scope and DELETE requires deleteIngredients. Spring Security matches those scopes through SCOPE_-prefixed authorities, inspects JWT claims, and verifies the token’s signature using the authorization server’s public key. A token is therefore more than a string to attach: it must be valid and grant the requested action. section 8.3
@Bean
@RequestScope
public IngredientService ingredientService(
OAuth2AuthorizedClientService clientService) {
Authentication authentication =
SecurityContextHolder.getContext().getAuthentication();
String accessToken = null;
if (authentication.getClass()
.isAssignableFrom(OAuth2AuthenticationToken.class)) {
OAuth2AuthenticationToken oauthToken =
(OAuth2AuthenticationToken) authentication;
String clientRegistrationId =
oauthToken.getAuthorizedClientRegistrationId();
if (clientRegistrationId.equals("taco-admin-client")) {
OAuth2AuthorizedClient client =
clientService.loadAuthorizedClient(
clientRegistrationId, oauthToken.getName());
accessToken = client.getAccessToken().getTokenValue();
}
}
return new RestIngredientService(accessToken);
}
This request-scoped service loads the authenticated client’s access token and passes its value to the REST service that makes API calls. section 8.4
Adding an ingredient
A client can POST a JSON ingredient with Authorization: Bearer $token. Without a suitable token, the protected write request cannot pass the resource server’s check. When the access token expires, the client can use its refresh token to request a new one rather than ask the user to change a password. section 8.3 section 8.2












