0
votes

I am in a real problem here and would require your help. I work for a bank and have been assigned a task of implementing an OAuth2 service using Spring Boot, I have been exploring since last one week and have been able to implement a password flow grant type OAuth2 service, but now I have a few questions as my seniors said that password flow is not the right fit for our use case. First I would like to explain the use case:

Step 1: user will hit the application URL of the web app where no login is needed and before the application loads the OAuth2 service will be hit with logged in AD (system) user id.

Step 2. The OAuth2 service should authenticate the user using ldap with the given user id and return back all groups the user is part of along with the access token which will be used to access the API's there after

Now I have the below queries:

  1. Which grant type is best suited for my need, from what I have read authorization code grant type seems to be the right fit? Or is it implicit?

  2. Depending on the answer for question 1 what code changes do I need to make in the below code:

Code snippet for my authorization server:

Oauth2AuthserverApplication.java

     @SpringBootApplication
     @EnableAuthorizationServer
     public class Oauth2AuthserverApplication {

     public static void main(String[] args) {
          SpringApplication.run(Oauth2AuthserverApplication.class, args);
     }
  }

OAuth2Congig.java

   @Configuration
   public class Oauth2Config extends AuthorizationServerConfigurerAdapter {

   private String clientId = "client";
   private String clientSecret = "secret";
   private String privateKey = "private-key";
   private String publicKey = "public-key";


  @Autowired
  @Qualifier("authenticationManagerBean")
  private AuthenticationManager authenticationManager;

  @Bean
  public JwtAccessTokenConverter tokenEnhancer() {
    JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
    converter.setSigningKey(privateKey);
    converter.setVerifierKey(publicKey);
    return converter;
  }

  @Bean
  public JwtTokenStore tokenStore() {
    return new JwtTokenStore(tokenEnhancer());
  }

  @Override
  public void configure(AuthorizationServerEndpointsConfigurer endpoints) 
             throws Exception {
          endpoints.authenticationManager(authenticationManager).
              tokenStore(tokenStore())
                  .accessTokenConverter(tokenEnhancer());
  }

  @Override
  public void configure(AuthorizationServerSecurityConfigurer security) 
                 throws Exception {
          security.tokenKeyAccess("permitAll()").
              checkTokenAccess("isAuthenticated()");
  }

  @Override
  public void configure(ClientDetailsServiceConfigurer clients) throws 
               Exception {

     clients.inMemory().withClient(clientId).
           secret(clientSecret).scopes("read", "write")
            .authorizedGrantTypes("password", 
                 "refresh_token").accessTokenValiditySeconds(20000)
            .refreshTokenValiditySeconds(20000);

     }

  }

SecurityConfiguration.java

   @Configuration
   @EnableWebSecurity
   @EnableGlobalMethodSecurity(prePostEnabled = true)
   public class SecurityConfiguration extends WebSecurityConfigurerAdapter {

   @Autowired
   CustomDetailsService customDetailsService;

   @Bean
   public PasswordEncoder encoder() {
    return new BCryptPasswordEncoder();
   }

   @Override
   @Autowired
   protected void configure(AuthenticationManagerBuilder auth) throws 
              Exception {
          auth.userDetailsService(customDetailsService).
                    passwordEncoder(encoder());
   }

   @Override
   protected void configure(HttpSecurity http) throws Exception {
             http.authorizeRequests().anyRequest().authenticated().
                  and().sessionManagement()
            .sessionCreationPolicy(SessionCreationPolicy.STATELESS);
   }

   @Override
   @Bean
   public AuthenticationManager authenticationManagerBean() throws Exception 
       {
           return super.authenticationManagerBean();
   }
 }

Not pasting the model, dao, and service code of the authorization server class as they are not relevant for this question.

Code Snippet from resource server project:

OAuth2ResourceserverApplication.java

   @SpringBootApplication
   @EnableResourceServer
   @RestController
   public class Oauth2ResourceserverApplication {

      public static void main(String[] args) {
         SpringApplication.run(Oauth2ResourceserverApplication.class, args);
      }


     @RequestMapping(value="/api")
     public String success() {
         return "SUCCESS";
     }
  }

JwtConverter.java

  @Component
  public class JwtConverter extends DefaultAccessTokenConverter implements 
           JwtAccessTokenConverterConfigurer {

     @Override
     public void configure(JwtAccessTokenConverter converter) {
              converter.setAccessTokenConverter(this);
     }
  }

SecurityConfiguration.java

   @Configuration
   @EnableWebSecurity
   @EnableGlobalMethodSecurity(prePostEnabled = true)
   public class SecurityConfiguration extends WebSecurityConfigurerAdapter {

   @Override
   protected void configure(HttpSecurity http) throws Exception {
     http.authorizeRequests().anyRequest().authenticated().
                  and().sessionManagement()
            .sessionCreationPolicy(SessionCreationPolicy.NEVER);
   }
 }

application.yml

 server:
 port: 8081
 security:
 oauth2:
    resource:
        filter-order: 3 
        jwt: 
            key-value: private-key
1

1 Answers

1
votes

OAuth2 has 4 grant types. To quickly understand the difference between 'Resource Owner Password Credential', 'Authorization Code', 'Implicit', let us compare them side-by-side:

OAuth2 grant types introduction and comparison

NOTE: The full explanation is available at: https://blog.oauth.io/oauth2-flow-grant-types-in-pictures/.

To answer your question:

  1. Which grant type is best suited for my need, from what I have read authorization code grant type seems to be the right fit? Or is it implicit?

If you compare based on 'Security' the purple bar, 'Authorization Code' is best. However you can see that it has the concept of Guard (backend) that performs access to the User data store on behalf of the App (frontend), i,.e., the App never has access to the Key/Access Token directly, as the Key is retrieved through the exchange of username/password between the User and the OAuth Server, and then passed on to the Guard.

The 'Resource Owner Password Credential' that you implemented is the least secure as the username/password is handed over to the App for the App to do everything that the User can without the User's further consent. However, in your scenario, the App and the User data store both belong to you so that alleviates the security issue.

  1. Depending on the answer for question 1 what code changes do I need to make in the below code:

The full flow of Resource Owner Password Credential grant type you implemented is the left part of the image below and the Authorization Code grant type is the right part. As you can see, there are generally 5 steps. For Resource Owner Password Credential, some steps are not necessary, i.e., marked 'N.A.'.

Resource Owner Password Credential and Authorization Code full flow comparison

NOTE:

  • The 'cloud' represents the App
  • The 'www' represents the User/Browser
  • The 'safe' represents the OAuth Server

To get from the left-side to the right-side, changes you required will be:

Step 1. If your OAuth Server is going to support different Apps, then it needs to support App pre-registration to get client id/secret. If you only have one App then you can skip this.

Step 2. Instead of the App prompting for username/password now the App will redirect the User to the OAuth Server to perform username/password authentication

Step 3. After authenticating the User the OAuth Server can prompt the User about what kind of permissions (e.g., read email, update profile, etc.) she wants to grant the App

Step 4. The OAuth Server instead of handing the Key/Access token to the App, it hands a code to User which then passed it to the App

Step 5. The App then exchanges the code for the Key/Access Token with the OAuth Server.

Once you get the Key/Access Token you can call any protected API on a different server which can then validate the Key/Access Token with the OAuth Server before responding to the API request, e.g., return the groups a User belongs to.