Multi-tenancy Support for OpenID Connect Applications
As mentioned in my last blog post, applications deployed to WildFly can be secured with OpenID Connect, without needing to use the Keycloak client adapter.
WildFly 26.0.1.Final, which was just released, includes a fix for multi-tenancy support for OpenID Connect applications. This blog post gives an overview of how to configure OpenID Connect applications deployed to WildFly so they can support multi-tenancy.
Multi-tenancy
Multi-tenancy allows a single application to serve multiple tenants. This means that it’s possible to use
a different authentication policy for each tenant. In other words, it’s possible for a single application
to be associated with multiple OpenID Connect configuration files. As an example,
when using the Keycloak OpenID provider, you might want a different oidc.json
file to be used
depending on the request that was received in order to authenticate users from multiple Keycloak realms.
The elytron-oidc-client
subsystem allows your application to support multi-tenancy by making it
possible to use a custom configuration resolver so you can define which configuration file to use for
each request.
Using a Custom Configuration Resolver
To add multi-tenancy support to your OpenID Connect application, two steps are needed.
First, you’ll need to create a class that implements the org.wildfly.security.http.oidc.OidcClientConfigurationResolver
interface,
as shown in the example below:
package org.wildfly.security.examples;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.wildfly.security.http.oidc.OidcClientConfiguration;
import org.wildfly.security.http.oidc.OidcClientConfigurationBuilder;
import org.wildfly.security.http.oidc.OidcClientConfigurationResolver;
import org.wildfly.security.http.oidc.OidcHttpFacade;
public class MyCustomConfigResolver implements OidcClientConfigurationResolver {
private final Map<String, OidcClientConfiguration> cache = new ConcurrentHashMap<>();
@Override
public OidcClientConfiguration resolve(OidcHttpFacade.Request request) {
String path = request.getURI();
String tenant = ... // determine the tenant from the request
OidcClientConfiguration clientConfiguration = cache.get(tenant);
if (clientConfiguration == null) {
InputStream is = getClass().getResourceAsStream("/oidc-" + tenant + ".json"); // config to use based on the tenant
clientConfiguration = OidcClientConfigurationBuilder.build(is);
cache.put(tenant, clientConfiguration);
}
return clientConfiguration;
}
}
Next, to specify that you want to make use of your custom configuration resolver, you’ll need to set
the oidc.config.resolver
context-param
in your application’s web.xml
file, as shown in the example below:
<web-app>
...
<context-param>
<param-name>oidc.config.resolver</param-name>
<param-value>org.wildfly.security.examples.MyCustomConfigResolver</param-value>
</context-param>
...
</web-app>
A Complete Example with Multiple Keycloak Realms
In the rest of this post, we’ll go through an example to see how to add support for multi-tenancy to an OpenID Connect application that will be deployed to WildFly. Our tenants will be multiple Keycloak realms.
Example Project
First, clone the elytron-examples
repo locally:
git clone https://github.com/wildfly-security-incubator/elytron-examples
cd elytron-examples
We’re going to be looking at the multi-tenancy-oidc project.
Setting up your Keycloak OpenID provider
It’s easy to set up Keycloak using Docker. Follow the steps in Keycloak’s getting started guide
to start Keycloak, create a realm called tenant1
, create a user called alice
, and register a client called myclient
. After registering our client,
myclient
, we also need to configure valid redirect URIs. Simply click on Clients
and then on myclient
. In the Valid Redirect URIs
field,
enter http://localhost:8090/multi-tenancy-oidc/*.
Now, because we’re going to make use of two Keycloak realms, let’s repeat these steps again to create a second realm called
tenant2
, create a user called bob
, and register a client with the same name we used above, myclient
.
After registering our client, myclient
, we also need to configure valid redirect URIs. Simply click on Clients
and then
on myclient
. In the Valid Redirect URIs
field, enter http://localhost:8090/multi-tenancy-oidc/*.
Deploying the app to WildFly
Take a look at the custom configuration resovler defined in our application. Notice that the oidc-tenant1.json configuration file will be used for requests of the form http://localhost:8090/multi-tenancy-oidc/tenant1. Similarly, the oidc-tenant2.json configuration file will be used for requests of the form http://localhost:8090/multi-tenancy-oidc/tenant2.
Finally, notice that we’ve specified that we want to use our custom configuration resolver in our application’s web.xml file.
Now let’s deploy our application to WildFly.
First, we’re going to start our WildFly instance (notice that we’re specifying a port offset here since our Keycloak instance is already exposed on port 8080):
./bin/standalone.sh -Djboss.socket.binding.port-offset=10
Next, we’re going to build and deploy our project. From our elytron-examples
directory, run the
following commands:
cd multi-tenancy-oidc
mvn wildfly:deploy -Dwildfly.port=10000
Accessing the app
Now, let’s try accessing our application using http://localhost:8090/multi-tenancy-oidc/tenant1.
Click on "Access Secured Servlet".
Now, you’ll be redirected to Keycloak to log in. Log in with alice
and the password that you
set when configuring Keycloak.
Next, you’ll be redirected back to our application and you should see the "Secured Servlet" page.
That means that we’ve been able to successfully log in to our application using the tenant1
Keycloak realm. If you repeat the process and try logging in as bob
, authentication will
fail because bob
is not a user in the tenant1
realm.
Now, let’s try accessing our application using http://localhost:8090/multi-tenancy-oidc/tenant2.
This time, after clicking on "Access Secured Servlet", log in with bob
and the password you
set when configuring Keycloak. Now, because bob
is a user in the tenant2
realm, you’ll be
redirected back to our application and you should see the "Secured Servlet" page.
Summary
This blog post has given an overview of how to add support for multi-tenancy to an OpenID Connect application deployed to WildFly. For more information, be sure to check out the documentation.