quarkusio/quarkus · error · OIDCException

OIDC tenants '%s' and '%s' share the same back-channel logou

Error message

OIDC tenants '%s' and '%s' share the same back-channel logout path '%s', which is not supported

What it means

When two OIDC tenants (static or dynamic) resolve to the same back-channel logout path, the path matcher cannot route a logout callback to a unique tenant. The handler throws OIDCException (after logging an error) unless the duplicate comes from a previous config identical to the current tenant id.

Source

Thrown at extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/BackChannelLogoutHandler.java:127

                String routePath = getTenantLogoutPath(configContext);
                if (routePath.contains("*")) {
                    throw new IllegalStateException("Back-channel logout path cannot contain a wildcard '*' character");
                }
                OidcTenantConfig previousConfig = pathCache.put(routePath, configContext.oidcConfig());
                tenantIdCache.add(configContext.oidcConfig().tenantId().get());
                if (previousConfig == null) {
                    Handler<RoutingContext> routeHandler = new RouteHandler(configContext, resolver);
                    builder.addPath(routePath, routeHandler);
                } else {
                    String previousTenantId = previousConfig.tenantId().get();
                    String currentTenantId = configContext.oidcConfig().tenantId().get();
                    // maybe invalid state, but technically it could happen that some produces a static tenant with
                    // a same id as a dynamic tenant
                    if (!previousTenantId.equals(currentTenantId)) {
                        String errorMessage = "OIDC tenants '%s' and '%s' share the same back-channel logout path '%s', which is not supported"
                                .formatted(previousTenantId, currentTenantId, routePath);
                        LOG.error(errorMessage);
                        throw new OIDCException(errorMessage);
                    }
                }
            }
        }
        if (builder != null) {
            pathMatcher = builder.build();
        } else {
            pathMatcher = null;
        }
        return tenantIdCache;
    }

    private String getTenantLogoutPath(TenantConfigContext tenant) {
        return OidcUtils.getRootPath(resolver.getRootPath()) + tenant.oidcConfig().logout().backchannel().path().orElse(null);
    }

    private static final class RouteHandler implements Handler<RoutingContext> {
        private final TenantConfigContext tenantContext;

View on GitHub (pinned to e1c734241f)

Solutions

  1. Give each tenant a unique back-channel logout path (e.g. prefix with tenant id)
  2. Remove or merge the duplicate tenant configuration
  3. If a dynamic tenant duplicates a static tenant intentionally, ensure ids match, otherwise fix the path

Example fix

// before
quarkus.oidc.tenant-a.logout.backchannel-path=/back-channel-logout
quarkus.oidc.tenant-b.logout.backchannel-path=/back-channel-logout
// after
quarkus.oidc.tenant-a.logout.backchannel-path=/tenant-a/back-channel-logout
quarkus.oidc.tenant-b.logout.backchannel-path=/tenant-b/back-channel-logout
Defensive patterns

Strategy: validation

Validate before calling

Set<String> paths = new HashSet<>();
for (tenant : allTenants) {
    String p = tenant.logout().backchannel().path().orElse(DEFAULT);
    if (!paths.add(p)) throw new IllegalStateException("Duplicate back-channel logout path: " + p);
}

Try / catch

try { handler.createPathMatcher(); } catch (OIDCException e) { if (e.getMessage().contains("share the same back-channel logout path")) { /* make paths unique per tenant */ } }

Prevention

When it happens

Trigger: Two tenants whose back-channel logout paths (or default paths) are equal — e.g. tenant-a and tenant-b both configured with the same backchannel-path — while building the path matcher via createPathMatcher/currentTenantIds.

Common situations: Copy-pasting tenant config and leaving the same logout path; a dynamic tenant registered at runtime that collides with a static tenant's path.

Related errors


AI-assisted analysis of quarkusio/quarkus@e1c734241f (2026-09-05). Data as JSON: /api/errors/1bd33891b9e9ec3c. Report an issue: GitHub.