What are ClientRegistration and ClientRegistrationRepository, and how does Spring populate them?
answer
- registrationId = key in URL + props
- InMemoryClientRegistrationRepository from yml
- CommonOAuth2Provider defaults; issuer-uri = discovery
- findByRegistrationId lookup
- Custom @Bean for multi-tenant/dynamic
basics
~20 sA 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 sA `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// 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
Know that provider config lives in application.yml and each provider has a registration id.
List the key ClientRegistration fields and explain CommonOAuth2Provider defaults and issuer-uri discovery.
Explain how to supply a custom repository for dynamic/multi-tenant registrations and the redirect-URI-behind-proxy gotcha.
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