Hybrid Deployments

Scaling Your Java MicroProfile-Based Microservices App (2 of 3)

Share this post:

MicroProfile makes it easier to secure your important microservices apps

Cloud-native applications have many challenges. Among them, security is one of the most important areas. When the user is trying to access your application, do you trust the client? Better to verify! Do you think the incoming request coming from the upstream service is safe and secure? Of course, not. Do you know who is accessing it and do they have the right permissions? Hopefully! Of course, programmers are more likely to apply security measures consistently if the required code is standardized and simple.

Securing the data from REST services is very important while building cloud-native applications. Since the data flows across different services, we need to make sure that the data is clean and secure. Also, to allow different users to access the application, we want to verify the user’s identity and their access rights. For MicroProfile applications, you can easily secure your REST API endpoints using a token-based approach with MicroProfile JSON Web Token (JWT) Propagation. To know the basics about JWT, have a look here, and to know more about MicroProfile JWT, have a look at here.

In this blog series, we’re covering how to handle the factors below when handling scalability for Liberty’s MicroProfile-based microservices:

  • Externalizing configuration
  • Security
  • Fault tolerance

In our last blog post, we covered how to externalize your microservice’s configuration using MicroProfile Config. In today’s entry, you’ll learn how to secure the REST APIs using MicroProfile JWT Propagation. You can learn how we implemented them in our simple storefront application.

MicroProfile

For those who may have joined late, this blog series is based on my team’s experience migrating our simple storefront application from a Spring Boot-based microservices to MicroProfile, an optimized microservices programming model for enterprise Java. It was originally coded using Spring Boot (source code) and migrated to MicroProfile (source code). Both the projects are hosted on GitHub with source code and full documentation.

Spring Boot – Authentication and Authorization

Spring Security: For user authorization and authentication, Spring Security Authentication and Authorization server is used. All the RESTful services are protected using OAuth 2.0. The communication client is Spring Feign Client. We used signed JWT Bearer tokens for identity propagation. The flow diagram below shows how we implemented it for the Spring Boot version of BlueCompute:

Control flow - Spring Boot Authentication and Authorization

  1. When a client wishes to acquire an OAuth token to call a protected API, it calls the Auth microservice (OAuth Provider) token endpoint with valid credentials and required scope.
  2. Auth microservice, in turn, calls the identity provider to validate credentials. In our sample application, the Customer microservice is the identity provider. The login credentials are validated with the identity provider using Spring Feign Client.
  3. If the credentials are valid, it returns a JWT signed using a HS256 shared secret in the JSON response under access_token, which contains the customer ID of the user passed in the user_name claim.
  4. The client uses the JWT in the Authorization header as a bearer token to call other Resource Servers that have OAuth protected APIs. These services verify the JWT and extract the user_name claim from the JWT to identify the caller. The JWT is encoded with the required scope and the expiry time.

Once the token is generated, there is no additional interaction between the Resource Server and the OAuth server until the token expires. This is how the security mechanism works in our simple storefront application for Spring Boot implementation.

For more details, check out how we implemented our Auth service in Spring Boot here.

MicroProfile – Authentication and Authorization

OpenID Connect: For user authorization and authentication in our MicroProfile implementation, the Auth microservice is set up as an OpenID Connect Provider. OpenID Connect is built on top of OAuth 2.0.

In OAuth 2.0, authentication is not done immediately. For this to be done, it contacts the resource provider to acquire the user information. In OpenID Connect, both authorization and authentication are available at the same time. The user information is provided as a set of claims from the UserInfo endpoint. The client receives the user information along with the access token, which is a MicroProfile JWT in our case here, generated from the Token endpoint.

MicroProfile Json Web Token (JWT) Propagation: This feature allows the developers to secure their microservices and propagate user identities using JWTs. This token-based security approach helps in securing the RESTful APIs by generating tokens which consist of the user identity, claims, grants, expiry time, and other useful information. Whenever a user requests a service, they will be permitted to use it if the token is valid. Once the token is generated, there is no need of going back to the Auth server. But if the token expires, the user access will be denied and they will be redirected to the Auth server.

The flow diagram below depicts how we implemented it on the MicroProfile version of BlueCompute:

Control flow - MicroProfile Authentication and Authorization

Initially, the client interacts with the Auth service, which validates the user credentials. Later, when the client wishes to call a protected API:

  1. The client requests an access token to call a protected API by calling the Auth microservice (OpenID Connect Provider) token endpoint with valid credentials and required scope.
  2. The Auth microservice will then perform the validation. If the credentials are valid, it returns a MicroProfile JWT  as an access_token.
  3. The client uses the access token in the Authorization header as a bearer token to call other Resource Servers having the REST endpoints secured with MicroProfile JWT Propagation. These services verify the MicroProfile JWT in the request and extract the required claims from the MicroProfile JWT to identify the caller.

The MicroProfile JWT contains the required scope and the expiry time. Once the token is generated, there is no additional interaction between the Resource Server and the OpenID Connect Server unless the token expires. This is how the security mechanism works in our simple storefront application for MicroProfile implementation.

To summarize:

  • Define an authentication server that handles user authentication and authorization using Liberty’s OpenID Connect 1.0 feature.
  • Use MicroProfile JWT Propagation to secure the REST services. This MicroProfile JWT Bearer token helps us to validate the user identity as well as the permissions associated with the user.

For more details, check out how we implemented our Auth Service for our sample application here.

Contrasting MicroProfile and Spring Boot security implementations

Let us now dive further into the technical details. In order to access protected resources in your application, Authorization and Authentication are required.

  • Authentication: Verifying the user has the right to access the system based on their identity.
  • Authorization: Verifying the user has the rights to perform specific actions.

Configuring the Auth server: In a Spring Boot application, code does mostly everything. In a Liberty application, we configured WebSphere Liberty as our Auth Server using the OpenID Connect feature and server configuration does most of it. Personally, I felt Liberty’s declarative approach is a lot easier because the server takes care of the details and there is no need for the developer to worry about the code.

Spring Boot MicroProfile
@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfig
  extends AuthorizationServerConfigurerAdapter {

  @Autowired
  private JwtConfig securityConfig;

  protected static class
    CustomTokenEnhancer implements TokenEnhancer {
    ...
  }

  @Bean
  public JwtAccessTokenConverter jwtTokenEnhancer() {
    ...
  }

  @Bean
  @Qualifier("tokenStore")
  public TokenStore tokenStore() {
    ...
  }

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

  @Override
  public void configure
    (AuthorizationServerEndpointsConfigurer endpoints)
      throws Exception {
    ...
  }

  @Override
  public void configure
    (AuthorizationServerSecurityConfigurer oauthServer)
      throws Exception {
    ...
  }
<server description="Sample Liberty server">

  <featureManager>
      <feature>microprofile-1.3</feature>
      <feature>openidConnectServer-1.0</feature>
  </featureManager>

  <ssl id="defaultSSLConfig"
    keyStoreRef="bcKeyStore"/>
  
  <keyStore id="bcKeyStore"
    location="/etc/keystorevol/BCKeyStoreFile.jks"
    type="JKS" password="password" />

  <openidConnectProvider id="OP"
    oauthProviderRef="Oauth"
    signatureAlgorithm="RS256"
    keyStoreRef="bcKeyStore"
    keyAliasName="bckey"
    issuerIdentifier="${env.jwksIssuer}"/>

  <oauthProvider httpsRequired="true"
      id="Oauth" tokenFormat="mpjwt"  >
    <localStore>
    <client name="bluecomputeweb"
      secret="bluecomputewebs3cret"
      displayname="bluecomputeweb"
      grant_types="password"
      scope="openid"
      preAuthorizedScope="openid"
      enabled="true"
      mapIdentityToRegistryUser="true">
    </client>
    </localStore>
  </oauthProvider>
   
  <oauth-roles>
    <authenticated>
        <special-subject
        type="ALL_AUTHENTICATED_USERS" />
    </authenticated>
  </oauth-roles>

</server>

REST services consuming JWTs: In the Spring Boot application, REST APIs are secured using OAuth2.0. The access token used is a JWT token signed using a HS256 shared secret. In the MicroProfile application, REST APIs are secured using MicroProfile JWT Propagation. The access token used is MicroProfile JWT.

Spring Boot MicroProfile
@Configuration
@EnableWebSecurity
@EnableREsourceServer
public class OAuth2ResourceServerConfig extends 
ResourceServerConfigurerAdapter {

  @Autowired
  private JWTConfig securityConfig;
    ...
    ...
}

// *******

@RestController
public class CustomerController {

  @Autowired
  private OrdersRepository ordersRepo;

  @RequestMapping(value = "/orders",
      method = RequestMethod.GET)
  @ResponseBody ResponseEntity<?> getOrders() {
  try {
    final String customerId = getCustomerId();
      if (customerId == null) {
        // if no user passed in, bad request 
      }
      
      final List<Order> orders = 
        ordersRepo.findByCustomerIdOrderByDateDesc
        (customerId);
      return  ResponseEntity.ok(orders);
    } catch (Exception e) {
      return ResponseEntity.status
        (HttpStatus.INTERNAL_SERVER_ERROR).build();
    }
  }

  private String getCustomerId() {
    final SecurityContext ctx =
      SecurityContextHolder.getContext();
   if (ctx.getAuthentication() == null) {
     return null;
   }
   if (!ctx.getAuthentication().isAuthenticated()) {
     return null;
   }
 
   final OAuth2Authentication oauth =
      (OAuth2Authentication) ctx.getAuthentication();
   return oauth.getName();
  }
}
@DeclareRoles({"Admin", "User"})
@RequestScoped
@Path("/orders")
public class OrderService {    

  @Inject
  private JsonWebToken jwt;

  public Response getOrders() throws Exception {        
    final String customerId = jwt.getName();           
    if (customerId== null) {
      return "BAD_REQUEST";
    } 

    OrderDAOImpl ordersRepo = new OrderDAOImpl();            
    final List<Order> orders = 
      ordersRepo.findByCustomerIdOrderByDateDesc
        (customerId);            
    return Response.ok(orders).build();        
  }
}

The REST APIs are now all secured. For more details on how the Auth service is built, refer to the project introduction on GitHub. To run the whole application together, please check out the BlueCompute – MicroProfile implementation on GitHub.

Conclusion

Microservices have many benefits, and given their strategic importance, securing them is critical. Using MicroProfile, you can safely secure your RESTful services with MicroProfile JWT Propagation. When working on securing our services with MicroProfile, the guide from Open Liberty Securing microservices with JSON Web Tokens was very helpful.

This blog demonstrates how we migrated the security of our reference application from Spring Boot to MicroProfile. In our previous blog posts, we covered some interesting topics like RESTful APIs, Dependency Injection, REST Client, JSON-B, and externalizing configurations. In the next installment, we’ll close on the final subject of scaling microservices, fault tolerance.

Cloud Solution Architect

More Hybrid Deployments stories
November 7, 2018

Open Liberty Loves Spring

We think Open Liberty and Spring go well together. Liberty loves Spring and we want to make it easy for Spring Boot developers to love Liberty. With the new boost-maven-plugin plugin, it's handled automatically as part of your maven build.

Continue reading

October 22, 2018

Scaling Your Java MicroProfile-Based Microservices App (1 of 3)

In the next three entries of this blog series, we’ll cover how to handle scalability for Liberty’s MicroProfile-based microservices. First up: Externalizing your configuration in support of different environments like dev, test, and production. Each requires specific values that must be changed based on the deployment environment. The MicroProfile Config Feature makes it easy.

Continue reading

October 17, 2018

Certified Kubernetes Deployments Made Easy with WebSphere Liberty Cloud Pak

You want to have confidence in your cloud-based deployment of WebSphere Liberty on Kubernetes. Developers want an environment that's ready to go, administrators want an infrastructure that's trusted. Who wants to be in the business of stitching together component layers and then verifying they're compatible with each other? With IBM Cloud Paks, rely on a trusted and tested release with key components verified as a set.

Continue reading