Implementing OAuth2 with Spring Security

I would share my notes on understanding how to set up Spring Security to implement OAuth2. My ultimate goal is to implement an authority provider (Authorization Server in OAuth2 terminology) to support multiple microservices. In this post, I will describe step by step on how to setup Spring Security with OAuth2 and demonstrate how a web server client should interact with the Oauth2 servers.

OAuth2 Roles

OAuth2 consists of the following “roles”:

  1. User / Resource Owner – an entity capable of granting access to a protected resource.
  2. Resource Server – server hosting the protected resources, capable of accepting and responding to protected resource requests using access token
  3. Client – An application making requests to protected resources on behalf of the owner. It can be a web app server, a mobile app, or a client side (e.g. javascript) application.
  4. Authorization Server – Server issuing access tokens to client after successfully authentication the resource owner and obtaining authorization.

Note:

  1. Many common servers, e.g. Facebook, Google APIs, implement both the Authorization and Resource Servers.
  2. Depending on the type of clients, the interaction with the OAuth2 servers varies. This blog will focus on a web server client. Aaron Parecki’s blog post provides a concise description of OAuth2 and the interactions required by various client applications

Spring Security OAuth2 Configuration

The codes used in this blog post are largely taken from the sample here, with some minor additions/changes.

Authorization Server

The codes to configure an authorization server are shown below.

@Configuration
@ComponentScan
@EnableAutoConfiguration
@RestController
public class Application {

 private static final String RESOURCE_ID = "blog_resource";

 public static void main(String[] args) {
      SpringApplication.run(Application.class, args);
 }

 @Configuration
 @EnableAuthorizationServer // [1]
 protected static class OAuth2Config extends AuthorizationServerConfigurerAdapter {

      @Autowired
      private AuthenticationManager authenticationManager;

      @Override // [2]
      public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
           endpoints.authenticationManager(authenticationManager);
      }

      @Override // [3]
      public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
           // @formatter:off
           clients.inMemory()
           .withClient("client-with-registered-redirect")
           .authorizedGrantTypes("authorization_code")
           .authorities("ROLE_CLIENT")
           .scopes("read", "trust")
           .resourceIds(RESOURCE_ID)
           .redirectUris("http://anywhere?key=value")
           .secret("secret123")
           .and()
           .withClient("my-client-with-secret")
           .authorizedGrantTypes("client_credentials", "password")
           .authorities("ROLE_CLIENT")
           .scopes("read")
           .resourceIds(RESOURCE_ID)
           .secret("secret");
           // @formatter:on
      } 

 }
}

Note:

  1. The convenient annotation @EnableAuthorizationServer is used. The server is customized by extending the class AuthorizationServerConfigurerAdapter which provides empty method implementations for the interface AuthorizationServerConfigurer. See the javadoc here for more information.
  2. By default, the authorization server does not secure the authorization end point (/oauth/authorize). The configure method here injects the Spring Security authentication manager (set up in @EnableWebSecurity as in normal Spring Security)
  3. The configure method here setup the clients that can access the server. An in memory client detail service is used here for demo purpose.

Resource Server

The codes to configure a resource server are shown below

 @RequestMapping("/") //[1]
 public String home() {
      return "Hello World";
 }

 @Configuration
 @EnableResourceServer // [2]
 protected static class ResourceServer extends ResourceServerConfigurerAdapter {

      @Override // [3]
      public void configure(HttpSecurity http) throws Exception {
           // @formatter:off
           http
           // Just for laughs, apply OAuth protection to only 2 resources
           .requestMatchers().antMatchers("/","/admin/beans").and()
           .authorizeRequests()
           .anyRequest().access("#oauth2.hasScope('read')"); //[4]
           // @formatter:on
      }

      @Override
      public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
           resources.resourceId(RESOURCE_ID);
      }

 }

Note:

  1. For testing purpose, a resource end point is included here
  2. Like the Authorization Server, the convenient annotation @EnableResourceServer is used with a bean that extends ResourceServerConfigurerAdapter. See javadoc here for more details
  3. The configure method shows how to setup resources for OAuth2 protection.
  4. Spring Security’s expression based support is used here, i.e. #autho2.hasScope(). An expression handler is registered by default by @EnableResourceServer.

Web Server Client

Now with the OAuth2 servers setup, we can demonstrate how a web server client can access protected resource on behalf of the end user via OAuth2. It consists of the following sequence of interactions:

  1. Client redirects user to the authorization server. User login and approve client access to the resource
  2. Authorization server redirects back to client with the access code
  3. Client exchange the access code with an access token from the authorization server
  4. Client uses the access token to get resource from  the resource server

I won’t have a client web server implemented here but will use a chrome rest client plugin to issue the requests to the oauth2 servers. You may use curl to send the equivalent requests.

Also, for the following to work, you will need to setup normal Spring Security and have a login page so that the end user can login with his credential at the oauth2 server so that he can approve the client for accessing the resource on his behalf. For example, as shown in the codes here.

1: Client redirects user to the authorization server. User login and approve client access to the resource

The client redirects the user to the following URL:

http://localhost:8080/oauth/authorize?
     response_type=code
     &client_id=client-with-registered-redirect
     &redirect_url=http://client_host?key=value
     &scope=read

Running the above on your browser should redirect it to the oauth2 server’s login page. Once the user enters and submits his username and password, The OAuth approval page should display as below. I will need to work out how to customize this page later…

blogOauth2Approve

2: Authorization server redirects back to client with the access code

When the user clicks “Approve”, the authorization server will redirect back to the client url as defined in the redirect_url parameter of the original request, together with an authorization code, i.e.

http://client_host/?key=value&code=3X42jv

Since we don’t have a client server implemented,  the above will cause browser error. But we do now has the authorization code from the authorization server.

3: Client exchange the access code with an access token from the authorization server

Now the client has to exchange the access code with the authorization server for an access token by the following:

POST http://localhost:8080/oauth/token
Header: Authorization: Basic Y2xpZW50LXdpdGgtcmVnaXN0ZXJlZC1yZWRpcmVjdDpzZWNyZXQxMjM=
Payload: grant_type=authorization_code&code=3X42jv

Note that base authorization is set up by default with the @EnableAuthroizationServer annotation for the token endpoint. The string “Y2xp…M=” is the Base64 encoded text of the client’s id and password of the form <client_id>:<client_secret>, i.e.  client-with-registered-redirect:secret123. Ignoring this header will result in the oauth2 server returning an “Full authentication is required to access this resource” error. 

The authorization server should return something like below:

{
     access_token: "cd515d9d-56b1-4ef6-ae99-317d8975f292     token_type: "bearer     expires_in: 43199 
     scope: "read"
}

4: Client uses the access token to get resource from  the resource server

With the access token, the client can now get the resource on behalf of the user. Remember we set up 2 toy resources to be protected under oauth2 when setting up the resource server? Let’s try go get hold of it

GET http://localhost:8080/ 
Header : Authorization: Bearer cd515d9d-56b1-4ef6-ae99-317d8975f292

It should return “Hello World” in the response body.

That’s it. We finally got the resource after all these interactions!

A few words on grant type

The client here uses authorization code grant type. As demonstrated above, this means the authorization code is obtained using the authorization server as an intermediate between the client and the resource owner. Note the resource onwer’s credential is never shared with the client. Also, the access token is passed directly from the authorization server to the ciient without going through resource owner’s user-agent, adding a level of security.

You may notice the authorization server config also includes a client my-client-with-secret with grant type client_credentials. This is intended for the client to get access token for accessing its own account. For example:

POST http://localhost:8080/oauth/token
Header: Authorization: Basic bXktY2xpZW50LXdpdGgtc2VjcmV0OnNlY3JldA==
Payload: grant_type=client_credentials

Another interesting grant type is implicit, which is intended for client-side (e.g. browse) client.

Wrap Up…for now

That’s it for now. OAuth2 is a flexible protocol and this article only describe a rather simple but hopefully typical use of it in securing web resources. Also, most of Spring Security support for OAuth2 is not explored here. I wish to cover both in future posts.