Authorization Code Grant Flow With Spring Security OAuth 2.0
In this tutorial, learn how to implement a Spring authorization server using Spring Security OAuth2.
Join the DZone community and get the full member experience.
Join For FreeWhat Is an Authorization Code Grant?
According to the OAuth 2.0 specification, an authorization code grant flow is a two-step process mainly used by confidential clients (a web server or secured application that can promise the security of credentials).
OAuth 2.0 is an open standard authorization protocol that enables secure and delegated access to resources on the web. It allows users to grant limited access to their resources (such as profiles or data) to third-party applications without sharing their credentials. OAuth 2.0 is widely used for authentication and authorization in modern web and mobile applications.
Spring Authorization Server is an OAuth 2.0 and OpenID Connect (OIDC) compliant authorization server built on the Spring framework designed to simplify the implementation of secure and standardized authorization protocols. Introduced as part of the Spring Security ecosystem, the Spring Authorization Server facilitates the centralized management of authorization policies and access control for distributed applications. It supports various grant types, enabling different authentication flows such as authorization codes, client credentials, and refresh token grants. The server's modular architecture and integration with Spring Boot make it easy to configure and customize, providing developers with a flexible and scalable solution for managing authentication and authorization in their Spring-based applications.
DZone’s previously covered OAuth 2.0 specifications and how we can implement OAuth 2.0 client credentials grant flow working with Spring's authorization server. In this article, we're going to see how we can implement an authorization code grant flow and get it working with Spring Security. In the first step, we'll request the authorization endpoint to get an authorization code from the authorization server and then use it to get an access token from the authorization server at the token endpoint.
Getting Started
Please make sure that you have all these dependencies in your pom.xml.
x
<dependencies>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-jwt</artifactId>
<version>1.1.1.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.security.oauth</groupId>
<artifactId>spring-security-oauth2</artifactId>
<version>2.5.0.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.security.oauth.boot</groupId>
<artifactId>spring-security-oauth2-autoconfigure</artifactId>
<version>2.4.0</version>
</dependency>
</dependencies>
spring-security-oauth2
has all core dependencies required for OAuth, and spring-security-jwt
is for jwt
support in oauth2
. The auto-configure dependency is required for auto-configuration, and if you don't want to include this one, you will have to add some jaxb
dependencies to get it working.
Enabling Authorization Server Support
To enable the support for the authorization server, you would need to add an annotation on top of @SpringBootApplication
: @EnableAuthorizationServe
.
xxxxxxxxxx
public class SpringAuthorizationServerApplication {
public static void main(String[] args) {
SpringApplication.run(SpringAuthorizationServerApplication.class, args);
}
}
Overriding Authorization Server's Default Configuration
To override the default configuration of Spring's Authorization Server, we will need to extend our configuration class with AuthorizationServerConfigurerAdapter
. To reduce the code and effort for demonstration purposes, we will be using in-memory client configuration. The configuration should look similar to what I have here.
xxxxxxxxxx
"deprecation") (
public class AuthServerConfig extends AuthorizationServerConfigurerAdapter {
("passwordEncoder")
BCryptPasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients
.inMemory()
.withClient("clientId")
.secret(passwordEncoder().encode("client-secret"))
.scopes("read", "write")
.authorizedGrantTypes("authorization_code", "refresh_token")
.redirectUris("http://localhost:8081/oauth/login/client-app")
.autoApprove(true);
}
JwtTokenStore getAccessTokenConverter() {
return new JwtTokenStore(JwtTokenEnhancer.getInstance());
}
}
Please make sure that you've marked your class @Configuration
so that it can be picked by Spring Security OAuth2.
As discussed, authorization code grant flow is for confidential clients; one can guarantee the security of credentials. So here, we have used the BCryptPassword
encoder to encode our credentials. That's the reason I've defined a bean of BCrypt.
To configure client details, we will need to override a configure method that contains ClientDetailsServiceConfigurer
, and using in-memory configuration, we can add the required details.
The token store bean that you see I used to customize the jwt
token. All you have to do is extend your token converter class from JwtAccessTokenConverter
and define a bean in the Authorization Server config to tell the Auth Server to use your configuration for jwt
.
Configuring Spring Security
Till now, all we did was for the Authorization Server. Let's add some Spring Security configurations to add users that we will be authenticating. Extend your class from the WebSecurityConfigurer
adapter like so:
x
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
("passwordEncoder")
BCryptPasswordEncoder passwordEncoder;
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser("username")
.password(passwordEncoder.encode("password"))
.roles("USER");
}
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.anyRequest()
.authenticated().and()
.formLogin().permitAll()
.and()
.logout().permitAll();
http.csrf().disable();
}
}
It's a normal Spring Security configuration for form login, and we've used in-memory user storage. With that being finished, we're good to start testing the application.
Getting Authorization Code
To get the authorization code, we need to request the server and redirect you to the auth server's login page if you're not authenticated. To get the code, we hit /oauth/authorize
with a few required params.
response_type
: Must set to code (required)client_id
:clientId
that we set up in the auth server (required)state
: Some random value to maintain state between server and client (optional)redirect_uri
: Optionallocalhost:8080/oauth/authorize?response_type=code&client_id=clientId&state=8781487s1
: The link will redirect you to a login page and after successful login, it will redirect you to the redirect link that we had set up in the auth server with some params (http://localhost:8081/oauth/login/client-app?code=EXUdZm&state=8781487s1).
The core value that we see in the response parameter is the authorization code that we will use later to access the access token and refresh token from the auth server. Learn more tips for OAuth2 token validation.
Access Token Exchange Authorization Code Example
To get an access token and refresh token, we will need to make a post request with clientId
and client-secret
in basic auth header with a few params.
Here are some code samples of a token request and response:
x
POST /oauth/token?grant_type=authorization_code& code=X2KnGB& client_Id=clientId& state=8781487s1 HTTP/1.1
Host: localhost:8080
Authorization: Basic Y2xpZW50SWQ6Y2xpZW50LXNlY3JldA==
cache-control: no-cache
Once, you make this post request you will get a response similar to this:
xxxxxxxxxx
{
"access_token": "U9c7fB35jHh6vW-WsBd-VdfLcOs",
"token_type": "bearer",
"refresh_token": "hGMk8bgNwO7YHrKaCZkc380BB68",
"expires_in": 43199,
"scope": "read write"
}
That's all! You can use this token to access protected resources. Since I've signed this token using RSA private and public keys, that's the reason it's different from a normal jwt
token. Further review how to secure Spring Boot microservices with JSON web tokens.
I would like you to see how I implemented an access token converter.
x
public class JwtTokenEnhancer extends JwtAccessTokenConverter {
private static final String PRIVATE_KEY = "Your private rsa key";
private static final String PUBLIC_KEY = "Your public rsa key";
public JwtTokenEnhancer(String publicKey, String privateKey) {
super.setSigningKey(privateKey);
super.setVerifierKey(publicKey);
}
public static JwtAccessTokenConverter getInstance() {
return new JwtTokenEnhancer(JwtTokenEnhancer.PUBLIC_KEY, JwtTokenEnhancer.PRIVATE_KEY);
}
}
Remember, it's wrapped into a token store bean that we've defined in auth server configurations. With that being said, thank you so much for taking the time to read this post, and I will be coming up with some Spring Security 5 OAuth2.0 articles. This project is available on GitHub.
Opinions expressed by DZone contributors are their own.
Comments