Build an OAuth2 Authorization Server with Spring Authorization Server 0.1.0
This guide walks you through setting up Spring Authorization Server 0.1.0 on Spring Boot 2.4.2, covering Maven dependencies, bean configurations, token customization, and testing with curl commands for authorization code flow, token issuance, refresh, and revocation.
Introduction
Spring Authorization Server is the Spring team’s latest project for OAuth‑compatible authorization servers, intended to replace the older Spring Security OAuth.
After six months of development, version 0.1.0 is released, supporting authorization code, client, refresh, and revocation endpoints.
This article uses Spring Boot 2.4.2 and authorization‑server 0.1.0.
Server Setup
1. Maven Dependencies
<code><!--oauth2 server-->
<dependency>
<groupId>org.springframework.security.experimental</groupId>
<artifactId>spring-security-oauth2-authorization-server</artifactId>
<version>0.1.0</version>
</dependency>
<!--security dependency-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
</code>2. Initial Configuration
Because the official Spring Boot starter is not yet available, you need to define the required @Bean components manually.
The configuration shown is based on Spring Boot 2.4.2.
<code>@Configuration
@EnableWebSecurity
@Import(OAuth2AuthorizationServerConfiguration.class)
public class AuthServerConfiguration {
// Define Spring Security filter chain
@Bean
SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception {
http.authorizeRequests(authorize -> authorize.anyRequest().authenticated())
.formLogin(withDefaults());
return http.build();
}
// Default in‑memory user
@Bean
public UserDetailsService userDetailsService() {
UserDetails user = User.builder()
.username("lengleng")
.password("{noop}123456")
.authorities("ROLE_USER")
.build();
return new InMemoryUserDetailsManager(user);
}
// Default client supporting authorization code and refresh token
@Bean
public RegisteredClientRepository registeredClientRepository() {
RegisteredClient client = RegisteredClient.withId("pig")
.clientId("pig")
.clientSecret("pig")
.clientAuthenticationMethod(ClientAuthenticationMethod.BASIC)
.authorizationGrantTypes(grants -> {
grants.add(AuthorizationGrantType.AUTHORIZATION_CODE);
grants.add(AuthorizationGrantType.REFRESH_TOKEN);
})
.redirectUri("https://pig4cloud.com")
.build();
return new InMemoryRegisteredClientRepository(client);
}
// RSA key pair for token signing
@Bean
@SneakyThrows
public JWKSource<SecurityContext> jwkSource() {
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(UUID.randomUUID().toString())
.build();
JWKSet jwkSet = new JWKSet(rsaKey);
return (jwkSelector, securityContext) -> jwkSelector.select(jwkSet);
}
}
</code>Testing
Authorization Code Request
<code>curl --location --request GET 'http://localhost:3000/oauth2/authorize?client_id=pig&client_secret=pig&response_type=code&redirect_uri=https://pig4cloud.com'</code>Token Retrieval
<code>curl --location --request POST 'http://localhost:3000/oauth2/token' \
--header 'Authorization: Basic cGlnOnBpZw==' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'grant_type=authorization_code' \
--data-urlencode 'code={code}' \
--data-urlencode 'redirect_uri=https://pig4cloud.com'</code>Refresh Token
<code>curl --location --request POST 'http://localhost:3000/oauth2/token' \
--header 'Authorization: Basic cGlnOnBpZw==' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'grant_type=refresh_token' \
--data-urlencode 'refresh_token={refresh_token}'</code>Revoke Token
Revoke via access token
<code>curl --location --request POST 'http://localhost:3000/oauth2/revoke' \
--header 'Authorization: Basic cGlnOnBpZw==' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'token={access_token}' \
--data-urlencode 'token_type_hint=access_token'</code>Revoke via refresh token
<code>curl --location --request POST 'http://localhost:3000/oauth2/revoke' \
--header 'Authorization: Basic cGlnOnBpZw==' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'token={refresh_token}' \
--data-urlencode 'token_type_hint=refresh_token'</code>Extended Content | Token Customization
RegisteredClient supports custom token settings via tokenSettings().
<code>RegisteredClient..tokenSettings()</code>Default settings include token lifetime and refresh token reuse control.
<code>protected static Map<String, Object> defaultSettings() {
Map<String, Object> settings = new HashMap<>();
settings.put(ACCESS_TOKEN_TIME_TO_LIVE, Duration.ofMinutes(5));
settings.put(REUSE_REFRESH_TOKENS, true);
settings.put(REFRESH_TOKEN_TIME_TO_LIVE, Duration.ofMinutes(60));
return settings;
}</code>Conclusion
Source code: https://github.com/lltx/auth-server-demo
Since official documentation is still incomplete, refer to the OAuth 2.0 Authorization Framework for endpoint parameters.
macrozheng
Dedicated to Java tech sharing and dissecting top open-source projects. Topics include Spring Boot, Spring Cloud, Docker, Kubernetes and more. Author’s GitHub project “mall” has 50K+ stars.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.