Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

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/nbf with 60 s skew, exact iss, optional aud pinning), and principal extraction (userNameClaim, default sub, with fallbackUserNameClaim).
  • Signature verification uses ring with an alg allowlist (RS256/RS384/RS512/ES256); none and 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 every jwksRefreshSeconds (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 SaslHandshake response lists OAUTHBEARER alone, 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).