OAuth2.0 and Dynamic Client Registration

1. Introduction

In this tutorial, we are going to prepare a dynamic client registration with the OAuth2.0. The OAuth2.0 is an authorization framework that enables obtaining limited access to user accounts on an HTTP service. The OAuth2.0 client is the application that wants to access the user’s account. This client can be an external web application, an user agent or just a native client.

In order to achieve dynamic client registration, we’re going to store the credentials in database, instead of hardcoded configuration. The application we’re going to extend was initially described in Spring REST API + OAuth2 tutorial.

Note: this article is using the Spring OAuth legacy project.

2. Maven Dependencies

We’ll first set up the following set of dependencies:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>    
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-jdbc</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.security.oauth</groupId>
    <artifactId>spring-security-oauth2</artifactId>
</dependency>

Note that we’re using spring-jdbc because we’re going to use a DB to store the newly registered users with passwords.

3. OAuth2.0 Server Configuration

First, we need to configure our OAuth2.0 authorization server. The main configuration is inside the following class:

@Configuration
@PropertySource({ "classpath:persistence.properties" })
@EnableAuthorizationServer
public class OAuth2AuthorizationServerConfig
  extends AuthorizationServerConfigurerAdapter {
    
    // config
}

There are a few major things we need to configure; let’s start with ClientDetailsServiceConfigurer:

@Override
public void configure(final ClientDetailsServiceConfigurer clients) throws Exception {
    clients.jdbc(dataSource())
    
    // ...		
}

This will make sure we’re using persistence to get the client information from.

Let’s of course set up this standard data source:

@Bean
public DataSource dataSource() {
    DriverManagerDataSource dataSource = new DriverManagerDataSource();

    dataSource.setDriverClassName(env.getProperty("jdbc.driverClassName"));
    dataSource.setUrl(env.getProperty("jdbc.url"));
    dataSource.setUsername(env.getProperty("jdbc.user"));
    dataSource.setPassword(env.getProperty("jdbc.pass"));
    return dataSource;
}

And so, now, our application will use the database as a source of registered clients, instead of the typical hard-coded in memory clients.

4. The DB Scheme

Let’s now define the SQL structure for storing our OAuth clients:

create table oauth_client_details (
    client_id VARCHAR(256) PRIMARY KEY,
    resource_ids VARCHAR(256),
    client_secret VARCHAR(256),
    scope VARCHAR(256),
    authorized_grant_types VARCHAR(256),
    web_server_redirect_uri VARCHAR(256),
    authorities VARCHAR(256),
    access_token_validity INTEGER,
    refresh_token_validity INTEGER,
    additional_information VARCHAR(4096),
    autoapprove VARCHAR(256)
);

The most important fields from the oauth_client_details we should focus on are:

  • client_id – to store the id of newly registered clients
  • client_secret – to store the password of clients
  • access_token_validity – which indicates if client is still valid
  • authorities – to indicate what roles are permitted with particular client
  • scope – allowed actions, for example writing statuses on Facebook etc.
  • authorized_grant_types, which provides information how users can login to the particular client (in our example case it’s a form login with password)

Please note, that each client has one to many relationship with users, which naturally means that multiple users can utilize a single client.

5. Let’s Persist Some Clients

With SQL schema define, we can finally create some data in the system – and basically define a client.

We’re going to use the following data.sql script – which Spring Boot will run by default – to initialize the DB:

INSERT INTO oauth_client_details
	(client_id, client_secret, scope, authorized_grant_types,
	web_server_redirect_uri, authorities, access_token_validity,
	refresh_token_validity, additional_information, autoapprove)
VALUES
	("fooClientIdPassword", "secret", "foo,read,write,
	"password,authorization_code,refresh_token", null, null, 36000, 36000, null, true);

The description of the most important fields in oauth_client_details is provided in previous section.

6. Testing

In order to test the dynamic client registration, we need to run both spring-security-oauth-server and spring-security-oauth-resource projects, on the 8081 and 8082 ports, respectively.

Now, we can finally write a few live tests.

Let’s assume, that we registered client with id named fooClientIdPassword, that has an access to read foos.

First, we’ll try to obtain an Access Token from the Auth Server, using an already defined client:

@Test
public void givenDBUser_whenRevokeToken_thenAuthorized() {
    String accessToken = obtainAccessToken("fooClientIdPassword", "john", "123");
    
    assertNotNull(accessToken);
}

And here’s the logic of obtaining the Access Token:

private String obtainAccessToken(String clientId, String username, String password) {
    Map<String, String> params = new HashMap<String, String>();
    params.put("grant_type", "password");
    params.put("client_id", clientId);
    params.put("username", username);
    params.put("password", password);
    Response response = RestAssured.given().auth().preemptive()
      .basic(clientId, "secret").and().with().params(params).when()
      .post("http://localhost:8081/spring-security-oauth-server/oauth/token");
    return response.jsonPath().getString("access_token");
}

7. Conclusion

In this tutorial, we learned how to dynamically register unlimited number of clients with OAuth2.0 framework.

The full implementation of this tutorial can be found over on GitHub – this is a Maven-based project, so it should be easy to import and run as it is.

Please note, that in order to test, you’ll need to add clients into DB, and that the .inMemory() config will be no longer valid. If you want to use the old .inMemory() config, there is a second file containing configuration with hardcoded clients.

Related posts:

Exception Handling in Java
Vấn đề Nhà sản xuất (Producer) – Người tiêu dùng (Consumer) và đồng bộ hóa các luồng trong Java
Java Program to Find Second Smallest of n Elements with Given Complexity Constraint
Java Program to Optimize Wire Length in Electrical Circuit
Converting Strings to Enums in Java
Java Program to Find Whether a Path Exists Between 2 Given Nodes
Java Program to Implement HashSet API
Java – Random Long, Float, Integer and Double
Chuyển đổi Array sang ArrayList và ngược lại
Java Program to Implement Control Table
Java Program to Implement TreeMap API
Java Program to Construct a Random Graph by the Method of Random Edge Selection
Java Program to find the peak element of an array using Binary Search approach
JUnit 5 @Test Annotation
Java Program to Compute Discrete Fourier Transform Using Naive Approach
Java Program to Permute All Letters of an Input String
Spring – Injecting Collections
Java Program to Compute the Area of a Triangle Using Determinants
Comparing getPath(), getAbsolutePath(), and getCanonicalPath() in Java
Java Program to Implement Miller Rabin Primality Test Algorithm
Guide to the Synchronized Keyword in Java
An Example of Load Balancing with Zuul and Eureka
Làm thế nào tạo instance của một class mà không gọi từ khóa new?
Java Program to Generate All Possible Subsets with Exactly k Elements in Each Subset
ETL with Spring Cloud Data Flow
Java Program to Implement the Alexander Bogomolny’s UnOrdered Permutation Algorithm for Elements Fro...
Java Program to Implement Knight’s Tour Problem
Guide to the Java ArrayList
Automatic Property Expansion with Spring Boot
Java Program to Implement Pairing Heap
Java Program to Remove the Edges in a Given Cyclic Graph such that its Linear Extension can be Found
HttpClient Basic Authentication