Running an Authorization Server
What Spring Authorization Server is, a minimal setup, and registering clients - the difference between issuing tokens and merely validating them.
On this page
Most Spring apps are resource servers or clients - they validate tokens or obtain them. Running an authorization server is the other side: the party that authenticates users and mints tokens in the first place. Spring Authorization Server is the official project for this. Understanding what it is (and when you actually need it) matters as much as the config.
Issuing vs. validating
Keep three responsibilities distinct - they're three different Spring modules:
- Resource Server (
spring-boot-starter-oauth2-resource-server) - validates incoming tokens and protects endpoints. This is what BookVault's API did with its HS256 shortcut. - Client (
spring-boot-starter-oauth2-client) - obtains tokens by driving a login flow ("Sign in with Google"). - Authorization Server (
spring-security-oauth2-authorization-server) - issues tokens: authenticates the user, runs the grants, exposes the OAuth2/OIDC endpoints.
You only run an authorization server when you want to be the identity provider - your own login, your own tokens, multiple apps trusting one issuer. If you just want users to log in with Google or Okta, you're a client, not an authorization server.
A minimal server
Spring Authorization Server is a set of Spring Security filters you enable and configure. The core is a security filter chain that applies the OAuth2/OIDC endpoint defaults:
@Configuration
class AuthServerConfig {
@Bean
@Order(1)
SecurityFilterChain authServer(HttpSecurity http) throws Exception {
OAuth2AuthorizationServerConfigurer.authorizationServer()
.oidc(Customizer.withDefaults()); // enable OpenID Connect
return http
.securityMatcher("/oauth2/**", "/.well-known/**", "/userinfo")
.with(OAuth2AuthorizationServerConfigurer.authorizationServer(), c -> c.oidc(withDefaults()))
.build();
}
@Bean
@Order(2)
SecurityFilterChain login(HttpSecurity http) throws Exception {
return http.formLogin(withDefaults()).build(); // how users authenticate
}
}Turning it on exposes the standard endpoints automatically: /oauth2/authorize, /oauth2/token,
/oauth2/jwks (the public keys), and the discovery document at
/.well-known/openid-configuration that clients read to auto-configure.
Registering clients
An authorization server won't issue tokens to anyone - each app must be a registered client with an ID, (for confidential clients) a secret, its allowed redirect URIs, grant types, and scopes:
@Bean
RegisteredClientRepository clients(PasswordEncoder encoder) {
RegisteredClient bookvaultSpa = RegisteredClient.withId(UUID.randomUUID().toString())
.clientId("bookvault-web")
.clientAuthenticationMethod(ClientAuthenticationMethod.NONE) // public client (SPA)
.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)
.redirectUri("https://app.bookvault.dev/callback") // exact match required
.scope(OidcScopes.OPENID).scope("catalog:read")
.clientSettings(ClientSettings.builder().requireProofKey(true).build()) // PKCE
.build();
return new InMemoryRegisteredClientRepository(bookvaultSpa);
}Every field is a security boundary: the exact redirect URI stops token theft via open redirects, the grant types limit what the client may do, and the scopes cap what its tokens can access.
In-memory is for demos only
InMemoryRegisteredClientRepository and in-memory keys reset on restart and don't work across replicas. In production, back the client repository with a database (JdbcRegisteredClientRepository) and load signing keys from a real keystore or secret manager - never generate them fresh on boot, or every restart invalidates every issued token.
A resource server is a border guard: it inspects passports and waves people through, but it doesn't make passports. An authorization server is the passport office: it verifies your identity at the counter and issues the document itself. Registering a client is the office's list of recognized travel agencies - only an agency on the list, using the exact address on file, may request passports on travelers' behalf. Running the office is a bigger commitment than standing at the border, which is why you do it only when you truly need to be the issuer.
Two scenarios: (a) an internal company portal where employees log in once and access five internal Spring apps with the same account; (b) a hobby app where you just want visitors to sign in with their GitHub accounts. Which needs you to run a Spring Authorization Server, and which makes you an OAuth2 client - and why?
When do you actually need to run a Spring Authorization Server (rather than being a client or resource server)?
Key takeaways
- Three distinct roles/modules: resource server validates tokens, client obtains them, authorization server issues them.
- Run a Spring Authorization Server only when you want to be the identity provider - your own login and tokens trusted by multiple apps.
- Enabling it exposes standard endpoints automatically: /oauth2/authorize, /oauth2/token, /oauth2/jwks, and the /.well-known discovery document.
- Every app must be a registered client with an ID, optional secret, exact redirect URIs, grant types, and scopes - each field is a security boundary.
- In-memory clients and boot-generated keys are demo-only; production needs a JDBC client repository and keys from a real keystore/secret manager.