SPRING SECURITY USING OAUTH2

1. WHAT IS OAUTH2?

OAuth2 is version 2 of Oauth protocol which was created in 2006. It allows the 3rd party applications to obtain limited access to User accounts hosted on applications like Google, Facebook, and Twitter etc.

A practical example is

  • You go to Dropbox. It provides you an option to signup/signIn via Google account.
  • You click on it and popup appears asking for your Google credentials.
  • You provide the credentials to google and it authenticates them and on success, asks you to provide permissions to Dropbox.
  • Upon allowing the permissions, we are logged into Dropbox using Google’s credentials.

Some of the major points of the flow being

  • We never shared our credentials with Dropbox.
  • We could access Dropbox using Google’s credentials. Also based on the access provided, we can also see the options to share a file with our google contacts (provided we granted permission to Dropbox to access our contacts).

oauth2

1.1 Roles in OAuth2:

There are 4 roles in Oauth2:

  • Resource Owner - Generally is User
  • Client Application - In above example it was Dropbox (Or 3rd party app)
  • Resource Server - In above example it was Google server where our account/contacts are hosted. Third Party Apps accesses the resource server using temporary access tokens
  • Authorization Server - The server which issues access tokens. In above example it was “accounts.google.com”, which authorizes the google account.

1.2 Tokens in OAuth2:

There are 2 types of tokens issued by Authorization Server

  • Access Token - Third party apps use this token to access information from the resource server. It has a limited lifetime and must be sent with each request.
  • Refresh Token - This is similar to REMEMBER ME option. This token has a longer lifetime and is used to re-generate the access token.

1.3 Grant Types:

Based on the nature of 3rd party applications, Authorization server defines Grant Types for obtaining the access token. More information here.

  • Authorization Code Grant - Authorization server first issues Authorization code to Client. It then uses this code to get the Access token.
  • Implicit Grant - Similar to the Authorization Code Grant, but Authentication server directly returns the token.
  • Resource Owner Password Credentials OR password Grant - Here the user submits his credentials and then only access token is issues.
  • Client Credentials Grant- Client uses client_id and client_secret to get access token.
  • Refresh Token- Client uses this token to regenerate access token.

2. What are JWT Tokens?

JWT stands for JSON Web Tokens is a popular format for representing tokens. More info at here.

JWT Tokens are JSON encoded and it contains information about the issuer, claims, an algorithm used. These tokens are digitally signed and thus its integrity can be ensured.

A typical JWT Token is a string divided into 3 periods. (Periods are.)

XXXX.YYYYYY.ZZZZZ

The different parts are:

  • Header: It consists of the type and the algorithm used. This info is Base64Url encoded to form the header.
  • Payload: It consists of claims encoded in Base64Url.
  • Signature: The last part is constructed using encoded header, encoded payload, a secret key and the algorithm specified in the header. Since the secret key is only known to issuer, the token cannot be decoded and thus its integrity is ensured.

Example:
Consider following JWT Token:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1MzI3Nzc1MTEsInVzZXJfbmF tZSI6ImFkbWluQGdtYWlsLmNvbSIsImF1dGhvcml0aWVzIjpbIlJPTEVfQURNSU4iXSwianR pIjoiZmZjYWY1YTUtNjFiYy00ZDc4LThlYzMtNTdjZTIwNWUwZDY2IiwiY2xpZW50X2lkIjo iaGVtYW50Iiwic2NvcGUiOlsibWF0Y2hkIl19.fJHjdPsGjzYiGs5cvR-1oZ6Sqqb5_BYid3Xl2ZyzkwI

Header (Part 1)

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9

Based64 decoded String is

"{"alg":"HS256","typ":"JWT"}"

Payload (Part2)

eyJleHAiOjE1MzI3Nzc1MTEsInVzZXJfbmFtZSI6ImFkbWluQGdtYWlsLmN
vbSIsImF1dGhvcml0aWVzIjpbIlJPTEVfQURNSU4iXSwianRpIjoiZmZjYW
Y1YTUtNjFiYy00ZDc4LThlYzMtNTdjZTIwNWUwZDY2IiwiY2xpZW50X2lkI
joiaGVtYW50Iiwic2NvcGUiOlsibWF0Y2hkIl19

Base64 decoded String is

"{"exp":1532777511,"user_name":"admin@gmail.com","authorities":["ROLE_ADMIN"],"jti":"ffcaf5a5-61bc-4d78-8ec3-57ce205e0d66","client_id":"hemant","scope":["matchd"]}"

Signarture
(Part 3)

fJHjdPsGjzYiGs5cvR-1oZ6Sqqb5_BYid3Xl2ZyzkwI

Since its encoded by issuer using secret key, we cannot decode it.


In our project, the tokens will be encoded in JWT.

3. Spring OAuth2 Implementation:

In Spring implementation of OAuth2 is divided between Authorization-Server and Resource-Server.

They can reside in the same application or be 2 distinct applications.

3.1 Authorization Server:

  • This issues access tokens to the clients (3rd party apps) and verifies them.
  • For issuing tokens, token endpoints are provided (/oauth/token)
  • For authorizing the tokens, authorize endpoints are provided (/oauth/authorize).

3.2 Resource Server:

Resource server holds the resources/endpoints which can be accessed by the client(3rd party apps), only when granted by Authorization Server.

For protecting the resource server’s APIs, Spring security adds OAuth2AuthenticationProcessingFilter in the spring security filter chain.

This filter checks the access token in the request, validates and authorizes it and then only request reaches Resource Server endpoints.

4. Project

In our project, we’ll:

  • Implement OAuth2 using Spring Boot and Spring Security.
  • The access and refresh tokens will be JWT encoded.
  • Authentication will be done using OAuth2
  • Further, we will be implementing authorization too.
  • Resource server and Authorization server will run in a single instance.

The project Structure is as follows:

oauth2

4.1 Maven Dependencies

<?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.hemant</groupId> <artifactId>spring-oauth2-jwt</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>jar</packaging> <name>spring-oauth2-jwt</name> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.5.1.RELEASE</version> <relativePath /> <!-- lookup parent from repository --> </parent> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>1.8</java.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-security</artifactId> </dependency> <dependency> <groupId>org.springframework.security.oauth</groupId> <artifactId>spring-security-oauth2</artifactId> </dependency> <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-jwt</artifactId> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>

4.2 Application properties

The application.properties file is as follows:

#Application properties server.port=8080 spring.application.name=spring-oauth2-jwt #logging pattern logging.pattern.console=%d{HH:mm:ss} %-5level %logger{10} - %msg%n #signing key for JWT Tokens signing.key=alteredcarbons1e7

4.3 Spring Boot Main Application class

package com.hemant.oauth2; importorg.springframework.boot.SpringApplication; importorg.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication publicclassSpringOauth2JWTApp { publicstaticvoidmain(String[] args) { SpringApplication.run(SpringOauth2JWTApp.class, args); } }

4.4 Authorization Server Configuration

package com.hemant.oauth2.config; importorg.springframework.beans.factory.annotation.Autowired; importorg.springframework.beans.factory.annotation.Value; importorg.springframework.context.annotation.Bean; importorg.springframework.context.annotation.Configuration; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.core.userdetails.UserDetailsService; 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.provider.token.store.JwtAccessTokenConverter; /** * Configuration for Authorization Server * @author hemant * */ @Configuration @EnableAuthorizationServer publicclassAuthorizationServerConfigextendsAuthorizationServerConfigurerAdapter { @Autowired privateAuthenticationManagerauthenticationManager; @Autowired privateUserDetailsServiceuserDetailsService; // For signing JWT Token @Value("${signing.key}") private String signingKey; @Bean publicJwtAccessTokenConverteraccessTokenConverter() { finalJwtAccessTokenConverteraccessTokenConverter = newJwtAccessTokenConverter(); accessTokenConverter.setSigningKey(signingKey); returnaccessTokenConverter; } /** * a configurer that defines the client details service * Currently we are storing clients in-memory */ @Override publicvoidconfigure(finalClientDetailsServiceConfigurer clients) throws Exception { clients.inMemory() .withClient("client1") .secret("secret") .authorizedGrantTypes("password", "refresh_token") .scopes("read", "write") .accessTokenValiditySeconds(100); } /** * defines the security constraints on the token endpoints. */ @Override publicvoidconfigure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception { endpoints.authenticationManager(authenticationManager).userDetailsService(userDetailsService).accessTokenConverter(accessTokenConverter()); } }

Explanation:

  • @EnableAuthorizationServer : is a convenience annotation which enables the Authorization server in the application and sets up authorization endpoints (/oauth/authorize) and token endpoints (/oauth/token).
  • JwtAccessTokenConverter - For encoding tokens using JWT. Also for signing JWT tokens, Authorization server will use the value in ${signing.key}. Since this signing key is only known to Authorization server, only he can sign it and verify its integrity.
  • ClientDetailsServiceConfigurer - Configures how clients (or 3rd party apps) are configured and stored. Currently, they are stored in memory. We have configured only 1 client, which will provide access tokens for grant-types (password - where a user provides his credentials and refresh_token - where a user provides refresh token value to regenerate access token). The access token issued will expire in 100 sec.
  • AuthorizationServerEndpointsConfigurer - It defines the security for the Authorization server endpoints.
  • For storing tokens org.springframework.security.oauth2.provider.token.store.JwtTokenStore is used, which doesn’t really stores the tokens.

4.5 Resource Server Configuration:

package com.hemant.oauth2.config; importorg.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.web.builders.HttpSecurity; importorg.springframework.security.config.http.SessionCreationPolicy; import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer; import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter; /** * All the resources which are not explicitly ignored * or not part of Authorization server are protected by Resource server * @author hemant * */ @Configuration @EnableResourceServer publicclassResourceServerConfigextendsResourceServerConfigurerAdapter { /** * 1. public/** APIs can be accessed by any user, authenticated or not * 2. admin/** APIs can be accessed by authenticated users who possess the role ADMIN * 3. Remaining all APIs can only be accessed by authenticated users, irrespective of their roles */ @Override publicvoidconfigure(finalHttpSecurity http) throws Exception { http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS); http.authorizeRequests().antMatchers("/public/**").anonymous(); http.authorizeRequests().antMatchers("/admin/**").hasRole("ADMIN"); http.authorizeRequests().anyRequest().authenticated(); http.csrf().disable(); http.logout().disable(); } }

Explanation:

  • EnableResourceServer - This enables the resource server which encompasses all the URLs which are not part of Authorization server. This sets up OAuth2AuthenticationProcessingFilter - A pre-authentication filter for OAuth2 protected resources

4.6 Authentication Provider and User Services:

In this project, for the sake of simplicity, the users are stored in-memory and authentication is provided against them only.

package com.hemant.oauth2.config.security; importjava.util.Arrays; importjava.util.HashMap; importjava.util.Map; importjavax.annotation.PostConstruct; import org.springframework.security.core.authority.SimpleGrantedAuthority; importorg.springframework.security.core.userdetails.User; importorg.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.core.userdetails.UsernameNotFoundException; importorg.springframework.stereotype.Component; @Component publicclassUserDetailsServiceImplimplementsUserDetailsService { Map<String, User>userMap; @PostConstruct publicvoidinitUsers() { // admin@gmail.com -- admin -- role=ADMIN, so he is admin user UseradminUser = new User("admin@gmail.com", "admin", Arrays.asList(newSimpleGrantedAuthority("ROLE_ADMIN"))); // normal@gmail.com -- normal -- role=NORMAL, so he is normal user UsernormalUser = new User("normal@gmail.com", "normal", Arrays.asList(newSimpleGrantedAuthority("ROLE_NORMAL"))); userMap = newHashMap<>(); userMap.put(adminUser.getUsername(), adminUser); userMap.put(normalUser.getUsername(), normalUser); } @Override publicUserDetailsloadUserByUsername(String username) throwsUsernameNotFoundException { User user = userMap.get(username); if(null == user) { returnnull; } returnnew User(username, user.getPassword(),user.getAuthorities()); } } _________________________________________________________________________________________________________ package com.hemant.oauth2.config.security; importorg.springframework.beans.factory.annotation.Autowired; import org.springframework.security.authentication.AuthenticationProvider; import org.springframework.security.authentication.BadCredentialsException; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; importorg.springframework.security.core.Authentication; importorg.springframework.security.core.AuthenticationException; importorg.springframework.security.core.userdetails.User; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.core.userdetails.UsernameNotFoundException; importorg.springframework.stereotype.Component; @Component publicclassCustomAuthenticationProviderimplementsAuthenticationProvider { @Autowired privateUserDetailsServiceuserDetailsService; @Override public Authentication authenticate(Authentication authentication) throwsAuthenticationException { String username = authentication.getPrincipal().toString(); String password = authentication.getCredentials().toString(); User user = (User) userDetailsService.loadUserByUsername(username); if (user == null) { thrownewUsernameNotFoundException("User not found :=" +username); } if(!user.getPassword().equals(password)) { thrownewBadCredentialsException("Invalid password"); } returnnewUsernamePasswordAuthenticationToken(username, password, user.getAuthorities()); } @Override publicbooleansupports(Class<?> authentication) { returntrue; } }

4.7 Controllers:

For covering cases related to Authorization, we have defined 3 controllers providing APIs for resource server.

No

Name

Config

1

AdminController

Its APIs can only accessed by authenticated users with role - ADMIN

2

CommonController

Its APIs can only accessed by authenticated users with ANY role

3

PublicController

Its APIs can be accessed by any user - authenticated and non-authenticated


This security configuration is provided in ResourceServer Configuration.

package com.hemant.oauth2.controller; importjava.util.HashMap; importjava.util.Map; importorg.springframework.web.bind.annotation.RequestMapping; importorg.springframework.web.bind.annotation.RequestMethod; importorg.springframework.web.bind.annotation.RestController; /** * Referring to the HTTP Security logic in * #{com.hemant.oauth2.config.ResourceServerConfig} , the endpoints in this * * controller can only accessed by authenticated users with role - ADMIN * * @author hemant * */ @RestController @RequestMapping("admin") publicclassAdminController { @RequestMapping(value = "", method = RequestMethod.GET) public Map<String, String>getAdminData() { Map<String, String> map = newHashMap<>(); map.put("1", "admin-value1"); map.put("2", "admin-value2"); return map; } } package com.hemant.oauth2.controller; importjava.util.HashMap; importjava.util.Map; importorg.springframework.web.bind.annotation.RequestMapping; importorg.springframework.web.bind.annotation.RequestMethod; importorg.springframework.web.bind.annotation.RestController; /** * * Referring to the HTTP Security logic in * #{com.hemant.oauth2.config.ResourceServerConfig} , the endpoints in this * * controller can only accessed by authenticated users with ANY role * * @author hemant */ @RestController @RequestMapping("commons") publicclassCommonController { @RequestMapping(value = "", method = RequestMethod.GET) public Map<String, String>getUserData() { Map<String, String> map = newHashMap<>(); map.put("1", "common-value-1"); map.put("2", "common-value-2"); return map; } } package com.hemant.oauth2.controller; importjava.util.HashMap; importjava.util.Map; importorg.springframework.web.bind.annotation.RequestMapping; importorg.springframework.web.bind.annotation.RequestMethod; importorg.springframework.web.bind.annotation.RestController; /** * Referring to the HTTP Security logic in * #{com.hemant.oauth2.config.ResourceServerConfig} , the endpoints in this * * controller can be accessed by any user - authenticated and non authenticated * * @author hemant * */ @RestController @RequestMapping("public") publicclassPublicController { @RequestMapping(value = "", method = RequestMethod.GET) public Map<String, String>getPublicData() { Map<String, String> map = newHashMap<>(); map.put("1", "pub-value-1"); map.put("2", "pub-value-2"); return map; } }

5. Running the Application

1. Go to the application ROOT directory and execute

>>> mvn clean install

2. If the build is success, the JAR can be executed by

>>> java -jar target/spring-oauth2-jwt-0.0.1-SNAPSHOT.jar

3. The following logs indicate that application is up and running

01:34:42 INFO o.s.b.c.e.t.TomcatEmbeddedServletContainer - Tomcat started on port(s): 8080 (http) 01:34:42 INFO c.h.o.SpringOauth2JWTApp - Started SpringOauth2JWTApp in 6.066 seconds (JVM running for 6.805)

6. Demonstration

1. Try accessing /public/ API. It should be accessible without any restrictions.

GET http://localhost:8080/public { "1": "pub-value-1", "2": "pub-value-2" }

2. Similarly try accessing any other URL (/commons or /admin). You should get access denied error.

GET http://localhost:8080/commons 401 { "error": "unauthorized", "error_description": "Full authentication is required to access this resource" }

This is because, we cannot access these without access token.

3. Get access token

We have to pass, clientId and client secret in Authorization header, set grant_type=password and also send the user credentials.

3.1 Sending improper credentials

curl -X POST -u client1:secret -i"http://localhost:8080/oauth/token?grant_type=password&username=admin&password=xyz" HTTP/1.1401 { "error": "unauthorized", "error_description": "User not found :=admin" }

3.2 Sending proper credentials

curl -X POST -u client1:secret -i"http://localhost:8080/oauth/token?grant_type=password&username=admin@gmail.com&password=admin" { "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1MzI4MTAwNDQsInVzZXJfbm FtZSI6ImFkbWluQGdtYWlsLmNvbSIsImF1dGhvcml0aWVzIjpbIlJPTEVfQURNSU4iXSwian RpIjoiMmRiMmUwM2YtMjFkYi00ZWQwLWEyZmMtZTExOWRkNjQ1OGNmIiwiY2xpZW50X2lkIj oiY2xpZW50MSIsInNjb3BlIjpbInJlYWQiLCJ3cml0ZSJdfQ.-qFr_BMDuvt6UtQ68GZHGqnDjCHB6cf1FfSfpjjTvc8", "token_type": "bearer", "refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX25hbWUiOiJhZG1pbkBnbWFpbC5jb20iLCJzY29wZSI6WyJyZWFkIiwid3JpdGUiXSwiYXRpIjoiMmRiMmUwM2YtMjFkYi00ZWQwLWEyZmMtZTExOWRkNjQ1OGNmIiwiZXhwIjoxNTM1NDAxOTQ0LCJhdXRob3JpdGllcyI6WyJST0xFX0FETUlOIl0sImp0aSI6IjVkOGU5OWY4LTM0OGUtNDMxZC1iODk2LTcyYzVhMGNkYmM3NiIsImNsaWVudF9pZCI6ImNsaWVudDEifQ.Xpygqx0heunlTJqWRUKA2TcoRDbpeDIkVQ3bykcDXr8", "expires_in": 99, "scope": "read write", "jti": "2db2e03f-21db-4ed0-a2fc-e119dd6458cf" }

4. Once access token is obtained, we can access the protected resources.

curl -X GET --header "Authorization: bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1MzI4MTAwNDQsInVzZXJfbmFtZSI6ImFkbWluQGdtYWlsLmNvbSIsImF1dGhvcml0aWVzIjpbIlJPTEVfQURNSU4iXSwianRpIjoiMmRiMmUwM2YtMjFkYi00ZWQwLWEyZmMtZTExOWRkNjQ1OGNmIiwiY2xpZW50X2lkIjoiY2xpZW50MSIsInNjb3BlIjpbInJlYWQiLCJ3cml0ZSJdfQ.-qFr_BMDuvt6UtQ68GZHGqnDjCHB6cf1FfSfpjjTvc8" -i"http://localhost:8080/admin" 200 OK { "1": "admin-value1", "2": "admin-value2" }

5. Once access token has expired (100 sec), trying to use to accessing the same resource shouldn’t work. It should give token expired error.

curl -X GET --header "Authorization: bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1MzI4MTAwNDQsInVzZXJfbmFtZSI6ImFkbWluQGdtYWlsLmNvbSIsImF1dGhvcml0aWVzIjpbIlJPTEVfQURNSU4iXSwianRpIjoiMmRiMmUwM2YtMjFkYi00ZWQwLWEyZmMtZTExOWRkNjQ1OGNmIiwiY2xpZW50X2lkIjoiY2xpZW50MSIsInNjb3BlIjpbInJlYWQiLCJ3cml0ZSJdfQ.-qFr_BMDuvt6UtQ68GZHGqnDjCHB6cf1FfSfpjjTvc8" -i"http://localhost:8080/admin" HTTP/1.1401 { "error": "invalid_token", "error_description": "Access token expired: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1MzI4MTAwNDQsInVzZXJfbmFtZSI6ImFkbWluQGdtYWlsLmNvbSIsImF1dGhvcml0aWVzIjpbIlJPTEVfQURNSU4iXSwianRpIjoiMmRiMmUwM2YtMjFkYi00ZWQwLWEyZmMtZTExOWRkNjQ1OGNmIiwiY2xpZW50X2lkIjoiY2xpZW50MSIsInNjb3BlIjpbInJlYWQiLCJ3cml0ZSJdfQ.-qFr_BMDuvt6UtQ68GZHGqnDjCHB6cf1FfSfpjjTvc8" }

6. We can use the refresh token we got earlier to regenerate the access token.

curl -X POST -u client1:secret -i"http://localhost:8080/oauth/token?grant_type=refresh_token&refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX25hbWUiOiJhZG1pbkBnbWFpbC5jb20iLCJzY29wZSI6WyJyZWFkIiwid3JpdGUiXSwiYXRpIjoiMmRiMmUwM2YtMjFkYi00ZWQwLWEyZmMtZTExOWRkNjQ1OGNmIiwiZXhwIjoxNTM1NDAxOTQ0LCJhdXRob3JpdGllcyI6WyJST0xFX0FETUlOIl0sImp0aSI6IjVkOGU5OWY4LTM0OGUtNDMxZC1iODk2LTcyYzVhMGNkYmM3NiIsImNsaWVudF9pZCI6ImNsaWVudDEifQ.Xpygqx0heunlTJqWRUKA2TcoRDbpeDIkVQ3bykcDXr8" HTTP/1.1200 { "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1MzI4MTEyNTgsInVzZXJfbmFtZSI6ImFkbWluQGdtYWlsLmNvbSIsImF1dGhvcml0aWVzIjpbIlJPTEVfQURNSU4iXSwianRpIjoiZmNhNjRjYjYtZWVkOC00YmY2LTliZDktYzQzMmJmYWE4OTA5IiwiY2xpZW50X2lkIjoiY2xpZW50MSIsInNjb3BlIjpbInJlYWQiLCJ3cml0ZSJdfQ.k3J0mVDyebLOKUiZvCXGkwlJaUHSfp4UGuNB99IuDJU", "token_type": "bearer", "refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX25hbWUiOiJhZG1pbkBnbWFpbC5jb20iLCJzY29wZSI6WyJyZWFkIiwid3JpdGUiXSwiYXRpIjoiZmNhNjRjYjYtZWVkOC00YmY2LTliZDktYzQzMmJmYWE4OTA5IiwiZXhwIjoxNTM1NDAxOTQ0LCJhdXRob3JpdGllcyI6WyJST0xFX0FETUlOIl0sImp0aSI6IjVkOGU5OWY4LTM0OGUtNDMxZC1iODk2LTcyYzVhMGNkYmM3NiIsImNsaWVudF9pZCI6ImNsaWVudDEifQ.VwRILrmjExP-I7cKS9SfvSX21j3mN_sNpUqBYGTozO4", "expires_in": 99, "scope": "read write", "jti": "fca64cb6-eed8-4bf6-9bd9-c432bfaa8909" }

7. Let’s check Authorization part (for normal User). Get access token for normal User

curl -X POST -u client1:secret -i"http://localhost:8080/oauth/token?grant_type=password&username=normal@gmail.com&password=normal" { "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1MzI4MTIyNzksInVzZXJfbmFtZSI6Im5vcm1hbEBnbWFpbC5jb20iLCJhdXRob3JpdGllcyI6WyJST0xFX05PUk1BTCJdLCJqdGkiOiJlZGEwMGQwNS1hYTQ1LTQ1N2YtOTFlNC0wODdkNGE3ZTM5ZWMiLCJjbGllbnRfaWQiOiJjbGllbnQxIiwic2NvcGUiOlsicmVhZCIsIndyaXRlIl19.1YTL0ZWC0O9VfszVhgpomFXcTrldl3xqp8dn9BWU8qU", "token_type": "bearer", "refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX25hbWUiOiJub3JtYWxAZ21haWwuY29tIiwic2NvcGUiOlsicmVhZCIsIndyaXRlIl0sImF0aSI6ImVkYTAwZDA1LWFhNDUtNDU3Zi05MWU0LTA4N2Q0YTdlMzllYyIsImV4cCI6MTUzNTQwNDE3OSwiYXV0aG9yaXRpZXMiOlsiUk9MRV9OT1JNQUwiXSwianRpIjoiNTg4NWY0Y2EtNTczZC00ZmUxLWJmNjItMTY3ZDVlNDY0NDliIiwiY2xpZW50X2lkIjoiY2xpZW50MSJ9.UipHQRf3IURJY2sZhch8QwKcZXM0y_s80az6vSXsX0Y", "expires_in": 99, "scope": "read write", "jti": "eda00d05-aa45-457f-91e4-087d4a7e39ec" }

8. Use the access token to access commons API

curl -X GET --header "Authorization: bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1MzI4MTIyNzksInVzZXJfbmFtZSI6Im5vcm1hbEBnbWFpbC5jb20iLCJhdXRob3JpdGllcyI6WyJST0xFX05PUk1BTCJdLCJqdGkiOiJlZGEwMGQwNS1hYTQ1LTQ1N2YtOTFlNC0wODdkNGE3ZTM5ZWMiLCJjbGllbnRfaWQiOiJjbGllbnQxIiwic2NvcGUiOlsicmVhZCIsIndyaXRlIl19.1YTL0ZWC0O9VfszVhgpomFXcTrldl3xqp8dn9BWU8qU" -i"http://localhost:8080/commons" 200 OK {"1":"common-value-1","2":"common-value-2"}

He should be able to access it, since it’s meant for all authenticated users irrespective of role.

9. Use the access token to access Admin API

curl -X GET --header "Authorization: bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1MzI4MTIyNzksInVzZXJfbmFtZSI6Im5vcm1hbEBnbWFpbC5jb20iLCJhdXRob3JpdGllcyI6WyJST0xFX05PUk1BTCJdLCJqdGkiOiJlZGEwMGQwNS1hYTQ1LTQ1N2YtOTFlNC0wODdkNGE3ZTM5ZWMiLCJjbGllbnRfaWQiOiJjbGllbnQxIiwic2NvcGUiOlsicmVhZCIsIndyaXRlIl19.1YTL0ZWC0O9VfszVhgpomFXcTrldl3xqp8dn9BWU8qU" -i"http://localhost:8080/admin" HTTP/1.1403 {"error":"access_denied","error_description":"Access is denied"}

This is because the user only has ROLE_NORMAL, but ROLE_ADMIN is needed to access the admin APIs. Thus authorization config is working fine

7. Conclusion

Thus we implemented the OAuth2 with Spring Security.

We used grant_type=password (where user needs to provide access token to receive access token) and grant_type=refresh_token (where user provides refresh token to generate the access token value). In addition to OAuth2 implementation, we also applied authorization to our resource server endpoints.

Note that the code shared by Java Application Development team is for reference purpose only. If you have any doubt or want to ask anything related to the issue, make comments below.

For further information, mail us at info@aegissofttech.com