In this blog, we will learn about OAuth2 and how we can secure our REST API using OAuth2 with Spring JPA in the Spring Boot application. Spring boot oauth2 rest API example. #spring boot + oauth2 + mysql
OAuth2 provides authorization flows for web and desktop applications, and mobile devices.
OAuth2 is an authorization framework that enables applications to obtain limited access to user accounts on an HTTP service, such as Facebook, GitHub, and DigitalOcean.
The sample applications developed using Spring Boot and Spring Security demonstrate versatility and ease of enhancement. These apps, principally leveraging OAuth 2.0 for authentication, can be customized for specific requirements primarily through simple changes to their configuration files. Such adjustments enable the apps to adapt to different use cases without the need for extensive coding or structural changes. It’s important to ensure that when these applications are deployed, new client credentials are obtained for the respective host addresses from services like GitHub. Additionally, it’s crucial to securely manage these credentials, avoiding their exposure in source control systems.
The use of WebClient in the context of OAuth2 authentication when accessing the GitHub API is aimed at effectively verifying the authentication details of a user. Essentially, WebClient facilitates the process by which the application communicates with the GitHub API to confirm a user’s membership in a specific organization. Through this process, WebClient sends requests to retrieve data regarding the user’s affiliated GitHub organizations. If the data shows that the user does not belong to the designated organization, the application will deny access, typically through a 401 unauthorized error. This method strengthens security by ensuring that only members of certain approved organizations are granted access.
To extend the authentication rule in a Spring Boot application to reject users who do not belong to a specific organization on GitHub, you can define a custom `OAuth2UserService`. This service is responsible for loading and examining the user’s organization membership during the authentication process. Here’s an outline of how you can implement this:
1. **Create a Bean of `OAuth2UserService`:** Start by defining a new bean that implements `OAuth2UserService`. This will intercept the OAuth2 authentication process to add custom verification logic.
2. **Delegate Default User Service:** Inside the service, instantiate `DefaultOAuth2UserService` which is the standard service for user details loading. Use this service to delegate the loading of the OAuth2 user details.
3. **Identify Github Users:** After loading the user details, check if the authentication request comes from GitHub by examining `getClientRegistration().getRegistrationId()` of the `OAuth2UserRequest`. Proceed with further checks only if it matches “github“.
4. **Extract Organization URL:** Get the URL pointing to the user’s organizations from the OAuth2 user’s attributes (e.g., `user.getAttribute(“organizations_url”)`).
5. **Call GitHub API to Fetch Organizations:** Use `WebClient` to send a GET request to the specified organization URL. Attach the necessary OAuth2 credentials to the request. Deserialize the response into a list of organization maps.
6. **Check Organization Membership:** Analyze the response to determine if the user belongs to the required organization, such as “spring-projects”. This can be done by streaming through the list of organizations and checking for a matching ‘login’ attribute.
7. **Handle Unauthorized Users:** If the user is not found in the required organization, throw an `OAuth2AuthenticationException`. This will reject the login attempt, sending an appropriate error response back to the user.
8. **Return the Authenticated User:** If all conditions are satisfied (correct GitHub user and correct organization membership), return the authenticated `OAuth2User` object, completing the custom authentication process.
By following these steps, your Spring Boot application will only authenticate users who are part of a specified organization on GitHub, effectively using the GitHub API to enforce organization-based access control in your application. This implementation relies heavily on Spring Security’s OAuth2 client and its given capabilities to customize user authentication flows involving external systems like GitHub.
To generate a 401 response in a server when a user fails to authenticate with GitHub using Spring Security, follow this approach:
1. **Leverage Spring Security’s built-in mechanisms**: Spring Security automatically produces a 401 Unauthorized response whenever an authentication attempt fails, such as when the user rejects a token grant from GitHub. This default behavior handles basic authentication failures.
2. **Customize the authentication process**: For more specific scenarios, such as restricting access to users from specific GitHub organizations, extend the authentication process using Spring Boot’s customization features. Implement this by defining a custom `OAuth2UserService`.
3. **Define a custom `OAuth2UserService` Bean**: Create a Spring Bean that returns a custom `OAuth2UserService`. This service should extend the behavior of the default `OAuth2UserService` provided by Spring Security.
– Use the `DefaultOAuth2UserService` to fetch user details. – Extract the `organizations_url` from the user’s attributes to query GitHub for the user’s organizations. – Call the GitHub API to retrieve a list of organizations the user belongs to, using a `WebClient` instance configured with the user’s OAuth2 credentials.
4. **Check organization membership**: In the custom `OAuth2UserService`, after retrieving the organizations, check if the user is a member of the required organization (e.g., “spring-projects”). If the user is not found within the specified organization:
– Throw an `OAuth2AuthenticationException` with an appropriate error code and description, such as `invalid_token`. – Spring Security catches this exception and converts it into a 401 Unauthorized response.
5. **Configure WebClient and HTTP Client**: Ensure the `WebClient` used for calling the GitHub API is properly configured to include the necessary credentials and headers to authenticate the request.
By following these steps, the server can effectively enforce organizational access control via GitHub authentication, generating a 401 response when the user fails to meet the specified criteria. This method not only secures the application but also allows for flexibility by adjusting conditions under which a user is considered authenticated.
To capture an error message when authentication fails in a Spring Boot application, you need to implement a custom authentication failure handling mechanism. Here is a step-by-step approach:
1. **Configure an Authentication Failure Handler**: In your Spring Security configuration, use the `HttpSecurity` object to set up an `AuthenticationFailureHandler`. This handler will be invoked whenever the authentication process fails.
Start by adding to your existing security configuration a failure handler within the `oauth2Login` method. In the lambda function provided to `failureHandler`, use the request session to store the exception message. This involves obtaining the exception message and setting it as a session attribute.
Example: “`java protected void configure(HttpSecurity http) throws Exception { http .oauth2Login(o -> o .failureHandler((request, response, exception) -> { request.getSession().setAttribute(“error.message”, exception.getMessage()); // Optionally forward to a default failure handler // handler.onAuthenticationFailure(request, response, exception); }) ); } “`
2. **Create an Error Controller**: After configuring the failure handler, create a controller to display or handle the error. Define a method mapped to `/error` that retrieves the error message from the session, which was set by the failure handler. Once fetched, ensure to clear the message from the session to prevent it from being re-displayed on subsequent requests.
Example: “`java @GetMapping(“/error”) public String error(HttpServletRequest request) { String message = (String) request.getSession().getAttribute(“error.message”); request.getSession().removeAttribute(“error.message”); return message; } “`
Implementing the above two steps allows your application to effectively capture and display an error message when the authentication fails. This setup also replaces the default error handling, giving you more control over the error management and user feedback in your Spring Boot application.
OAuth defines the four main roles
Resource Owner: User – The resource owner is the user who authorizes an application to access their account
Resource / Authorization Server – The resource server hosts the protected user accounts, and the authorization server verifies the identity of the user then issues access tokens to the application.
Client: Application – The client is the application that wants to access the user’s account. The user must allow it before it may do so, and the API must validate the authorization.
In this example, we are using the following technology stack
package com.the.basic.tech.info;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
OAuth2AuthorizationServerConfig class extends AuthorizationServerConfigurerAdapter and is responsible for generating tokens specific to a client.
Suppose, if a user wants to log in to www.xyz.com via Facebook then Facebook Auth Server will be generating tokens for Devglan. In this case, Devglan becomes the client which will be requesting for authorization code on behalf of the user from Facebook – the authorization server. Following is a similar implementation that Facebook will be using.
@EnableAuthorizationServer: Enables an authorization server. AuthorizationServerEndpointsConfigurer defines the authorization and token endpoints and the token services.
package com.the.basic.tech.info.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
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.AuthorizationServerSecurityConfigurer;
@SuppressWarnings("deprecation")
@Configuration
@EnableAuthorizationServer
public class OAuth2AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
@Value("${user.oauth.clientId}")
private String clientID;
@Value("${user.oauth.clientSecret}")
private String clientSecret;
@Value("${user.oauth.redirectUris}")
private String redirectURLs;
@Value("${user.oauth.accessTokenValidity}")
private int accessTokenValidity;
@Value("${user.oauth.refreshTokenValidity}")
private int refreshTokenValidity;
@Autowired
private BCryptPasswordEncoder passwordEncoder;
@Override
public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
oauthServer.tokenKeyAccess("permitAll()")
.checkTokenAccess("isAuthenticated()");
}
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients
.inMemory()
.withClient(clientID)
.secret(passwordEncoder.encode(clientSecret))
.authorizedGrantTypes("password", "authorization_code", "refresh_token")
.scopes("user_info")
.authorities("READ_ONLY_CLIENT")
.redirectUris(redirectURLs)
.accessTokenValiditySeconds(accessTokenValidity)
.refreshTokenValiditySeconds(refreshTokenValidity);
}
}
Below Spring configuration class enables and configures an OAuth authorization server.
package com.the.basic.tech.info.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
@SuppressWarnings("deprecation")
@Configuration
public class OAuth2ResourceServerConfig extends ResourceServerConfigurerAdapter {
@Override
public void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/api/**").authenticated()
.antMatchers("/").permitAll();
}
}
package com.the.basic.tech.info.config;
import javax.annotation.Resource;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
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.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;
@Configuration
@Order(1)
public class OauthSecurityConfiguration extends WebSecurityConfigurerAdapter {
@Resource(name = "userService")
private UserDetailsService userDetailsService;
@Value("${user.oauth.user.username}")
private String username;
@Value("${user.oauth.user.password}")
private String password;
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.antMatcher("/**")
.authorizeRequests()
.antMatchers("/oauth/authorize**", "/login**", "/error**")
.permitAll()
.and()
.authorizeRequests()
.anyRequest().authenticated()
.and()
.formLogin().permitAll();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth
.inMemoryAuthentication()
.withUser(username).password(passwordEncoder().encode(password)).roles("ADMIN");
}
@Bean
public BCryptPasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@SuppressWarnings({ "rawtypes", "unchecked" })
@Bean
public FilterRegistrationBean corsFilter() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
CorsConfiguration config = new CorsConfiguration();
config.setAllowCredentials(true);
config.addAllowedOrigin("*");
config.addAllowedHeader("*");
config.addAllowedMethod("*");
source.registerCorsConfiguration("/**", config);
FilterRegistrationBean bean = new FilterRegistrationBean(new CorsFilter(source));
bean.setOrder(0);
return bean;
}
}
# DB Configuration
spring:
datasource:
url: jdbc:mysql://localhost:3306/bootdb
username: root
password: admin
jpa:
hibernate.ddl-auto: update
show-sql: true
user.datasource.driver-class-name: com.mysql.jdbc.Driver
user.oauth.clientId: devglan-client
user.oauth.clientSecret: devglan-secret
user.oauth.redirectUris: http://localhost:8081/login
user.oauth.user.username: Alex123
user.oauth.user.password: password
user.oauth.accessTokenValidity: 3600
user.oauth.refreshTokenValidity: 21600
security.oauth2.resource.filter-order: 3
Maven file (pom.xml) would be like as below
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>spring-boot-security-oauth2</artifactId>
<version>1.0-SNAPSHOT</version>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.4.4</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security.oauth</groupId>
<artifactId>spring-security-oauth2</artifactId>
<version>2.4.0.RELEASE</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>commons-dbcp</groupId>
<artifactId>commons-dbcp</artifactId>
<version>1.4</version>
</dependency>
<dependency>
<groupId>javax.persistence</groupId>
<artifactId>javax.persistence-api</artifactId>
</dependency>
</dependencies>
<properties>
<java.version>1.8</java.version>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
package com.the.basic.tech.info.controller;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import com.the.basic.tech.info.model.User;
import com.the.basic.tech.info.service.UserService;
@RestController
@RequestMapping("/users")
public class UserController {
@Autowired
private UserService userService;
@RequestMapping(value="/user", method = RequestMethod.GET)
public List<User> listUser(){
return userService.findAll();
}
@RequestMapping(value = "/user", method = RequestMethod.POST)
public User create(@RequestBody User user){
return userService.save(user);
}
@RequestMapping(value = "/user/{id}", method = RequestMethod.DELETE)
public String delete(@PathVariable(value = "id") Long id){
userService.delete(id);
return "success";
}
}
package com.the.basic.tech.info.model;
import javax.persistence.*;
import javax.persistence.Id;
import com.fasterxml.jackson.annotation.JsonIgnore;
@Entity
public class User {
@Id
@GeneratedValue(strategy= GenerationType.AUTO)
private long id;
@Column
private String username;
@Column
@JsonIgnore
private String password;
@Column
private long salary;
@Column
private int age;
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public long getSalary() {
return salary;
}
public void setSalary(long salary) {
this.salary = salary;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
package com.the.basic.tech.info.dao;
import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;
import com.the.basic.tech.info.model.User;
@Repository
public interface UserDao extends CrudRepository<User, Long> {
User findByUsername(String username);
}
package com.the.basic.tech.info.service;
import java.util.List;
import com.the.basic.tech.info.model.User;
public interface UserService {
User save(User user);
List<User> findAll();
void delete(long id);
}
package com.the.basic.tech.info.service.impl;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
import com.the.basic.tech.info.dao.UserDao;
import com.the.basic.tech.info.model.User;
import com.the.basic.tech.info.service.UserService;
@Service(value = "userService")
public class UserServiceImpl implements UserDetailsService, UserService {
@Autowired
private UserDao userDao;
public UserDetails loadUserByUsername(String userId) throws UsernameNotFoundException {
User user = userDao.findByUsername(userId);
if(user == null){
throw new UsernameNotFoundException("Invalid username or password.");
}
return new org.springframework.security.core.userdetails.User(user.getUsername(), user.getPassword(), getAuthority());
}
private List<SimpleGrantedAuthority> getAuthority() {
return Arrays.asList(new SimpleGrantedAuthority("ROLE_ADMIN"));
}
public List<User> findAll() {
List<User> list = new ArrayList<>();
userDao.findAll().iterator().forEachRemaining(list::add);
return list;
}
@Override
public void delete(long id) {
userDao.deleteById(id);
}
@Override
public User save(User user) {
return userDao.save(user);
}
}
To run the application mvn spring-boot:run command needs to be executed.
2021-06-05 14:56:27.772 INFO 18240 --- [ main] com.the.basic.tech.info.Application : No active profile set, falling back to default profiles: default
2021-06-05 14:56:28.467 INFO 18240 --- [ main] .s.d.r.c.RepositoryConfigurationDelegate : Bootstrapping Spring Data JPA repositories in DEFAULT mode.
2021-06-05 14:56:28.536 INFO 18240 --- [ main] .s.d.r.c.RepositoryConfigurationDelegate : Finished Spring Data repository scanning in 53 ms. Found 1 JPA repository interfaces.
2021-06-05 14:56:29.809 INFO 18240 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port(s): 8080 (http)
2021-06-05 14:56:29.809 INFO 18240 --- [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat]
2021-06-05 14:56:29.809 INFO 18240 --- [ main] org.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/9.0.44]
2021-06-05 14:56:29.971 INFO 18240 --- [ main] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext
2021-06-05 14:56:29.971 INFO 18240 --- [ main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 2121 ms
2021-06-05 14:56:30.157 INFO 18240 --- [ main] o.hibernate.jpa.internal.util.LogHelper : HHH000204: Processing PersistenceUnitInfo [name: default]
2021-06-05 14:56:30.210 INFO 18240 --- [ main] org.hibernate.Version : HHH000412: Hibernate ORM core version 5.4.29.Final
2021-06-05 14:56:30.326 INFO 18240 --- [ main] o.hibernate.annotations.common.Version : HCANN000001: Hibernate Commons Annotations {5.1.2.Final}
2021-06-05 14:56:30.426 INFO 18240 --- [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Starting...
2021-06-05 14:56:30.758 INFO 18240 --- [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Start completed.
2021-06-05 14:56:30.781 INFO 18240 --- [ main] org.hibernate.dialect.Dialect : HHH000400: Using dialect: org.hibernate.dialect.MySQL8Dialect
2021-06-05 14:56:31.429 INFO 18240 --- [ main] o.h.e.t.j.p.i.JtaPlatformInitiator : HHH000490: Using JtaPlatform implementation: [org.hibernate.engine.transaction.jta.platform.internal.NoJtaPlatform]
2021-06-05 14:56:31.445 INFO 18240 --- [ main] j.LocalContainerEntityManagerFactoryBean : Initialized JPA EntityManagerFactory for persistence unit 'default'
2021-06-05 14:56:31.715 WARN 18240 --- [ main] JpaBaseConfiguration$JpaWebConfiguration : spring.jpa.open-in-view is enabled by default. Therefore, database queries may be performed during view rendering. Explicitly configure spring.jpa.open-in-view to disable this warning
2021-06-05 14:56:32.147 INFO 18240 --- [ main] o.s.s.concurrent.ThreadPoolTaskExecutor : Initializing ExecutorService 'applicationTaskExecutor'
2021-06-05 14:56:32.666 INFO 18240 --- [ main] o.s.s.web.DefaultSecurityFilterChain : Will secure Or [Ant [pattern='/oauth/token'], Ant [pattern='/oauth/token_key'], Ant [pattern='/oauth/check_token']] with [org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter@c11332b, org.springframework.security.web.context.SecurityContextPersistenceFilter@26a45089, org.springframework.security.web.header.HeaderWriterFilter@3a88f6fb, org.springframework.security.web.authentication.logout.LogoutFilter@c318864, org.springframework.security.web.authentication.www.BasicAuthenticationFilter@45bd4753, org.springframework.security.web.savedrequest.RequestCacheAwareFilter@5ab5924c, org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter@191f4d65, org.springframework.security.web.authentication.AnonymousAuthenticationFilter@7e5e6573, org.springframework.security.web.session.SessionManagementFilter@844e66d, org.springframework.security.web.access.ExceptionTranslationFilter@74d22ddd, org.springframework.security.web.access.intercept.FilterSecurityInterceptor@520a95ff]
2021-06-05 14:56:32.686 INFO 18240 --- [ main] o.s.s.web.DefaultSecurityFilterChain : Will secure Ant [pattern='/**'] with [org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter@4ecd8ab1, org.springframework.security.web.context.SecurityContextPersistenceFilter@606d6d2c, org.springframework.security.web.header.HeaderWriterFilter@e3ed455, org.springframework.security.web.csrf.CsrfFilter@12c76d6e, org.springframework.security.web.authentication.logout.LogoutFilter@697a92af, org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter@69ce14e6, org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter@2ab8589a, org.springframework.security.web.authentication.ui.DefaultLogoutPageGeneratingFilter@57bdceaa, org.springframework.security.web.savedrequest.RequestCacheAwareFilter@2e3cd732, org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter@56d822dc, org.springframework.security.web.authentication.AnonymousAuthenticationFilter@52909a97, org.springframework.security.web.session.SessionManagementFilter@27bb74e1, org.springframework.security.web.access.ExceptionTranslationFilter@7956f93a, org.springframework.security.web.access.intercept.FilterSecurityInterceptor@4c65d8e3]
2021-06-05 14:56:32.749 INFO 18240 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8080 (http) with context path ''
2021-06-05 14:56:32.764 INFO 18240 --- [ main] com.the.basic.tech.info.Application : Started Application in 5.779 seconds (JVM running for 6.467)
2021-06-05 14:56:41.146 INFO 18240 --- [nio-8080-exec-1] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring DispatcherServlet 'dispatcherServlet'
2021-06-05 14:56:41.146 INFO 18240 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : Initializing Servlet 'dispatcherServlet'
2021-06-05 14:56:41.152 INFO 18240 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : Completed initialization in 2 ms
http://localhost:8080/oauth/authorize?client_id=devglan-client&response_type=code&scope=user_info
This URL will redirect to the login page. Once the customer provides the login information, the system redirects to the grant access page. The page gives an option to the customer to approve or reject the request or to provide certain access to the third-party applications.
Enter the credentials as Alex123/password
OAuth Approval page will be opened
Once we approve the request, it will redirect to an URL (check the user.oauth.redirectUris property). This redirect URL will also contain a code as part of the query string (http://localhost:8081/login?code=5BsHRS
). This code is the authorization code for the third-party application.
In the header, we have username and password as Alex123 and password respectively as Authorization header. As per Oauth2 specification, the Access token request should use application/x-www-form-urlencoded. Following is the setup.
Once you make the request you will get the following result. It has an access token as well as a refresh token.
Usually, the token expiry time is significantly less in the case of oAuth2 and you can use the following API to refresh the token once it is expired.
Thank you for your time. Keep learning 🙂