Careeroza
Setting Up the Server
Dependency
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-oauth2-authorization-server</artifactId>
</dependency>Create a separate Spring Boot app, shopease-auth, running on port 9000. Keep it separate from your APIs: it holds the private signing key.
Configuration
package in.shopease.auth;
import com.nimbusds.jose.jwk.JWKSet;
import com.nimbusds.jose.jwk.RSAKey;
import com.nimbusds.jose.jwk.source.ImmutableJWKSet;
import com.nimbusds.jose.jwk.source.JWKSource;
import com.nimbusds.jose.proc.SecurityContext;
// ... org.springframework imports
@Configuration
@EnableWebSecurity
public class AuthServerConfig {
// 1. The OAuth2 / OIDC protocol endpoints
@Bean
@Order(1)
SecurityFilterChain authorizationServerChain(HttpSecurity http) throws Exception {
OAuth2AuthorizationServerConfigurer authorizationServer =
OAuth2AuthorizationServerConfigurer.authorizationServer();
http
.securityMatcher(authorizationServer.getEndpointsMatcher())
.with(authorizationServer, as -> as.oidc(Customizer.withDefaults())) // enable OpenID Connect
.authorizeHttpRequests(auth -> auth.anyRequest().authenticated())
.exceptionHandling(ex -> ex.defaultAuthenticationEntryPointFor(
new LoginUrlAuthenticationEntryPoint("/login"), // browsers go to the login page
new MediaTypeRequestMatcher(MediaType.TEXT_HTML)));
return http.build();
}
// 2. Everything else: the login page itself
@Bean
@Order(2)
SecurityFilterChain defaultChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests(auth -> auth
.requestMatchers("/login", "/css/**").permitAll()
.anyRequest().authenticated())
.formLogin(form -> form.loginPage("/login"));
return http.build();
}
// 3. Users (in production: a JPA-backed UserDetailsService, see the basic level)
@Bean
UserDetailsService users(PasswordEncoder encoder) {
return new InMemoryUserDetailsManager(
User.withUsername("asha").password(encoder.encode("asha-dev-pass")).roles("CUSTOMER").build(),
User.withUsername("meera").password(encoder.encode("meera-dev-pass")).roles("ADMIN").build());
}
@Bean
PasswordEncoder passwordEncoder() {
return PasswordEncoderFactories.createDelegatingPasswordEncoder();
}
// 4. Signing key (in production: load from a keystore / secret manager, not generated at startup)
@Bean
JWKSource<SecurityContext> jwkSource() throws NoSuchAlgorithmException {
KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA");
generator.initialize(2048);
KeyPair keyPair = generator.generateKeyPair();
RSAKey rsaKey = new RSAKey.Builder((RSAPublicKey) keyPair.getPublic())
.privateKey((RSAPrivateKey) keyPair.getPrivate())
.keyID("shopease-2026-03")
.build();
return new ImmutableJWKSet<>(new JWKSet(rsaKey));
}
// 5. The issuer value that appears in every token's "iss" claim
@Bean
AuthorizationServerSettings authorizationServerSettings() {
return AuthorizationServerSettings.builder().issuer("http://localhost:9000").build();
}
}Note: SecurityContext in JWKSource<SecurityContext> is com.nimbusds.jose.proc.SecurityContext, not Spring's. The OAuth2AuthorizationServerConfigurer.authorizationServer() style is for Spring Authorization Server 1.4+ (Spring Boot 3.4+); older guides call OAuth2AuthorizationServerConfiguration.applyDefaultSecurity(http).
Check It Runs
Discovery
curl -s http://localhost:9000/.well-known/openid-configuration | jq "{issuer, authorization_endpoint, token_endpoint, jwks_uri, grant_types_supported}"Example output
{
"issuer": "http://localhost:9000",
"authorization_endpoint": "http://localhost:9000/oauth2/authorize",
"token_endpoint": "http://localhost:9000/oauth2/token",
"jwks_uri": "http://localhost:9000/oauth2/jwks",
"grant_types_supported": [
"authorization_code",
"client_credentials",
"refresh_token",
"urn:ietf:params:oauth:grant-type:device_code",
"urn:ietf:params:oauth:grant-type:token-exchange"
]
}