The OAuth 2.0 provider mechanism is responsible for exposing OAuth 2.0 protected resources. The configuration involves establishing the OAuth 2.0 clients that can access its protected resources independently or on behalf of a user.
The provider role in OAuth 2.0 is actually split between Authorization Service and Resource Service, and while these sometimes reside in the same application, with Spring Security OAuth you have the option to split them across two applications, and also to have multiple Resource Services that share an Authorization Service.
Now we start looking in to how to Implement this oauth2.0.
Maven Dependencies.
Here is my pom.xml
Since we are going to have the authorization server and client on the same application added the below dependencies.
<dependencies>
<dependency>
<groupId>org.springframework.security.oauth.boot</groupId>
<artifactId>spring-security-oauth2-autoconfigure</artifactId>
<version>2.1.4.RELEASE</version>
</dependency>
<!-- <dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency> -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
The following endpoints are required in the Spring Security filter chain in order to implement OAuth 2.0 Authorization Server:
1. Authorization end point. Where we get authorize by giving the proper username and password. /oauth/authorize
2.Token Endpoint.This is where we get the Token from the server./oauth/token
Authorization Server.
As you configure the Authorization Server, you have to consider the grant type that the client is to use to obtain an access token from the end-user (e.g. authorization code, user credentials, refresh token). The configuration of the server is used to provide implementations of the client details service and token services and to enable or disable certain aspects of the mechanism globally.
Create a class that extends AuthorizationServerConfigurerAdapter. Use the anotations @Configuration for making it as the configuration.
@EnableAuthorizationServer for the authorization.
Here we need to have configurer method for the below purpose.
ClientDetailsServiceConfigurer: a configurer that defines the client details service. Client details can be initialized, or you can just refer to an existing store.
eg:
@Override
public void configure(
ClientDetailsServiceConfigurer clients
) throws Exception {
clients.inMemory()
.withClient("client")
.authorizedGrantTypes("password")
.secret("{noop}secret")
.scopes("all");
}
AuthorizationServerSecurityConfigurer: defines the security constraints on the token endpoint.
eg:
@Override
public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
oauthServer.tokenKeyAccess("isAnonymous() || hasAuthority('ROLE_TRUSTED_CLIENT')").checkTokenAccess(
"hasAuthority('ROLE_TRUSTED_CLIENT')");
}
AuthorizationServerEndpointsConfigurer: defines the authorization and token endpoints and the token services.
eg:
@Override
public void configure(
AuthorizationServerEndpointsConfigurer endpoints
) throws Exception {
endpoints.authenticationManager(authenticationManager);
}
Here is my Authorization code.
package com.searchendeca.oauthdemo.server;
import org.springframework.beans.factory.annotation.Autowired;
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;
@Configuration
@EnableAuthorizationServer
public class AuthorizationServer extends AuthorizationServerConfigurerAdapter {
@Autowired
private BCryptPasswordEncoder passwordEncoder;
@Override
public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
security
.tokenKeyAccess("permitAll()")
.checkTokenAccess("isAuthenticated()")
.allowFormAuthenticationForClients();
}
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients
.inMemory()
.withClient("client")
.secret(passwordEncoder.encode("123456"))
.authorizedGrantTypes("password", "authorization_code", "refresh_token")
.authorities("READ_ONLY_CLIENT")
.scopes("all")
.resourceIds("oauth2")
.redirectUris("http://localhost:8081/login")
.accessTokenValiditySeconds(5000)
.refreshTokenValiditySeconds(50000);
}
//in the above method you can use the jdbc confguration and fetch the data from it. I am using the Inmemory database.
}
The above configuration also avaliable in the XML as well.
Resource Server
A Resource Server (can be the same as the Authorization Server or a separate application) serves resources that are protected by the OAuth2 token. Spring OAuth provides a Spring Security authentication filter that implements this protection.
eg:
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests().antMatchers("/login").permitAll().and()
// default protection for all resources (including /oauth/authorize)
.authorizeRequests()
.anyRequest().hasRole("USER")
// ... more configuration, e.g. for form login
}
here is Resource Server configuration.
package com.searchendeca.oauthdemo.server;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
@Configuration
@EnableResourceServer
public class ResourceServer extends ResourceServerConfigurerAdapter
{
@Override
public void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/").permitAll()
.antMatchers("/api/v1/**").authenticated();
}
}
Now we have to configure the security configuration as we did for the basic authentication.Here is my file.
package com.searchendeca.oauthdemo.config;
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.crypto.bcrypt.BCryptPasswordEncoder;
@Configuration
@Order(1)
public class ServerConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.requestMatchers()
.antMatchers("/login", "/oauth/authorize")
.and()
.authorizeRequests()
.anyRequest().authenticated()
.and()
.formLogin().permitAll();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth
.inMemoryAuthentication()
.withUser("syed")
.password(passwordEncoder().encode("123456"))
.roles("USER");
}
@Bean
public BCryptPasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}
}
When ever it asks for the authentication we can give the username as syed and password as 123456.
Rest Controller.
package com.searchendeca.oauthdemo.controller;
import org.springframework.security.access.annotation.Secured;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.security.core.userdetails.User;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
@Controller
public class RestController
{
@Secured("ROLE_USER")
@GetMapping(value = "/user/hello")
public ResponseEntity<String> welcomeAppUser(@AuthenticationPrincipal User user) {
return ResponseEntity.ok("Welcome User " + user.getUsername());
}
}
Now its done build the application.
Access the
Url: http://localhost:8080/oauth/authorize?client_id=client&response_type=code&scope=all
This is what we mentioned in the Authorization Server.you will get the login window give the username as syed and password as 123456.
Once you give the next window takes to the authorization window give approve and it will give the code note it down.
eg We got http://localhost:8081/login?code=0Tccin
Next in the post man pass all this information.
Getting the access token.
http://localhost:8080/oauth/token
pass the below as headers.
Content-Type:application/x-www-form-urlencoded
authorization: Basic Y2xpZW50OjEyMzQ1Ng== // this is nothing but the base64 of client:123456 which we have mentoned in the authorization server.
Pass the below as formdata in body.
grant_type:authorization_code
code:0Tccin
redirect_uri:http://localhost:8081/login
you will get the response like below.
{
"access_token": "afe1c541-b366-4439-bffb-1f06493c5a8e",
"token_type": "bearer",
"refresh_token": "7b5fbaea-cc34-4b23-a4ad-fab74ef646d0",
"expires_in": 4999,
"scope": "read_profile_info"
}
Pass the access token to the controller service.
http://localhost:8080/api/users/hello
with the below headers.
Authorization:Bearer afe1c541-b366-4439-bffb-1f06493c5a8e
Output: Welcome User syed
Happy Learning !!!!