Additional Features for OpenId Connect with Payara Platform

Photo of Rudy De Busscher by Rudy De Busscher

With the release of the Payara Platform products in August 2021, we have added some additional features when you are using the OpenID Connect option within Payara Platform products.

The first option allows you to configure the connector based on some identification contained within the user request. This allows you to realise a multi-tenant solution where your application is used by users from multiple companies that are defined within different systems.

The second feature is that you can use the Access Token to propagate the authentication and authorization information to other services instead of creating a JWT token and use MicroProfile JWT for example.

Recap of OpenIdConnect

Here's a short refresher of what OpenId Connect is can how you can use it: OpenID Connect is a security mechanism for an application to contact an identity service, verify the identity of the end-user, and obtain authentication and authorization information.

You can see it as outsourcing your login functionality. Instead of maintaining user names and passwords in a database yourself, for example, you use an external system for this. The login of the end-user is handled by the OpenId Connect provider and you receive information about the identity of the user through a callback method. Due to the use of signed data constructs and other mechanisms like a nonce, the entire protocol is secure and reliable.

You can read more about the topic in the OpenID Connect in the Payara Platform blog and on our documentation page.

Multi-tenant Support

Requested by one of our customers, we have added the option to support a multi-tenant solution to the OpenId Connection security feature.

Within the Jakarta EE security specification, there is only an option to define one security mechanism for an application. With an application supporting multi-tenant option, you need to be able to use a different provider based on the user that accesses your application. This is not available within the Jakarta specification.

The request, like a different domain name, indicates how the user is accessing your application. Based on that information, you can decide to use a different OpenId Connect provider and although you are using only one application, users can be maintained in different providers that don't interact with each other.

With this new Payara Platform release, you can specify an EL expression within the configuration of the OpenIdAuthenticationDefinition annotation.

@OpenIdAuthenticationDefinition(
providerURI = "https://rubus.eu.auth0.com"
, clientId = "#{tenantControlBean.clientId}"
, clientSecret = "#{tenantControlBean.clientSecret}"
)

Instead of defining some constants (they should be configured within a MicroProfile configuration source and not hardcoded within the application), we now refer to a TenantControlBean which will provide the clientId and clientSecret value. Different values for these pairs allows different applications within our Provider and a separated users base.

The TenantControlBean in our case is a named CDI bean so that it can be instantiated for each user session.  The bean can have a reference to the HTTP request and thus identify the multi-tenant markers to determine the tenant the user belongs to.

@Named
public class TenantControlBean {

   @Inject
   private HttpServletRequest request;

   @Inject
   private Config config;

   private static final String BASE_OPENID_KEY = "demo.security.openid";

   public String getClientId() {
      String tenant = getTenant(request); // a custom method to decide which tenant to use
      return config
         .getOptionalValue(BASE_OPENID_KEY + ".clientId." + tenant, String.class)
         .orElse("Unknown");
   }

   public String getClientSecret() {
      String tenant = getTenant(request); // a custom method to decide which tenant to use
      return config
         .getOptionalValue(BASE_OPENID_KEY + ".clientSecret." + tenant, String.class)
         .orElse("Unknown");
   }

   private String getTenant(HttpServletRequest request) {
      return request.getParameter("tenant"); // resolves the tenant name from a query parameter
   }

}

 

As this behavior deviates from the standard, it needs to be activated. Without the following configuration property in a MicroProfile source, the EL expression is only evaluated once and not for every user.

payara.security.openid.sessionScopedConfiguration=true

Bearer Authentication

The OpenId Connect integration with the Authorization code flow as we have used in the previous example works very well for the login within a web application in the browser.  But what if our application uses calls to other services when we have a microservices-like architecture?  Our JAX-RS-client call to the other service can't respond to the challenge to enter user credentials. And in fact, the user is already authenticated in the application so if we can propagate this information to the other service that would be much easier.

Until the new release, you needed to construct a JWT (JSON Web Token) yourself and use  MicroProfile JWT specification on the other service to validate and capture the propagated authentication information.

With the August 2021 release, we have added the option to use the AccessToken you received from the OpenId Connect provider to perform the authentication within the service. And there is no need to create a new JWT token and maintain cryptographic keys yourself.

You can just define the same @OpenIdAuthenticationDefinition within the service and protect the JAX-RS endpoint with a @RolesAllowed annotation.  The Payara implementation for the OpenId Connect protocol handles the necessary steps of validating the token when it receives an Authorization header starting with the word Bearer.

Your application can define a Client Header Factory with the MicroProfile Rest Client specification to propagate the AccessToken and thus propagate the user information to the other service.

@ApplicationScoped
public class BearerHeaderFactory implements ClientHeadersFactory {

   @Inject
   private OpenIdContext openIdContext;

   @Override
   public MultivaluedMap<String, String> update(MultivaluedMap<String, String> multivaluedMap, MultivaluedMap<String, String> multivaluedMap1) {
      MultivaluedMap<String, String> result = new MultivaluedHashMap<>();

      result.add("Authorization", "Bearer "+ openIdContext.getIdentityToken());
      return result;
   }
}

Conclusion

The OpenId Connect protocol is a modern approach to perform the authentication and authorization for your applications. You can use local providers like Keycloak or cloud-based ones like Auth0 or Azure Active Directory.

With the Payara Platform release of August 2021, we have added some new functionalities related to this OpenId Connect functionality.

By allowing EL expressions that are evaluated for each user, you can achieve a multi-tenant solution where multiple groups of users can use the same application but the secure storage is done in separate stores.

With the Bearer support, you can propagate more easily the identity of the user when your application calls other services running on Payara instances.  Instead of creating new JWT tokens, you can propagate the OpenId Connect token which is used by the service the identify the user who made the original call to the web application.

 

 

Comments