CVE-2026-50627 is a flaw in the JwtAccessTokenValidator class of Apache CXF, a Java web services framework, where the validator did not check the aud (audience) claim of incoming JWT access tokens. A token issued for one API could therefore be replayed against a completely different API that trusted the same issuer. Even when the identity provider signs tokens correctly, as a managed Keycloak service does, each API you operate still has to decide whether a validly signed token was meant for it, and this CVE shows what happens when that decision is skipped.
Key Takeaways
- In June 2026, the Apache CXF project listed CVE-2026-50627 as “important” and fixed it in 4.2.2, 4.1.7 and 3.6.12 (Apache CXF security advisories, 2026).
- A valid signature shows who issued a token, while the
audcheck shows whether the token was meant for your API.- Keycloak adds an API to
audautomatically only when that API client has client roles in the token, so otherwise add the audience yourself and enforce it in every service.
What is CVE-2026-50627 in Apache CXF?
In June 2026, the Apache Software Foundation published CVE-2026-50627 for the cxf-rt-rs-security-oauth2 module of Apache CXF, with the finder credited as Guanping Zhang (CVE Program record CVE-2026-50627, published 2026-06-12). The record says JwtAccessTokenValidator “fails to validate the ‘aud’ (Audience) claims of incoming JWT access tokens”, which allows a JWT issued for one resource server to be replayed against another, producing what the record calls token confusion and routing attacks. A resource server is simply the API that receives the bearer token and serves protected data.
The affected versions are those before 3.6.12, 4.0.0 up to but not including 4.1.7, and 4.2.0 up to but not including 4.2.2. The fixed releases are 3.6.12, 4.1.7 and 4.2.2. There is no fixed 4.0.x release, so a 4.0.x deployment has to move to 4.1.7 or later, and anything older than 3.6 has to move to 3.6.12 or later. At the time of writing, the CXF download page lists 3.6.13, 4.1.9 and 4.2.4 as current (Apache CXF, Download page, retrieved 2026-10-18), and any of those includes the fix.
The CVE record classifies the weakness as CWE-289 (Authentication Bypass by Alternate Name) and rates it “important” in Apache’s own severity scale. NVD and the CISA-ADP entry both score it CVSS 3.1 base 9.1 Critical (vector AV:N/AC:L/PR:H/UI:N/S:C/C:H/I:H/A:H), so Apache’s label is the project’s own judgment and the CVSS number is the scoring agencies’ (Red Hat’s entry on the same NVD page scores it 8.1 High) (NVD, CVE-2026-50627, retrieved 2026-10-18). One detail worth knowing is that the title on the CXF advisory page reads “Missing JWT Audience and Issuer Validation in Access Token Validator”, while the description text only describes the audience problem, so treat audience as the confirmed gap and verify your own issuer handling regardless (Apache CXF, Security Advisories, CVE-2026-50627, retrieved 2026-10-18).
Does CVE-2026-50627 affect Keycloak?
Keycloak itself is not listed as affected. The vulnerable code is in the CXF library that a Java service would use to validate tokens, not in the Keycloak server that issues them. What matters for Keycloak users is the situation the bug creates: if you have an API built on CXF’s JAX-RS OAuth2 module and it accepts tokens from your Keycloak realm, then a token issued to a client for a different API in the same realm would have passed the audience step on a vulnerable version.
This is also why the issue is not limited to CXF. Any resource server that verifies the signature, issuer and expiry but never looks at aud has the same behavior, whether the cause is a library bug or a configuration choice. The IETF’s JWT Best Current Practices document (RFC 8725, February 2020, section 3.9) says that when an issuer can create JWTs for more than one application, the receiving application “MUST validate the audience value” and reject the token if the audience is missing or not associated with the recipient (IETF, RFC 8725, JSON Web Token Best Current Practices, retrieved 2026-10-18).
If your services are not built on CXF, you do not need to patch anything for this CVE, but the review below is still worth doing. A related Keycloak CVE about audience handling is covered in our post on CVE-2026-93999 and the disabled audience client.
Why does the JWT audience claim matter when the signature is valid?
A valid signature tells your API that the realm’s private key signed the token, but it does not say which API the token was issued for. In a realm with several clients and APIs, many different tokens carry that same valid signature, including a token a user obtained for a low-privilege internal tool. Without an audience check, your payments API would accept that token, because every property it verifies is true of it.
RFC 9068, the IETF standard for JWT access tokens, states the rule for resource servers directly: the server “MUST validate that the ‘aud’ claim contains a resource indicator value corresponding to an identifier the resource server expects for itself”, and must reject the token otherwise (IETF, RFC 9068, JSON Web Token Profile for OAuth 2.0 Access Tokens, October 2021, retrieved 2026-10-18). The same section requires the iss claim to match the authorization server’s issuer exactly and the current time to be before exp, so those are the checks to carry out together.
The Keycloak documentation describes the same attack with a concrete example. A frontend client sends a token to service1, and service1 reuses it against service2. If service2 checks the audience, it sees that the token was intended only for service1 and refuses it (Keycloak, Server Administration Guide, Audience support, retrieved 2026-10-18). The guide recommends limiting the audience on tokens and configuring services to verify it, particularly in environments with low trust between services.
How do you get the right aud claim into Keycloak tokens?
Keycloak adds an API to aud automatically only when that API’s client has client roles in the token, so you need to check the audience on the issuing side, and often complete it, before the resource server can rely on it. The Server Administration Guide describes two mechanisms. The first is the Audience Resolve protocol mapper, which sits in the default roles client scope and adds the client ID of every client that has at least one client role available for the current token. The second is a hardcoded audience, which is a protocol mapper that adds a fixed value regardless of roles (Keycloak, Server Administration Guide, Audience setup, retrieved 2026-10-18).
The roles-based approach is convenient when your API client already defines client roles, but the guide notes that a user with no roles on that client will not get its audience. In that case, or when your service relies on realm roles only, add a hardcoded audience mapper with the client ID of your API. Keep in mind the reverse as well: because clients that leave Full Scope Allowed enabled get every role the user holds in the token, such a token can carry the audiences of other clients whose roles the user holds (tokens from a default realm often show the built-in account client, for example), so check the generated token for audiences you did not intend. The Keycloak guide calls Full Scope Allowed deprecated and recommends disabling it and mapping only the roles each client needs. The guide also states that the frontend client itself is not automatically added to the access token audience, which helps tell access tokens and ID tokens apart.
A short checklist for the issuing side looks like this:
- Create a client for each API (a resource server) and give it a stable client ID.
- Create a client scope per API that carries either the roles or an audience mapper for that API.
- Attach that scope to only the frontend clients that should be able to call the API, as an Optional client scope, so the client receives that audience only when it requests the scope.
- In Clients, open the client, go to its Client scopes tab and use the Evaluate sub-tab to generate an example access token and confirm
audcontains exactly the services you expect.
If you work with MCP servers or other clients that send an RFC 8707 resource parameter, we walk through the same mapper setup in Keycloak MCP Server 401: Fixing the Audience Claim.
What should every resource server check on a Keycloak token?
Every resource server should verify five things in this order: the signature against the realm’s public keys with a pinned algorithm, the iss claim against your exact realm URL, the aud claim against the identifier of that API, the exp time (and nbf if the token carries one), and then whatever authorization claims (roles or scopes) the request needs. Each check rejects a different kind of unacceptable token, and none of them can stand in for another. Our guide on how to verify a Keycloak-issued access token on the backend explains the signature and JWKS steps in detail, and the earlier post on CVE-2026-5430 in WSO2 covers the algorithm pinning side.
| Check | Claim or header | What it stops |
|---|---|---|
| Signature and algorithm | alg, kid, JWKS |
Forged or tampered tokens |
| Issuer | iss |
Tokens from another realm or server |
| Audience | aud |
Tokens issued for a different API |
| Time window | exp, nbf if present |
Expired or not yet valid tokens |
| Authorization | roles, scope |
Valid tokens without the right permission (answer 403) |
When a token fails any of these, return a 401 with a WWW-Authenticate: Bearer error="invalid_token" header and no detail about which claim failed, and log the reason server side without the raw token. A token that passes every check but lacks the permission for the request is a different case, and RFC 6750 says to answer it with 403 and error="insufficient_scope" (IETF, RFC 6750, The OAuth 2.0 Authorization Framework: Bearer Token Usage, October 2012, section 3.1, retrieved 2026-10-18).
How do you enforce audience in Java, Quarkus and Spring?
The common thread is to use the audience validation your framework already provides and to make it mandatory, not optional. In Spring Security you add a claim validator next to the default issuer and time validators, and in Quarkus you set a configuration property. The Quarkus OIDC guide says to consider using quarkus.oidc.token.audience to verify the aud claim, and the Quarkus JWT guide documents mp.jwt.verify.audiences as a comma-separated list of audiences a token may contain (Quarkus, OpenID Connect Bearer token authentication, and Quarkus, Using JWT RBAC, both retrieved 2026-10-18).
For Quarkus with the OIDC extension, the configuration is two lines:
quarkus.oidc.auth-server-url=https://auth.example.com/realms/acme
quarkus.oidc.token.audience=orders-api
In recent Spring Boot versions, the one-line option is the spring.security.oauth2.resourceserver.jwt.audiences property, which sits next to issuer-uri and validates aud (Spring Security reference, OAuth 2.0 Resource Server JWT, retrieved 2026-10-18). If you build the decoder yourself, the one below pins the signing algorithm, validates the issuer and expiry through the default validators, and rejects any token whose aud does not contain this API’s identifier:
import java.util.List;
import org.springframework.context.annotation.Bean;
import org.springframework.security.oauth2.core.DelegatingOAuth2TokenValidator;
import org.springframework.security.oauth2.core.OAuth2TokenValidator;
import org.springframework.security.oauth2.jose.jws.SignatureAlgorithm;
import org.springframework.security.oauth2.jwt.*;
@Bean
JwtDecoder jwtDecoder() {
String issuer = "https://auth.example.com/realms/acme";
NimbusJwtDecoder decoder = NimbusJwtDecoder
.withJwkSetUri(issuer + "/protocol/openid-connect/certs")
.jwsAlgorithm(SignatureAlgorithm.RS256)
.build();
OAuth2TokenValidator<Jwt> audience = new JwtClaimValidator<List<String>>(
JwtClaimNames.AUD, aud -> aud != null && aud.contains("orders-api"));
decoder.setJwtValidator(new DelegatingOAuth2TokenValidator<>(
JwtValidators.createDefaultWithIssuer(issuer), audience));
return decoder;
}
If you are on CXF today, upgrade first, then add a test that sends a correctly signed token with a different audience and confirms the API answers 401. Because the test exercises the behavior your API depends on, it keeps working as a regression check even if you change libraries. For a wider checklist on token handling, see JWT best practices for developers and API authentication best practices.
What does managed Keycloak change, and what stays with your team?
A managed Keycloak service runs the identity provider for you. Skycloak runs real upstream Keycloak, tracks Keycloak releases and security advisories, and applies updates and CVE patches so your clusters stay current, with an uptime SLA of 99.9% on Business and up to 99.99% on Enterprise (Skycloak hosting). That removes the work of patching the identity provider itself, which is the part of the problem a hosting provider can fairly own.
What stays with your team is everything that decides which tokens your APIs accept. You configure the realm’s clients, signing keys and audience mappers, and your services decide whether they validate aud, which identifier they expect for themselves, and how they respond to failures. That is application logic, so neither a managed provider nor the identity provider can enforce it from outside, and it deserves a short, tested validation path in each service and a review whenever a library advisory like this one is published.
Frequently Asked Questions
Which Apache CXF versions fix CVE-2026-50627?
Upgrade to 3.6.12, 4.1.7 or 4.2.2, or any later release on those lines. There is no fixed 4.0.x release, so 4.0.x users move to 4.1.7 or later, and versions older than 3.6 move to 3.6.12 or later. The CXF download page currently lists 3.6.13, 4.1.9 and 4.2.4. The vulnerable component is org.apache.cxf:cxf-rt-rs-security-oauth2, so check that artifact’s version in your build, including transitive dependencies (Apache CXF security advisories, 2026).
Is Keycloak vulnerable to CVE-2026-50627?
Keycloak is not named in the advisory, which concerns the CXF validator class in the OAuth2 security module. The risk is in Java services that use that class to check tokens from your realm. Keycloak behaves as designed by issuing correctly signed tokens, so the missing check has to be fixed in the receiving service.
Does Keycloak put my API in the aud claim by default?
Only if the API client has client roles in the token. The Audience Resolve mapper in the default roles scope adds every client whose roles appear in the token, while realm-role-only services need a hardcoded audience mapper. The requesting client is not added automatically, and default tokens often carry audiences you did not intend (Keycloak Server Administration Guide, 2026).
Is checking azp enough instead of aud?
Not as a replacement. The azp claim names the client the token was issued to, which is the caller, while aud names the intended recipient. Your API needs to confirm it is the recipient. Some setups check azp when aud is missing, but that confirms the caller, not the recipient, and the standards require the audience check for resource servers, but the standards require the audience check for resource servers (IETF RFC 9068, 2021).
What is the difference between iss and aud validation?
The iss check confirms which authorization server issued the token, so a token from another realm or server is rejected. The aud check confirms which API the token is meant for, so a token issued for a different API in the same realm is rejected. You need both, because a valid issuer says nothing about the recipient.
What to do next
If any service uses Apache CXF’s OAuth2 JAX-RS module, move it to 3.6.12, 4.1.7, 4.2.2 or later, and then add the wrong-audience test described above. For services on any other framework, generate an access token in the Keycloak client scope evaluator, confirm aud lists only the APIs the client should reach, and verify that each API rejects a token carrying a different audience. Then confirm that each service also pins its signing algorithm, checks iss against the exact realm URL, and returns 401 for invalid tokens and 403 for valid tokens without permission.
Sources
- Apache CXF, “Security Advisories”, CVE-2026-50627: Apache CXF: OAuth2: Missing JWT Audience and Issuer Validation in Access Token Validator, retrieved 2026-10-18, https://cxf.apache.org/security-advisories.html
- Apache CXF, advisory text for CVE-2026-50627, retrieved 2026-10-18, https://cxf.apache.org/security-advisories.data/CVE-2026-50627.txt
- CVE Program, CVE-2026-50627 record, published 2026-06-12, retrieved 2026-10-18, https://www.cve.org/CVERecord?id=CVE-2026-50627
- Apache CXF, “Download”, retrieved 2026-10-18, https://cxf.apache.org/download.html
- IETF, RFC 8725, “JSON Web Token Best Current Practices”, February 2020, section 3.9, retrieved 2026-10-18, https://www.rfc-editor.org/rfc/rfc8725.html
- IETF, RFC 6750, “The OAuth 2.0 Authorization Framework: Bearer Token Usage”, October 2012, section 3.1, retrieved 2026-10-18, https://www.rfc-editor.org/rfc/rfc6750.html
- NVD, CVE-2026-50627 detail, retrieved 2026-10-18, https://nvd.nist.gov/vuln/detail/CVE-2026-50627
- Spring Security, “OAuth 2.0 Resource Server JWT”, retrieved 2026-10-18, https://docs.spring.io/spring-security/reference/servlet/oauth2/resource-server/jwt.html
- IETF, RFC 9068, “JSON Web Token (JWT) Profile for OAuth 2.0 Access Tokens”, October 2021, section 4, retrieved 2026-10-18, https://www.rfc-editor.org/rfc/rfc9068.html
- Keycloak, “Server Administration Guide”, sections “Audience support” and “Limit token audience”, retrieved 2026-10-18, https://www.keycloak.org/docs/latest/server_admin/index.html
- Quarkus, “OpenID Connect (OIDC) Bearer token authentication”, retrieved 2026-10-18, https://quarkus.io/guides/security-oidc-bearer-token-authentication
- Quarkus, “Using JWT RBAC”, retrieved 2026-10-18, https://quarkus.io/guides/security-jwt