Spring Boot + OAuth 2 Password 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 Password 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 password.
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 Resource owner Password Credentials grant type.
What is Password Grant Type?
The Password grant type is a way to exchange a user's username and password for an access token. Since the client application has to collect the user's password and send it to the authorization server.
Password Grant Type Flow
The flow shown in above Figure includes the following steps:
- The resource owner provides the client application with it's username and password.
- The Client Application requests an access token from the Authorization Server by passing credentials received from the resource owner.
- The Authorization Server authenticates the client by validating the resource owner credentials. Once Validation is successful and if request is valid, it sends an access token.
- Client 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.
Now, let's explore the example of Password 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>authorization-server</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>authorization-server</name>
<description>Password Authorization 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("password")
.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
Spring Security Configuration, enables users to easily configure Spring Security. Configure in-memory, user as techgeeknextUser with password testPass
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.core.annotation.Order;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.factory.PasswordEncoderFactories;
import org.springframework.security.crypto.password.DelegatingPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableOAuth2Client;
import javax.sql.DataSource;
@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");
}
@Autowired
private PasswordEncoder passwordEncoder;
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser("techgeeknextUser").password(passwordEncoder.encode("testPass"))
.roles("USER");
}
@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 Password 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, username and password as specified in the WebSecurityConfig class.
- 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 Password Grant Type