5.6. Configuring the application
# Default tenant configuration
%prod.quarkus.oidc.auth-server-url=http://localhost:8180/realms/quarkus
quarkus.oidc.client-id=multi-tenant-client
quarkus.oidc.credentials.secret=secret
quarkus.oidc.application-type=web-app
# Tenant A configuration is created dynamically in CustomTenantConfigResolver
# HTTP security configuration
quarkus.http.auth.permission.authenticated.paths=/*
quarkus.http.auth.permission.authenticated.policy=authenticated
The first configuration is the default tenant configuration that should be used when the tenant cannot be inferred from the request. Be aware that a %prod profile prefix is used with quarkus.oidc.auth-server-url to support testing a multitenant application with Dev Services For Keycloak. This configuration uses a Keycloak instance to authenticate users.
The second configuration, provided by TenantConfigResolver, is used when an incoming request is mapped to the tenant-a tenant.
Both configurations map to the same Keycloak server instance while using distinct realms.
Alternatively, you can configure the tenant tenant-a directly in application.properties:
# Default tenant configuration
%prod.quarkus.oidc.auth-server-url=http://localhost:8180/realms/quarkus
quarkus.oidc.client-id=multi-tenant-client
quarkus.oidc.credentials.secret=secret
quarkus.oidc.application-type=web-app
# Tenant A configuration
quarkus.oidc.tenant-a.auth-server-url=http://localhost:8180/realms/tenant-a
quarkus.oidc.tenant-a.client-id=multi-tenant-client
quarkus.oidc.tenant-a.credentials.secret=secret
quarkus.oidc.tenant-a.application-type=web-app
# HTTP security configuration
quarkus.http.auth.permission.authenticated.paths=/*
quarkus.http.auth.permission.authenticated.policy=authenticated
In that case, also use a custom TenantConfigResolver to resolve it:
package org.acme.quickstart.oidc;
import jakarta.enterprise.context.ApplicationScoped;
import io.quarkus.oidc.TenantResolver;
import io.vertx.ext.web.RoutingContext;
@ApplicationScoped
public class CustomTenantResolver implements TenantResolver {
@Override
public String resolve(RoutingContext context) {
String path = context.request().path();
String[] parts = path.split("/");
if (parts.length == 0) {
//Resolve to default tenant configuration
return null;
}
return parts[1];
}
}
You can define multiple tenants in your configuration file. To map them correctly when resolving a tenant from your TenantResolver implementation, ensure each has a unique alias.
However, using a static tenant resolution, which involves configuring tenants in application.properties and resolving them with TenantResolver, does not work for testing endpoints with Dev Services for Keycloak because it does not know how the requests are be mapped to individual tenants, and cannot dynamically provide tenant-specific quarkus.oidc.<tenant-id>.auth-server-url values. Therefore, using %prod prefixes with tenant-specific URLs within application.properties does not work in both test and development modes.
When a current tenant represents an OIDC web-app application, the current io.vertx.ext.web.RoutingContext contains a tenant-id attribute by the time the custom tenant resolver has been called for all the requests completing the code authentication flow and the already authenticated requests, when either a tenant-specific state or session cookie already exists. Therefore, when working with multiple OIDC providers, you only need a path-specific check to resolve a tenant id if the RoutingContext does not have the tenant-id attribute set, for example:
package org.acme.quickstart.oidc;
import jakarta.enterprise.context.ApplicationScoped;
import io.quarkus.oidc.TenantResolver;
import io.vertx.ext.web.RoutingContext;
@ApplicationScoped
public class CustomTenantResolver implements TenantResolver {
@Override
public String resolve(RoutingContext context) {
String tenantId = context.get("tenant-id");
if (tenantId != null) {
return tenantId;
} else {
// Initial login request
String path = context.request().path();
String[] parts = path.split("/");
if (parts.length == 0) {
//Resolve to default tenant configuration
return null;
}
return parts[1];
}
}
}
This is how Quarkus OIDC resolves static custom tenants if no custom TenantResolver is registered.
A similar technique can be used with TenantConfigResolver, where a tenant-id provided in the context can return OidcTenantConfig already prepared with the previous request.
If you also use Hibernate ORM multitenancy or MongoDB with Panache multitenancy and both tenant ids are the same, you can get the tenant id from the RoutingContext attribute with tenant-id. You can find more information here: