KIP-255 — SASL/OAUTHBEARER
Status: implemented — see the KIP index.
What the KIP changes in Apache Kafka
KIP-255 (Kafka 2.0) added the OAUTHBEARER SASL mechanism (RFC 7628):
clients authenticate by presenting an OAuth 2 bearer token instead of a
password, with the token's claims carrying the principal. Apache ships
an unsecured JWT implementation by default and leaves production
validation to pluggable callback handlers — in practice everyone runs
Strimzi's kafka-oauth handlers, which validate real JWTs against an
OIDC issuer's JWKS.
How kaas implements it
kaas skips the unsecured default and implements the production shape
directly: a listener with authentication.type: oauth validates real
JWTs against the configured issuer — the equivalent of Strimzi's
oauth.valid.issuer.uri + oauth.jwks.endpoint.uri fast-local-JWKS
path, with the config field names mirroring Strimzi's
KafkaListenerAuthenticationOAuth 1:1.
crates/kaas-auth/src/oauth.rs— RFC 7628 message parsing (the%x01-framed initial response, gs2 authzid check, the two-step JSON-challenge failure path), JWT validation (signature via the issuer's JWKS,exp/nbfwith 60 s skew, exactiss, optionalaudpinning), and principal extraction (userNameClaim, defaultsub, withfallbackUserNameClaim).- Signature verification uses
ringwith analgallowlist (RS256/RS384/RS512/ES256);noneand the HMAC family are rejected outright (algorithm-confusion defence). - The JWKS is hot-swapped: a fetch loop in
bins/kaas/src/main.rs(spawn_jwks_refreshers) re-pulls everyjwksRefreshSeconds(default 300, Strimzi's default) and early on unknown-kid. Until the first successful fetch every token is rejected — fail closed. - OAUTHBEARER is refused over plaintext connections, like SASL PLAIN: a bearer token is a reusable credential.
- Mechanism advertisement is per listener: an oauth listener's
SaslHandshakeresponse listsOAUTHBEARERalone, never mechanisms the engine would reject at authenticate time.
Deliberate deviation: Apache's default unsecured OAUTHBEARER (base64-JSON "tokens" with no signature) is not implemented at all — there is no mode in which kaas accepts an unsigned token.
How it's verified
Unit tests in crates/kaas-auth/src/oauth.rs cover the RFC 7628
parser edges, a static RS256 vector, ES256 round-trips, expiry/nbf
skew, issuer and audience mismatches, alg-confusion rejections
(alg_none_and_hs256_rejected), unknown-kid refresh hints, and the
two-step failure exchange. bins/kaas/tests/oauth_smoke.rs drives the
full stack over TLS: pre-auth gate → handshake → OAUTHBEARER with an
ES256 JWT validated against a wiremock-served JWKS → Produce
unblocked, plus the challenge-then-58 failure path. Live validation:
the canary producer/consumer pair authenticating via EntraID workload
identity (the same pair that runs against Strimzi's OAuth listener).