Spring Boot + OAuth 2 Client Credentials Grant Type
In previous article, we learned about OAuth 2 with Google Authorization Server. In this tutorial, will learn how to customize and create our Authorization Server with Client Credentials Grant Type.
Spring Boot Security - OAuth 2 Tutorial :
OAuth (Open Authorization) is an open standard on the Internet for token-based authentication and authorization. OAuth, allows third-party services, such as Facebook, to use account information from an end-user without exposing the user's Client Credentials.
In OAuth2, grant type is how an application gets the access token. Below are the grant types according to OAuth2 specification:
- Authorization code grant
- Implicit grant
- Resource owner Password Credentials grant
- Client Credentials grant
- Refresh token grant
In this tutorial, will see Client Credentials grant type.
What is Client Credentials grant type?
With the Client Credentials grant type, an app sends its own credentials (the Client ID and Client Secret)
to Authorization Server to generate an access token. If the credentials are valid, Authorization Server will return
an access token to the client app.
Client Credentials grant type flow occurs mainly between a client app and the authorization server.
An end user does not participate or contribute in this grant type flow.
To understand client credentials grant, consider Trivago app, a hotel aggregator portal which will act as a client application. In order to access or get data from makemytrip.com, Trivago Server will authenticate itself by calling makemytrip's authorization server to get access token and then using this token access the makemytrip resource server to get the search result.
Client Credentials Grant Type Flow
The flow shown in above Figure includes the following steps:
- The Client Application requests an access token from the Authorization Server by passing it's credentials.
- The Authorization Server authenticates the client by validating the client_id and client_secret. Once Validation is successful and if request is valid, it sends an access token.
- Client Application sends the received access token to Resource Server to access the resource end point.
- Resource Server validates the access token by calling Authorization Server.
- If the token is valid, resource server return the requested resource to Client Application.
Now, let's explore the example of Client Credentials Grant Type.
Maven Dependencies
Add spring-cloud-starter-oauth2
and spring-boot-starter-oauth2-resource-server
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.4.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.techgeeknext</groupId>
<artifactId>client-credentials-grant</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>authorization-server</name>
<description>Client Credentials Grant Type</description>
<properties>
<java.version>1.8</java.version>
<spring-cloud.version>Hoxton.SR8</spring-cloud.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-oauth2-resource-server</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-oauth2</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</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>
</project>
Project Structure
Authorization Server
Now, let's create and configure Authorization Server by annotating the class with @EnableAuthorizationServer
Authorization Server exposes endpoints for requesting access token (/oauth/token), checking the access token (/oauth/check_token), authorizing the client, etc
package com.techgeeknext.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
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.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter
{
@Override
public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
oauthServer.tokenKeyAccess("isAuthenticated()")
.checkTokenAccess("isAuthenticated()") ;
}
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.inMemory()
.withClient("techgeeknextClient")
.authorizedGrantTypes("client_credentials")
.secret(encoder().encode("pass123"))
.scopes("user_info","read","write")
.redirectUris("http://localhost:8083/techgeeknext/login/oauth2/code/techgeeknextclient")
.autoApprove(false);
}
@Autowired
private AuthenticationManager authenticationManager;
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.authenticationManager(authenticationManager);
}
@Bean
public BCryptPasswordEncoder encoder() {
return new BCryptPasswordEncoder();
}
}
Resource Server
Define the Spring Boot Main Application. Configure the Resource Server using @EnableResourceServer annotation. It means the service expects an access token in order to process the request.package com.techgeeknext;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
@EnableResourceServer
@SpringBootApplication
public class AuthorizationServerApplication {
public static void main(String[] args) {
SpringApplication.run(AuthorizationServerApplication.class, args);
}
}
Now create a resource that client wants to access.
package com.techgeeknext.controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class HelloController {
@GetMapping("/hello")
public String sayHello(Model model, @RequestParam(defaultValue = "TechGeekUser" ,required = false)String name) {
model.addAttribute("name", name);
return "Hello "+name;
}
}
Spring Security Configuration
package com.techgeeknext.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
@Configuration
@Order(1)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/login").permitAll()
.antMatchers("/oauth/authorize")
.authenticated()
.and().formLogin()
.and().requestMatchers()
.antMatchers("/login","/oauth/authorize");
}
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
}
Configure port and context path
logging:
level:
org.springframework.security: DEBUG
server:
port: 8083
servlet:
context-path: /techgeeknext
session:
cookie:
path: /techgeeknext
Take a look at our suggested posts:
Testing Client Credentials Grant Type
- Get the access token by making a POST request to http://localhost:8083/techgeeknext/oauth/token , provide the client_id and client_secret in the Basic Auth header.
- Provide grant_type as client_credentials.
- Now, to access the resource end point, pass the above access token in Authorization Bearer.
Download Source Code
The full source code for this article can be found on below.Download it here - OAuth 2 Client Credentials Grant Type