Setting up an OAuth2 Authorization Server and Resource Provider with Spring Boot

March 13th, 2016 by

OAuth2 is a frequently used standard for authorization and with Spring Boot it is easy to set up authorization and resource server in no time.

In the following short tutorial I’d like to demonstrate how to set up an OAuth2 authorization server as well as a connected and secured resource server within a few minutes using Java, Maven and Spring Boot.

OAuth2 Flow with Spring Boot in Action

OAuth2 Flow with Spring Boot in Action

 

Creating the Authorization Server

We’re using the Spring Initializr to create our initial Maven project including some selected dependencies ..

Setting up the Spring Boot Project using the Spring Initializr

Setting up the Spring Boot Project using the Spring Initializr

This is what our pom.xml looks like (excerpt)..

<project>
	[..]
 
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>1.3.1.RELEASE</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
 
	<properties>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
		<java.version>1.8</java.version>
	</properties>
 
	<dependencies>
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-oauth2</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-security</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
	</dependencies>
 
	<dependencyManagement>
		<dependencies>
			<dependency>
				<groupId>org.springframework.cloud</groupId>
				<artifactId>spring-cloud-starter-parent</artifactId>
				<version>Brixton.M4</version>
				<type>pom</type>
				<scope>import</scope>
			</dependency>
		</dependencies>
	</dependencyManagement>
 
	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>
 
	<repositories>
		<repository>
			<id>spring-snapshots</id>
			<name>Spring Snapshots</name>
			<url>https://repo.spring.io/snapshot</url>
			<snapshots>
				<enabled>true</enabled>
			</snapshots>
		</repository>
		<repository>
			<id>spring-milestones</id>
			<name>Spring Milestones</name>
			<url>https://repo.spring.io/milestone</url>
			<snapshots>
				<enabled>false</enabled>
			</snapshots>
		</repository>
	</repositories>
</project>

The following Java class is all we need to create our authorization server as we’re using an in-memory store with only one oauth client and the server is configured to allow password, auth-code and refresh-token as grant¬† types.

Our user method returns the Principal User and is used by the resource-server later.

package com.hascode.tutorial;
 
import java.security.Principal;
 
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
 
@SpringBootApplication
@RestController
@EnableResourceServer
public class Oauth2AuthorizationServerApplication extends WebMvcConfigurerAdapter {
 
	public static void main(String[] args) {
		SpringApplication.run(Oauth2AuthorizationServerApplication.class, args);
	}
 
	@Configuration
	@EnableAuthorizationServer
	protected static class OAuth2Config extends AuthorizationServerConfigurerAdapter {
		@Autowired
		private AuthenticationManager authenticationManager;
 
		@Override
		public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
			endpoints.authenticationManager(authenticationManager);
		}
 
		@Override
		public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
			clients.inMemory().withClient("foo").secret("foosecret")
					.authorizedGrantTypes("authorization_code", "refresh_token", "password").scopes("openid");
		}
	}
 
	@RequestMapping("/user")
	public Principal user(Principal user) {
		return user;
	}
 
}

In our application.properties we’re adding some configuration for the server port, the credentials used for the basic-auth and our application’s context path:

server.port=9000
security.user.name=bar
security.user.password=barsecret
server.contextPath=/hascode

Minimalistic Spring Boot 1.3 Setup

The following, minimalistic setup is possible in Spring Boot 1.3 if we needed only one client (for demonstration purpose etc.), a detailed description has been documented here in the Spring Blog.

Our application controller would now look like this one:

package com.hascode.tutorial;
 
import java.security.Principal;
 
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
 
@SpringBootApplication
@RestController
@EnableResourceServer
@EnableAuthorizationServer
public class Oauth2AuthorizationServerApplication extends WebMvcConfigurerAdapter {
 
	public static void main(String[] args) {
		SpringApplication.run(Oauth2AuthorizationServerApplication.class, args);
	}
 
	@RequestMapping("/user")
	public Principal user(Principal user) {
		return user;
	}
 
}

And our modified application.properties would look similar to this one:

server.port=9000
security.user.name=bar
security.user.password=barsecret
server.contextPath=/hascode
security.oauth2.client.clientId=foo
security.oauth2.client.clientSecret=foosecret
security.oauth2.client.authorized-grant-types=authorization_code,refresh_token,password
security.oauth2.client.scope=openid

Creating the Resource Provider

Again we’re using the Spring Initializr to create our initial project here..

Setting up the Resource Server Project using the Spring Initializr

Setting up the Resource Server Project using the Spring Initializr

This is an excerpt from our pom.xml:

<project>
 
	[..]
 
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>1.3.1.RELEASE</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
 
	<properties>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
		<java.version>1.8</java.version>
	</properties>
 
	<dependencies>
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-oauth2</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-security</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-security</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
 
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
	</dependencies>
 
	<dependencyManagement>
		<dependencies>
			<dependency>
				<groupId>org.springframework.cloud</groupId>
				<artifactId>spring-cloud-starter-parent</artifactId>
				<version>Brixton.M4</version>
				<type>pom</type>
				<scope>import</scope>
			</dependency>
		</dependencies>
	</dependencyManagement>
 
	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>
 
	<repositories>
		[..]
	</repositories>
</project>

And this is our resource, calling the secured API simply return a success message an a generated UUID.

package com.hascode.tutorial;
 
import java.util.UUID;
 
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
 
@SpringBootApplication
@RestController
@EnableResourceServer
public class SampleResourceApplication {
	public static void main(String[] args) {
		SpringApplication.run(SampleResourceApplication.class, args);
	}
 
	@RequestMapping("/")
	public String securedCall() {
		return "success (id: " + UUID.randomUUID().toString().toUpperCase() + ")";
	}
}

In our application.properties we’re telling our resource server

server.port=9001
server.contextPath=/resource
security.oauth2.resource.userInfoUri: http://localhost:9000/hascode/user

When using Spring Boot <1.3, the last property is named spring.oauth2.resource.userInfoUri!!

OAuth2 Workflow in Action

Assuming we have both server instances up and running, we may now test if accessing our secured resource using OAuth2 is working.

Accessing the resource in a direct GET request should not be possible and yield an error message like this one:

$ curl http://localhost:9001/resource/
{"error":"unauthorized","error_description":"Full authentication is required to access this resource"}

Obtaining the Access Token

Now we’re obtaining our access token from the authorization server:

$ curl -XPOST -k foo:foosecret@localhost:9000/hascode/oauth/token \
   -d grant_type=password -d client_id=foo -d client_secret=abc123 \
   -d redirect_uri=http://www.hascode.com -d username=bar -d password=barsecret
 
{"access_token":"dec6c15a-137f-475a-aa02-530c23943f91","token_type":"bearer","refresh_token":"19b44e18-a25f-427c-9884-ebe6dcec1b96","expires_in":43192,"scope":"openid"}

The response contains our access token, its expiration date, the refresh token (for refresh after expiration), the token type and its scope.

Accessing the Resource

With the obtained access token, we may now access the secured resource like this:

$ TOKEN=dec6c15a-137f-475a-aa02-530c23943f91
$ curl -H "Authorization: Bearer $TOKEN" http://localhost:9001/resource/
success (id: 27DCEF5E-AF11-4355-88C5-150F804563D0)

Refreshing the Access Token

May may refresh our access token using the refresh token received in our first  authorization request:

$ curl -v --data "grant_type=refresh_token&client_id=foo&refresh_token=19b44e18-a25f-427c-9884-ebe6dcec1b96" -k foo:foosecret@localhost:9000/hascode/oauth/token
{"access_token":"12f5c219-1e8b-45b6-be42-304a6833a3b1","token_type":"bearer","refresh_token":"19b44e18-a25f-427c-9884-ebe6dcec1b96","expires_in":43199,"scope":"openid"}%

Tutorial Sources

Please feel free to download the tutorial sources from my Bitbucket repository, fork it there or clone it using Git:

git clone https://bitbucket.org/hascode/spring-oauth2-example.git

Resources

Article Updates

  • 2016-09-14: Added an example for refreshing the access token.

Tags: , , , , , , , , ,

18 Responses to “Setting up an OAuth2 Authorization Server and Resource Provider with Spring Boot”

  1. xuan hoang Says:

    I use postman and POST these options:
    - URL: http://foo:foosecret@localhost:9000/hascode/oauth/token
    or http://localhost:9000/hascode/oauth/token
    - grant_type: password
    - client_id: foo
    - client_secret: abc123
    - username: bar
    - password: barsecret

    It always responses:
    {
    “error”:”unauthorized”,
    “error_description”:”Full authentication is required to access this resource”
    }

    Please help !

  2. tesnik03 Says:

    This one is the best tutorial that i have found for oauth so far!! Thank you!!

  3. Micha Kops Says:

    Thanks, you’re welcome! :)

  4. Sashko Says:

    Thanks for great article!
    I have a question – could we implement this functionality without using Spring Boot and Cloud?

  5. Marcos Says:

    Excellent !! work from Paraguay !

  6. fholisani Says:

    Thanks for this tutorial

    Can you please write the UI login using Angular JS

  7. Daniel Says:

    How I could obtain new token using a refresh token ?

    Thanks!

  8. Micha Kops Says:

    Hi Daniel,

    thanks for reading! I have updated the article including an example how to refresh the oauth access token.

    Cheers

    Micha

  9. Alexey Says:

    Cool! It’s the best tutorial! Thanks!

  10. Marcin Piechota Says:

    I have been digging through StackOverflow and various Spring Tutorials for the last eight hours to solve separation of auth and resource. Finally, I got here. Thank you for the article!

  11. Micha Kops Says:

    Thanks, I’m glad I could be a help for you :)

  12. Ivo Says:

    How can I use datasource here and what tables do I need?

  13. Cesar Says:

    Thanks for your excellent article only I have a comment.

    in the curl instruction you has put

    -d client_id=foo -d client_secret=abc123

    But the secret for food is foodsecret is a little confuse. In order to test this, I change the instruction for
    curl localhost:9000/hascode/oauth/token -d “grant_type=password&username=bar&password=barsecret” -u foo:foosecret

    And it works
    Thanks a lot for your excellent article again

  14. Alex Lopez Says:

    Hi Micha,

    Great post, It has helped my a lot!!! Thank you!!

    Would it be possible to avoid the user and password for token request? is there any other way? I would prefer not to have the password at my mobile App for all the requests.

    Thank you again!

  15. troll Says:

    hi..

    great tutorial!

    can please help,
    im using Postman
    how can i use the token??

  16. hui Says:

    Thanks for great tutorial!

  17. Scott Says:

    Thanks a million! Helped me a lot. Very practical way to start and to work my way from here.

  18. Micha Kops Says:

    Hi Scott, thanks, you’re welcome! :)

Leave a Reply

Please leave these two fields as-is:

Protected by Invisible Defender. Showed 403 to 114,603 bad guys.

Search
Categories