skip to content

What are ClientRegistration and ClientRegistrationRepository, and how does Spring populate them?

level: middleimportance: must knowfreq 60%

answer

  1. registrationId = key in URL + props
  2. InMemoryClientRegistrationRepository from yml
  3. CommonOAuth2Provider defaults; issuer-uri = discovery
  4. findByRegistrationId lookup
  5. Custom @Bean for multi-tenant/dynamic

basics

~20 s

A ClientRegistration holds the config for one OAuth2 provider — client id/secret, scopes, and the provider's endpoint URLs. ClientRegistrationRepository is the store of all registrations, looked up by registrationId. Spring Boot builds them from your application.yml properties.

solid answer

~40 s

A `ClientRegistration` is the immutable configuration for a single client-provider pairing: the `registrationId` (e.g. 'google'), `clientId`/`clientSecret`, `authorizationGrantType` (usually authorization_code), `redirectUri` template, requested `scopes`, and the provider's endpoints (authorization, token, userinfo, JWK set, issuer). `ClientRegistrationRepository` is the lookup service (`findByRegistrationId`) that Spring uses to resolve which provider a login request targets. Spring Boot auto-configures an `InMemoryClientRegistrationRepository` from `spring.security.oauth2.client.registration.*` and `...provider.*` properties. Well-known providers (Google, GitHub, Facebook, Okta) have built-in defaults via `CommonOAuth2Provider`, so you often only supply client-id/secret. For OIDC providers you can give just an `issuer-uri` and Spring performs OIDC discovery (`/.well-known/openid-configuration`) to fill in the endpoints. You can also define the repository as a `@Bean` to load registrations dynamically (e.g. from a database for multi-tenant SSO).

code

java · 20 lines
java
// Programmatic / dynamic repository (e.g. multi-tenant SSO)
@Bean
ClientRegistrationRepository clientRegistrationRepository() {
    // Discovery: fetches {issuer}/.well-known/openid-configuration
    ClientRegistration okta = ClientRegistrations
        .fromIssuerLocation("https://dev-123.okta.com/oauth2/default")
        .registrationId("okta")
        .clientId("client-id")
        .clientSecret("client-secret")
        .scope("openid", "profile", "email")
        .build();

    ClientRegistration github = CommonOAuth2Provider.GITHUB
        .getBuilder("github")
        .clientId("gh-id")
        .clientSecret("gh-secret")
        .build();

    return new InMemoryClientRegistrationRepository(okta, github);
}

go deeper

for a junior

Know that provider config lives in application.yml and each provider has a registration id.

for a middle

List the key ClientRegistration fields and explain CommonOAuth2Provider defaults and issuer-uri discovery.

for a senior

Explain how to supply a custom repository for dynamic/multi-tenant registrations and the redirect-URI-behind-proxy gotcha.

for a principal

Design tenant-scoped registration resolution, discovery caching/failure handling at startup, and secret management across many providers.

**ClientRegistration — the per-provider config object.** Each external provider you support is represented by one immutable `ClientRegistration`. Its fields: - `registrationId` — the short key ('google', 'github') that appears in URLs like `/oauth2/authorization/{registrationId}` and `/login/oauth2/code/{registrationId}`. - `clientId` / `clientSecret` — credentials issued to your app by the provider. - `clientAuthenticationMethod` — how the client authenticates at the token endpoint (`client_secret_basic`, `client_secret_post`, `none` for public PKCE clients…). - `authorizationGrantType` — for login this is `authorization_code`. - `redirectUri` — a template, default `{baseUrl}/login/oauth2/code/{registrationId}`. - `scopes` — e.g. `openid`, `profile`, `email`. Including `openid` makes it an OIDC flow. - `ProviderDetails` — the provider's `authorizationUri`, `tokenUri`, `jwkSetUri`, `issuerUri`, and `UserInfoEndpoint` (uri + the `userNameAttributeName` claim used as the principal name). **ClientRegistrationRepository — the registry.** An interface with `ClientRegistration findByRegistrationId(String id)`. Filters and services use it to resolve the target provider. The default implementation is `InMemoryClientRegistrationRepository`, built once at startup. It's an `Iterable`, which is how Boot's auto-generated login page enumerates the providers to show. **How Boot populates it.** With `spring-boot-starter-oauth2-client` on the classpath and `spring.security.oauth2.client.registration.<id>.*` properties present, `OAuth2ClientRegistrationRepositoryConfiguration` builds an `InMemoryClientRegistrationRepository`. Two shortcuts: 1. **CommonOAuth2Provider** — for google/github/facebook/okta, endpoint URLs and default scopes are baked in; you only set client-id/secret (and, for a custom provider, a `provider.<id>` block). 2. **OIDC discovery** — set `provider.<id>.issuer-uri` and Spring fetches `{issuer}/.well-known/openid-configuration` at startup to auto-populate authorization/token/userinfo/jwk endpoints. (This means startup makes a network call to the issuer — a gotcha for offline builds/tests.) **Custom / dynamic registration.** For multi-tenant SSO where tenants are added at runtime, provide your own `ClientRegistrationRepository` `@Bean` backed by a database or config service. You can build registrations programmatically via `ClientRegistrations.fromIssuerLocation(issuer)` (does discovery) or the `ClientRegistration.withRegistrationId(...).builder()` API. **Common gotchas.** - `registrationId` must match across the URL, the property key, and the provider's whitelisted redirect URI. - The redirect URI registered at the provider must exactly match the one Spring generates (scheme/host/port/path) — behind a proxy you often need `server.forward-headers-strategy` so `{baseUrl}` resolves to the public HTTPS URL, not the internal one. - Discovery via issuer-uri fails the whole context start if the issuer is unreachable. - Don't confuse `ClientRegistrationRepository` (static provider config) with `OAuth2AuthorizedClientRepository`/`Service` (per-user *obtained tokens*).

  • What is the difference between ClientRegistrationRepository and OAuth2AuthorizedClientRepository?
    ClientRegistrationRepository holds *static provider configuration* (client id/secret, endpoints) keyed by registrationId. OAuth2AuthorizedClientRepository/Service stores the *tokens actually obtained for a specific end-user* (access token, refresh token, the authorized scopes) so they can be reused to call downstream APIs.
  • How does issuer-uri simplify configuration and what's its runtime risk?
    Setting provider.<id>.issuer-uri lets Spring perform OIDC discovery at startup, auto-filling authorization/token/userinfo/JWK endpoints so you don't hand-configure them. The risk: startup makes a network call to the issuer's /.well-known/openid-configuration; if it's unreachable the ApplicationContext fails to start.

saying these in an interview costs you the question

  • Confusing ClientRegistrationRepository with OAuth2AuthorizedClientService (config vs per-user tokens)
  • Thinking you must always hand-write every endpoint URL even for Google/Okta
  • Not realizing issuer-uri triggers a network call at startup
  • Assuming registrationId is arbitrary and need not match the redirect URI path

context