Quarkus Cross-Tenant Authorization Bypass — CVE-2026-19625

Authorization Bypass
Affects
io.quarkus:quarkus-oidc
in
Quarkus
No items found.
Versions
<3.39.2

Patch Available.

Exclamation circle icon
Patch Available

This Vulnerability has been fixed in the Never-Ending Support (NES) version offered by HeroDevs.

Overview

Quarkus is a Kubernetes-native Java framework optimized for cloud-native applications, containers, and serverless workloads. It builds on standard libraries (Jakarta EE, Eclipse Vert.x, Hibernate, Netty) and compiles to both JVM and GraalVM native images for fast startup and low memory use. The OIDC extension, published as io.quarkus:quarkus-oidc, secures Quarkus endpoints with OpenID Connect providers and handles bearer token authentication, the authorization code flow, multi-tenancy, and optional caching of remote token introspection and UserInfo results.

An Authorization Bypass vulnerability (CVE-2026-19625) has been identified in the default token introspection and UserInfo cache of the Quarkus OIDC extension, which allows attackers to present an opaque access token that is valid for one OIDC tenant and have it accepted on an endpoint secured by a different OIDC tenant, bypassing that tenant's authentication.

Per OWASP: Access control enforces policy such that users cannot act outside of their intended permissions. Failures typically lead to unauthorized information disclosure, modification, or destruction of all data or performing a business function outside the user's limits.

This issue affects the multi-tenant token introspection and UserInfo caching path of Quarkus.

Details

Module Info

Vulnerability Info

This Medium-severity vulnerability is found in the io.quarkus:quarkus-oidc package in the default token introspection and UserInfo cache of Quarkus.

When a Quarkus endpoint is secured by an OIDC provider and receives an opaque bearer access token, the token cannot be verified locally and must be introspected remotely. To avoid a remote round trip on every request, the OIDC extension offers a built-in result cache, DefaultTokenIntrospectionUserInfoCache, enabled through the token cache configuration. The cache is disabled by default and is active only when quarkus.oidc.token-cache.max-size is set above 0. Each of its four entry points receives the OidcTenantConfig of the tenant that is currently authenticating the request, but none of them uses it in the cache key; the raw access token string alone selects the entry:

// io.quarkus.oidc.runtime.DefaultTokenIntrospectionUserInfoCache
public class DefaultTokenIntrospectionUserInfoCache implements TokenIntrospectionCache, UserInfoCache {
    // ...
    final MemoryCache<CacheEntry> cache;
    // ...

    @Override
    public Uni<Void> addIntrospection(String token, TokenIntrospection introspection, OidcTenantConfig oidcTenantConfig,
            OidcRequestContext<Void> requestContext) {
        CacheEntry entry = cache.get(token);
        if (entry != null) {
            entry.introspection = introspection;
        } else {
            cache.add(token, new CacheEntry(introspection));
        }

        return CodeAuthenticationMechanism.VOID_UNI;
    }

    @Override
    public Uni<TokenIntrospection> getIntrospection(String token, OidcTenantConfig oidcConfig,
            OidcRequestContext<TokenIntrospection> requestContext) {
        CacheEntry entry = cache.get(token);
        if (entry == null || entry.introspection == null) {
            return NULL_INTROSPECTION_UNI;
        }
        if (isTokenExpired(entry.introspection.getLong(OidcConstants.INTROSPECTION_TOKEN_EXP), oidcConfig)) {
            // ...
            cache.remove(token);
            return NULL_INTROSPECTION_UNI;
        }

        return Uni.createFrom().item(entry.introspection);
    }
    // ...

    @Override
    public Uni<Void> addUserInfo(String token, UserInfo userInfo, OidcTenantConfig oidcTenantConfig,
            OidcRequestContext<Void> requestContext) {
        CacheEntry entry = cache.get(token);
        if (entry != null) {
            entry.userInfo = userInfo;
        } else {
            cache.add(token, new CacheEntry(userInfo));
        }

        return CodeAuthenticationMechanism.VOID_UNI;
    }

    @Override
    public Uni<UserInfo> getUserInfo(String token, OidcTenantConfig oidcConfig,
            OidcRequestContext<UserInfo> requestContext) {
        CacheEntry entry = cache.get(token);
        return entry == null ? NULL_USERINFO_UNI : Uni.createFrom().item(entry.userInfo);
    }
    // ...
}

Because the cache is a single application-wide instance shared by every tenant, the token string alone determines a hit. In an application where several endpoints are secured by different OIDC tenants, a token that tenant A has already introspected and cached is returned as a valid introspection result when the same token is presented to an endpoint guarded by tenant B. The second tenant's introspection endpoint is never contacted, so its own validation, audience checks, and role mapping never run, and the caller is authenticated as if tenant B had approved the token. The same token-only lookup applies to cached UserInfo, so one tenant's UserInfo document is also served to requests authenticating against another tenant. An attacker who legitimately holds a token for the least privileged tenant, or who can cause a token to be cached on any tenant, can therefore reach endpoints that tenant was never meant to have access to; no privileges beyond possession of a token accepted by one tenant are required. Earlier affected releases carry the same defect in an inline map form, storing entries with cacheMap.put(token, ...) and reading them back through findValidCacheEntry(token) while the tenant configuration argument is likewise unused.

The correct behavior is to derive the cache key from both the authenticating tenant identifier and the token, so that entries written under one tenant can never satisfy a lookup performed under another.

Mitigation

Only recent versions of Quarkus are community-supported. The 2.16.x and 3.20.x lines were already End-of-Life when this CVE was published and will not receive public updates to address this issue. Quarkus LTS releases are maintained for 12 months from release; see the project's LTS release policy.

Users of the affected components should apply one of the following mitigations:

  • Upgrade Quarkus to a currently supported release that contains the fix.
  • Disable the built-in token introspection and UserInfo cache, or replace it with a tenant-aware implementation, in any application that secures endpoints with more than one OIDC tenant.
  • Leverage a commercial support partner like HeroDevs for post-EOL security support.

Credits

Vulnerability Details
Severity
Level
CVSS Assessment
Low
>=0 <4
Medium
>=4 <6
High
>=6 <8
Critical
>=8 <10
Medium
ID
Quarkus Cross-Tenant Authorization Bypass — CVE-2026-19625
PROJECT Affected
io.quarkus:quarkus-oidc
Versions Affected
<3.39.2
NES Versions Affected
Published date
September 17, 2026
≈ Fix date
September 17, 2026
Category
Authorization Bypass
Vex Document
Download VEXHow do I use it?
Sign up for the latest vulnerability alerts fixed in
NES for Quarkus
Rss feed icon
Subscribe via RSS
or

By submitting the form I acknowledge receipt of our Privacy Policy.

Thanks for signing up for our Newsletter! We look forward to connecting with you.
Oops! Something went wrong while submitting the form.