skip to content

What is the difference between OAuth2AuthorizedClientManager, OAuth2AuthorizedClientProvider, OAuth2AuthorizedClientService, and OAuth2AuthorizedClientRepository?

level: seniorimportance: should knowfreq 45%

answer

  1. Provider = one grant; Manager = orchestrate; Service = app store; Repository = per-request
  2. Default manager needs request context; ServiceManager for background
  3. ProviderBuilder.clientCredentials().refreshToken()
  4. Repository delegates to Service (authed) or HttpSession (anon)
  5. InMemory vs Jdbc service

basics

~10 s

Manager orchestrates authorizing a client; Provider does the actual grant (code/client_credentials/refresh); Service stores authorized clients application-wide; Repository is the per-request/session store that (by default) delegates to the Service for logged-in users.

solid answer

~40 s

These are four cooperating roles. **`OAuth2AuthorizedClientProvider`** implements a single grant strategy — one per grant type (authorization_code, client_credentials, refresh_token, jwt-bearer). **`OAuth2AuthorizedClientManager`** is the entry point you call: it loads any existing client, runs the provider chain to authorize/re-authorize, and persists the result. Two manager flavours: `DefaultOAuth2AuthorizedClientManager` (needs a request context, uses the Repository) and `AuthorizedClientServiceOAuth2AuthorizedClientManager` (context-free, for background jobs, uses the Service). **`OAuth2AuthorizedClientService`** is the application-wide persistent store (in-memory or JDBC), keyed by registrationId + principalName. **`OAuth2AuthorizedClientRepository`** is the web-tier store bound to `HttpServletRequest`; the default delegates to the Service for authenticated principals and to the HttpSession for anonymous ones. So: Provider = how to get a token, Manager = orchestration, Service/Repository = where tokens are kept.

code

java · 25 lines
java
// Background/service-layer manager (no HttpServletRequest available)
@Bean
OAuth2AuthorizedClientManager authorizedClientManager(
        ClientRegistrationRepository registrations,
        OAuth2AuthorizedClientService service) {

    OAuth2AuthorizedClientProvider providers =
        OAuth2AuthorizedClientProviderBuilder.builder()
            .clientCredentials()
            .refreshToken()
            .build();

    AuthorizedClientServiceOAuth2AuthorizedClientManager manager =
        new AuthorizedClientServiceOAuth2AuthorizedClientManager(registrations, service);
    manager.setAuthorizedClientProvider(providers);
    return manager;
}

// Usage from a @Scheduled job:
OAuth2AuthorizeRequest req = OAuth2AuthorizeRequest
        .withClientRegistrationId("my-api")
        .principal("system")   // stable principal name for M2M
        .build();
OAuth2AuthorizedClient client = manager.authorize(req);
String token = client.getAccessToken().getTokenValue();

go deeper

for a junior

Just know Manager coordinates and Service stores tokens.

for a middle

Distinguish Provider (grant) from Manager (orchestration) and Service from Repository.

for a senior

Explain the two manager types and their context requirements, plus the repository's delegate-to-service behaviour.

for a principal

Weigh Jdbc vs in-memory service for clustering, custom provider composition, and correct manager choice for background vs request threads.

## The four roles, precisely ### 1. `OAuth2AuthorizedClientProvider` — the grant executor Each provider knows **one** grant. Given an `OAuth2AuthorizationContext` it returns a new/updated `OAuth2AuthorizedClient` or `null` (meaning 'not my job / nothing to do'): - `AuthorizationCodeOAuth2AuthorizedClientProvider` — only *reports* that user redirect is needed (throws `ClientAuthorizationRequiredException`); it doesn't do the redirect itself. - `ClientCredentialsOAuth2AuthorizedClientProvider` — machine-to-machine token via client id/secret. - `RefreshTokenOAuth2AuthorizedClientProvider` — swaps a refresh token for a fresh access token when the current one is expired. - `JwtBearerOAuth2AuthorizedClientProvider`, and the deprecated `PasswordOAuth2AuthorizedClientProvider`. You compose them with **`OAuth2AuthorizedClientProviderBuilder`**: `.authorizationCode().refreshToken().clientCredentials().build()`. The builder returns a `DelegatingOAuth2AuthorizedClientProvider` that tries each in order. ### 2. `OAuth2AuthorizedClientManager` — the orchestrator You call `manager.authorize(OAuth2AuthorizeRequest)`. It: 1. Loads the existing client (from Repository or Service). 2. Builds an `OAuth2AuthorizationContext` and invokes the provider chain. 3. On a returned client, runs the configured success handler which **saves** it; on failure, the failure handler (which may **remove** it). Two implementations differ by context: - **`DefaultOAuth2AuthorizedClientManager`** — servlet, request-scoped, uses `OAuth2AuthorizedClientRepository`. This is the one the `@RegisteredOAuth2AuthorizedClient` resolver uses by default. It expects the current `HttpServletRequest`/`HttpServletResponse` and `Authentication`. - **`AuthorizedClientServiceOAuth2AuthorizedClientManager`** — for use **outside** a request (scheduled tasks, message listeners). It uses the `OAuth2AuthorizedClientService` directly and does not require web context. Ideal for `client_credentials` background calls. ### 3. `OAuth2AuthorizedClientService` — application-wide store Persists `OAuth2AuthorizedClient`s keyed by (registrationId, principalName). Implementations: `InMemoryOAuth2AuthorizedClientService` (default) and `JdbcOAuth2AuthorizedClientService` (survives restarts / is shared across instances). Methods: `loadAuthorizedClient`, `saveAuthorizedClient`, `removeAuthorizedClient`. ### 4. `OAuth2AuthorizedClientRepository` — the web-tier store Binds storage to the current `HttpServletRequest`. The default **`AuthenticatedPrincipalOAuth2AuthorizedClientRepository`** delegates: for an authenticated principal it uses the `OAuth2AuthorizedClientService`; for an anonymous user it falls back to `HttpSessionOAuth2AuthorizedClientRepository` (session-scoped). This lets pre-login/anonymous flows work before there's a stable principal to key on. ## How they fit together `@RegisteredOAuth2AuthorizedClient` → `OAuth2AuthorizedClientArgumentResolver` → **Manager** → **Provider chain** → result saved via **Repository** → (Repository) → **Service**. ## Common gotchas - Using `DefaultOAuth2AuthorizedClientManager` from a background thread fails — no request context; use the service-based manager. - If you build a custom manager you must wire a `setAuthorizedClientProvider(...)`; otherwise it authorizes nothing. - In-memory service loses tokens on restart and isn't shared across nodes — use JDBC in production clusters. - The refresh provider only refreshes; it never performs the *initial* authorization. ## When to use which manager Request-driven, user-scoped API calls → Default (repository) manager. Background/system-to-system → Service manager.

  • Which manager do you use in a @Scheduled task and why?
    AuthorizedClientServiceOAuth2AuthorizedClientManager — it works without an HttpServletRequest and reads/writes through the OAuth2AuthorizedClientService directly, which suits background client_credentials calls.
  • Why does the default repository fall back to the HttpSession for anonymous users?
    Because there is no stable principalName yet to key an application-wide Service entry, so tokens obtained before login are kept in the session and migrated once the principal is authenticated.

saying these in an interview costs you the question

  • Treating Manager and Service as the same thing.
  • Using DefaultOAuth2AuthorizedClientManager from a non-request thread.
  • Forgetting to set a provider on a hand-built manager (it then authorizes nothing).
  • Relying on in-memory service across a clustered/restarting deployment.

context