{"record":{"id":"af89aa39b66f317a","repo":"quarkusio/quarkus","slug":"back-channel-logout-path-cannot-contain-a-wildcard","errorCode":null,"errorMessage":"Back-channel logout path cannot contain a wildcard '*' character","messagePattern":"Back-channel logout path cannot contain a wildcard '\\*' character","errorType":"validation","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/BackChannelLogoutHandler.java","lineNumber":111,"sourceCode":"            }\n        }\n    }\n\n    private Set<String> createOrUpdatePathMatcher() {\n        ImmutablePathMatcher.ImmutablePathMatcherBuilder<Handler<RoutingContext>> builder = null;\n        Map<String, OidcTenantConfig> pathCache = null;\n        Set<String> tenantIdCache = null;\n        for (TenantConfigContext configContext : resolver.getTenantConfigBean().getAllTenantConfigs()) {\n            if (configContext.ready() && configContext.oidcConfig().tenantEnabled()\n                    && configContext.oidcConfig().logout().backchannel().path().isPresent()) {\n                if (builder == null) {\n                    builder = ImmutablePathMatcher.builder();\n                    pathCache = new HashMap<>();\n                    tenantIdCache = new HashSet<>();\n                }\n                String routePath = getTenantLogoutPath(configContext);\n                if (routePath.contains(\"*\")) {\n                    throw new IllegalStateException(\"Back-channel logout path cannot contain a wildcard '*' character\");\n                }\n                OidcTenantConfig previousConfig = pathCache.put(routePath, configContext.oidcConfig());\n                tenantIdCache.add(configContext.oidcConfig().tenantId().get());\n                if (previousConfig == null) {\n                    Handler<RoutingContext> routeHandler = new RouteHandler(configContext, resolver);\n                    builder.addPath(routePath, routeHandler);\n                } else {\n                    String previousTenantId = previousConfig.tenantId().get();\n                    String currentTenantId = configContext.oidcConfig().tenantId().get();\n                    // maybe invalid state, but technically it could happen that some produces a static tenant with\n                    // a same id as a dynamic tenant\n                    if (!previousTenantId.equals(currentTenantId)) {\n                        String errorMessage = \"OIDC tenants '%s' and '%s' share the same back-channel logout path '%s', which is not supported\"\n                                .formatted(previousTenantId, currentTenantId, routePath);\n                        LOG.error(errorMessage);\n                        throw new OIDCException(errorMessage);\n                    }\n                }","sourceCodeStart":93,"sourceCodeEnd":129,"githubUrl":"https://github.com/quarkusio/quarkus/blob/e1c734241f34c7919086ceb4c9262b4a58f6de44/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/BackChannelLogoutHandler.java#L93-L129","documentation":"BackChannelLogoutHandler builds a path matcher from each tenant's back-channel logout path. A wildcard '*' in such a path cannot be matched deterministically for logout callbacks, so an IllegalStateException is thrown while creating/updating the path matcher.","triggerScenarios":"Configuring a tenant's back-channel logout path (e.g. via OidcTenantConfig logout.backchannelPath or getTenantLogoutPath) with a '*' glob character, then building the handler via createPathMatcher or currentTenantIds.","commonSituations":"Reusing an HTTP route pattern (like /logout/*) as the back-channel logout path; copying wildcard path syntax from quarkus.http auth permission paths into OIDC tenant config.","solutions":["Set the back-channel logout path to a literal path without '*', e.g. /back-channel-logout","Configure wildcard matching at the HTTP level if needed, keeping the OIDC tenant path exact","Give each tenant a distinct exact logout path"],"exampleFix":"// before\nquarkus.oidc.tenant-a.logout.backchannel-path=/logout/*\n// after\nquarkus.oidc.tenant-a.logout.backchannel-path=/logout/tenant-a","handlingStrategy":"validation","validationCode":"String p = tenant.logout().backchannel().path().orElse(\"\");\nif (p.contains(\"*\")) throw new IllegalArgumentException(\"back-channel logout path must not contain '*': \" + p);","typeGuard":null,"tryCatchPattern":"try { handler.createPathMatcher(); } catch (IllegalStateException e) { if (e.getMessage().contains(\"wildcard\")) { /* fix tenant logout path config */ } }","preventionTips":["Use literal, exact logout paths per tenant","Do not reuse HTTP route wildcard syntax in OIDC config","Validate tenant config in integration tests"],"tags":["oidc","config","logout"],"backgroundTag":"wildcard-path-not-allowed","analyzedSha":"e1c734241f34c7919086ceb4c9262b4a58f6de44","analyzedAt":"2026-09-05T17:01:29.979Z","contentChangedAt":"2026-09-05T17:01:29.979Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}