The authorization code flow lets an application call a protected API on a user’s behalf without asking the application to handle the user’s password. The client sends the user’s browser to an authorization server for login and consent. After the user agrees, the authorization server redirects the browser back with an authorization code. The client exchanges that code for an access token, then sends the token when calling the API. section 8.1 section 8.4
Picture an ingredient-management application. Its client needs permission to write and delete ingredients through a REST API. Three applications have distinct jobs: the client requests permission and calls the API; the authorization server authenticates the user and issues tokens; the API accepts the token for protected requests. The client’s registered scopes specify the permissions it requests. section 8.2 section 8.4
To make that round trip work:
- Register the client with the authorization server using its client ID, secret, authorization-code grant type, redirect URI, requested scopes, and a requirement for user consent. section 8.2
- Configure the client with matching credentials and redirect URI, the requested scopes, and the authorization server’s provider details. Enable OAuth 2 login so an unauthenticated user is sent through authorization. section 8.4
- After consent, exchange the returned code at the token endpoint. Use the resulting bearer access token in requests to the protected API. section 8.2 section 8.4
@Bean
public RegisteredClientRepository registeredClientRepository(
PasswordEncoder passwordEncoder) {
RegisteredClient registeredClient =
RegisteredClient.withId(UUID.randomUUID().toString())
.clientId("taco-admin-client")
.clientSecret(passwordEncoder.encode("secret"))
.clientAuthenticationMethod(
ClientAuthenticationMethod.CLIENT_SECRET_BASIC)
.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)
.authorizationGrantType(AuthorizationGrantType.REFRESH_TOKEN)
.redirectUri(
"http://127.0.0.1:9090/login/oauth2/code/taco-admin-client")
.scope("writeIngredients")
.scope("deleteIngredients")
.scope(OidcScopes.OPENID)
.clientSettings(
clientSettings -> clientSettings.requireUserConsent(true))
.build();
return new InMemoryRegisteredClientRepository(registeredClient);
}
The server-side registration enables AUTHORIZATION_CODE, sets the redirect URI and scopes, and requires consent; these choices define what this client may request. section 8.2
spring:
security:
oauth2:
client:
registration:
taco-admin-client:
provider: tacocloud
client-id: taco-admin-client
client-secret: secret
authorization-grant-type: authorization_code
redirect-uri:
"http://127.0.0.1:9090/login/oauth2/code/{registrationId}"
scope: writeIngredients,deleteIngredients,openid
The client-side YAML selects authorization_code and supplies the credentials, scopes, and callback URI that must line up with its server-side registration. section 8.4
Exchange the code, then call the API
A token request posts grant_type=authorization_code, the redirect URI, and the returned code to /oauth2/token while authenticating the client. The response includes a bearer access token and, in this example, a refresh token. The client can put the access token in an Authorization: Bearer header when adding an ingredient. Access tokens can expire or be revoked; the user’s password need not change when that happens. section 8.2 section 8.1
For a Spring Boot client, spring-boot-starter-oauth2-client and an OAuth 2-enabled SecurityFilterChain handle the login integration. An authorized-client service can retrieve the user’s access token, and a RestTemplate interceptor can attach it to API requests. The authorization code is thus an intermediate step, not the credential sent to the ingredient API. section 8.4







