Saturday 16 April 2016

Spring Oauth2 with JWT Sample

Sometimes ago, we published one article sharing a custom approach to implementing stateless session in the cloud environment. Today, let explore another popular use case of setting up OAuth 2 authentication for a Spring Boot application. In this example, we will JSON Web Token (JWT) as the format of the OAuth 2 token.

This sample was developed partly based on the official sample of Spring Security OAuth 2. However, we will focus on understanding the principal of the OAuth 2 request. The source code is available at https://github.com/tuanngda/spring-boot-oauth2-demo.git

Background

OAuth 2 and JWT 


We will not go to detail when you may want to use OAuth 2 and JWT. In general, OAuth 2 is useful when you need to allow other people to build front end app for you services. We focus on OAuth 2 and JWT because they are the most popular authentication framework and protocol in the market.

Spring Security OAuth 2


Spring Security OAuth 2 is an implementation of OAuth 2 that built on top of Spring Security, which itself is a very extensible authentication framework.

In overall, Spring Security authentication includes 2 steps, creating an authentication object for each request and applying authorization check depending on authentication.

The first step was done in a multi-layer Security Filter. Depending on the configuration, each layer can help to create the authentication object for web request with basic authentication, digest authentication, form authentication or any custom method of authentication. The client side session we built in the previous article is effectively a layer of custom method authentication and Spring Security OAuth 2 is built on the same mechanism.

Because in this example, our application both provides and consume token, Spring Security OAuth 2 should not be the sole authentication layer for the application. We need another authentication mechanism to protect the token provider endpoint so that resource owner can authenticate himself before getting the JWT token.

For a cluster environment, the token or the secret to sign token (for JWT) suppose to be persisted so that the token can be recognized at any resource server but we skip this step to simplify the example. Similarly, the user authentication and client identities are all hard-coded.

System Design

Overview


In our application, we need to setup 3 components

  • Authorization Endpoint and Token Endpoint to help to provide OAuth 2 token.
  • A WebSecurityConfigurerAdapter, which is an authentication layer with hard-coded order of 3 (according to Dave Syer). This authentication layer will setup authentication and principal for any request that contains OAuth 2 token.
  • Another authentication mechanism to protect Token endpoint and other resources if the token is missing. In this sample, we choose basic authentication for its simplicity when writing tests. As we do not specify the order, it will take the default value of 100. With Spring security, the lower order, the higher priority; so we should expect OAuth 2 come before basic authentication in the FilterChainProxy. Inspecting in IDE proves that our setup is correct.



In the above picture, OAuth2AuthenticationProcessingFilter appear in front of BasicAuthenticationFilter.

Authorization Server Configuration


Here is our config for Authorization and Token Endpoint

@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter {

    @Value("${resource.id:spring-boot-application}")
    private String resourceId;
    
    @Value("${access_token.validity_period:3600}")
    int accessTokenValiditySeconds = 3600;

    @Autowired
    private AuthenticationManager authenticationManager;
    
    @Bean
    public JwtAccessTokenConverter accessTokenConverter() {
        return new JwtAccessTokenConverter();
    }

    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        endpoints
            .authenticationManager(this.authenticationManager)
            .accessTokenConverter(accessTokenConverter());
    }
    
    @Override
    public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
        oauthServer.tokenKeyAccess("isAnonymous() || hasAuthority('ROLE_TRUSTED_CLIENT')")
            .checkTokenAccess("hasAuthority('ROLE_TRUSTED_CLIENT')");
    }

    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients.inMemory()
            .withClient("normal-app")
                .authorizedGrantTypes("authorization_code", "implicit")
                .authorities("ROLE_CLIENT")
                .scopes("read", "write")
                .resourceIds(resourceId)
                .accessTokenValiditySeconds(accessTokenValiditySeconds)
        .and()
            .withClient("trusted-app")
                .authorizedGrantTypes("client_credentials", "password")
                .authorities("ROLE_TRUSTED_CLIENT")
                .scopes("read", "write")
                .resourceIds(resourceId)
                .accessTokenValiditySeconds(accessTokenValiditySeconds)
                .secret("secret");
    }
}

There are few things worth noticing about this implementation.

  • Setting up JWT token is as simple as using JwtAccessTokenConverter. Because we never specify the signing key, it is randomly generated. If we intended to deploy our application to the cloud environment, it is a must to sync the signing key across all authorization servers.
  • Instead of creating authentication manager, we choose to inject an existing authentication manager from Spring container. With this step, we can share the authentication manager with the Basic Authentication filter.
  • It is possible to have trusted application and not trusted application. Trusted application can have their own secret. This is necessary for client credential authorization grant. Except client credentials, all 3 other grants require resource owner's credential.
  • We allow anonymous for checking token endpoint. With this configuration, the checking token is accessible without basic authentication or OAuth 2 token. 

Resource Server Configuration


Here is our configuration for Resource Server Configuration

@Configuration
@EnableResourceServer
public class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {
    
    @Value("${resource.id:spring-boot-application}")
    private String resourceId;
    
    @Override
    public void configure(ResourceServerSecurityConfigurer resources) {
        resources.resourceId(resourceId);
    }

    @Override
    public void configure(HttpSecurity http) throws Exception {
         http.requestMatcher(new OAuthRequestedMatcher())
                .authorizeRequests()
                 .antMatchers(HttpMethod.OPTIONS).permitAll()
                    .anyRequest().authenticated();
    }
    
    private static class OAuthRequestedMatcher implements RequestMatcher {
        public boolean matches(HttpServletRequest request) {
            String auth = request.getHeader("Authorization");
            // Determine if the client request contained an OAuth Authorization
            boolean haveOauth2Token = (auth != null) && auth.startsWith("Bearer");
            boolean haveAccessToken = request.getParameter("access_token")!=null;
   return haveOauth2Token || haveAccessToken;
        }
    }

}

Here are few things to take note:
  • The OAuthRequestedMatcher is added in so that the OAuth filter will only process OAuth 2 requests. We added this in so that an unauthorized request will be denied at the Basic Authentication layer instead of OAuth 2 layer. This may not make any difference in term of functionality but we added it in for usability. For the client, they will receive 401 HTTP Status with this new header versus the old header:
    • WWW-Authenticate:Basic realm="Realm"
    • WWW-Authenticate:Bearer realm="spring-boot-application", error="unauthorized", error_description="Full authentication is required to access this resource"
  • With the new response header, a browser will auto prompt user for the username and password. If you do not want the resource to be accessible by any other authentication mechanism, this step is not necessary.
  • Some browsers like Chrome like to send OPTIONS request to look for CORS before making AJAX call. Therefore, it is better to  always allow OPTIONS requests.

Basic Authentication Security Configuration


As mentioned earlier, because we need to protect the token provider endpoint.

@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
@EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
    
    @Autowired
    public void globalUserDetails(AuthenticationManagerBuilder auth) throws Exception {
        auth.inMemoryAuthentication().withUser("user").password("password").roles("USER").and().withUser("admin")
                .password("password").roles("USER", "ADMIN");
    }
    
    @Override
    protected void configure(HttpSecurity http) throws Exception {
     http
        .authorizeRequests()
            .antMatchers(HttpMethod.OPTIONS).permitAll()
            .anyRequest().authenticated()
            .and().httpBasic()
            .and().csrf().disable();
    }
    
    @Override
    @Bean
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }
}

There are few things to take note:

  • We expose the AuthenticationManager bean so that our two authentication security adapter can share a single authentication manager.
  • Spring Security CSRF working seamlessly with JSP but is a hassle for RestAPI. Because we want this sample app to be used as a base for users to develop their own application, we turned CSRF off and add in a CORS filter so that it can be used right away.

Testing


We wrote one test scenario for each authorization grant type following exactly OAuth 2 specifications. Because Spring Security OAuth 2 is an implementation based on Spring Security framework, our interest is veered toward seeing how the underlying authentication and principal are constructed.

Before summarizing the outcome of the experiment, let take a quick look at few notes.

  • Most of the requests to token provider endpoints were sent using POST requests but they include user credential as parameters. Even though we put this credential as part of URL for convenient, never do this in your OAuth 2 client.
  • We created 2 endpoints /resources/principal and /resources/roles to capture the principal and authority for OAuth 2 authentication.
Here is our setup:

User
Type
Authorities
Credential
user
resource owner
ROLE_USER
Y
admin
resource owner
ROLE_ADMIN
Y
normal-app
client
ROLE_CLIENT
N
trusted-app
client
ROLE_TRUSTED_CLIENT
Y


Here is what we find out

Grant Type
User
Client
Principal
Authorities
Authorization Code
user
normal-app
user
ROLE_USER
Client Credentials
NA
trusted-app
trusted-app
No Authority
Implicit
user
normal-app
user
ROLE_USER
Resource Owner Password Credentials
user
trusted-app
user
ROLE_USER

This result is pretty as expected except for Client Credentials. Interestingly, even though the client retrieves Oauth 2 token by client credential, the approved request still does not have any of client authorities but only client credential. I think this make sense because the token from Implicit Grant Type cannot be reused.

24 comments:

  1. Work on exercises that'll boost your overall speed. During a football game, you'll be asked to sprint a lot. This will definitely tax your body. You'll be pushed during every game. To get ready for it,satta king play bazaar build sprints into practice sessions. It'll help you perform the best that you can come game time.play bazaar satta king

    ReplyDelete
  2. GET Upto 30% OFF on Airlines Flights Booking. Many Airlines are offering the biggest discounts and offer, Know what exactly offering the Airlines Reservations, Know Delta, American, Alaska, Southwest, United and Spirit Airlines reservations and deals. Contact to Our Experts Toll-Free Numbers Call +1(800)980-7884 Airline Tickets, Travel Deals and Flights to worldwide destinations.
    Airlines Reservations Number
    Delta Airlines Reservations Number
    Alaska Airlines Reservations Number
    American Airlines Flights
    American Airlines Reservations Number
    American Airlines Reservations
    Delta Airlines Reservations
    Delta Airlines Reservations Number
    Southwest Airlines Reservation
    Southwest Airlines Reservations Number
    United Airlines Reservations
    United Airlines Reservations Number
    Alaska Airlines Reservations
    Alaska Airlines Reservations Number
    Spirit Airlines Reservations Number
    Spirit Airlines Reservations

    ReplyDelete
  3. This comment has been removed by the author.

    ReplyDelete


  4. Search all the best cheap flights air travel websites and compare their rates, sites such as like Cheap Flights to London by fly Cheapest Flights

    ReplyDelete
  5. Check out our video for Oauth JWT with us at our YouTube channel Nooble. This video will show you a simple implementation of OAuth and JPA.

    ReplyDelete
  6. Always i appreciated such useful post . Thanks a lot guy for such brilliant shared. Image Retouching | Image Masking Service | Image Cropping

    ReplyDelete
  7. government jobs 12th pass https://www.examlabonline.com/a/government-jobs-10th-12th-pass
    upcoming competitive exams https://www.examlabonline.com/a/upcoming-government-exams
    SBI Clerk https://www.examlabonline.com/exam/sbi-clerk-mock-test-series
    SBI PO https://www.examlabonline.com/exam/sbi-po-mock-test-series nice sites

    ReplyDelete
  8. Every international jurisdiction abides by a different set of legal structures for taxation and banking. Confidus Solutions helps you to understand the nuances of each country's legal structures. To do business in Netherlands, it will be critical for you to have a firm grasp on the financial and legal implications. http://www.confiduss.com/en/jurisdictions/netherlands/business/bank-account-opening/

    ReplyDelete
  9. Cool, my website https://simplechinesefood.com/ also use JWT auth, it is very powerfull

    ReplyDelete
  10. First of all, your blog is fun. I would love to see more articles like this. I will always check your blog next time, as you have great articles. If you would like to learn more
    about the topic Business Games 2021, you can also visit the article I wrote about the topic. This post requires a lot of research and time for me. Please visit Business Simulation Games and let us know what you think.

    ReplyDelete
  11. This very informative and interesting blog. I have read many blog in days but your writing style is very unique and understanding.
    If you're planning a trip to Bangladesh and want to make the most of your time there, look no further than the best travel agency in the country. Our team of experienced professionals is dedicated to providing our clients with the highest level of service and expertise, ensuring that every aspect of their trip is seamless and stress-free.

    ReplyDelete