Skip to content
Apache Kafka kf security 4 min read

SASL Authentication

SASL (Simple Authentication and Security Layer) is Kafka’s pluggable framework for proving who is connecting. Unlike mTLS, which derives identity from a certificate, SASL negotiates a credential — a password, a Kerberos ticket, or an OAuth token — during the connection handshake and resolves it to a Kafka principal such as User:orders-service. Choosing the right mechanism and pairing it with TLS is the difference between a hardened cluster and one that leaks credentials on the wire. This page compares the four mechanisms and then walks through a complete SASL_SSL + SCRAM setup, end to end.

Comparing the mechanisms

Kafka supports four SASL mechanisms out of the box. They differ in how the credential is stored, whether the secret crosses the network, and what infrastructure they assume.

MechanismCredentialSecret on the wire?Best for
PLAINUsername + passwordYes (clear text inside the handshake)Quick setups, only over TLS
SCRAM-SHA-256Salted, hashed passwordNo (challenge–response)Password auth, good security
SCRAM-SHA-512Salted, hashed passwordNo (challenge–response)Recommended password default
GSSAPIKerberos service ticketNoEnterprises with existing Kerberos/AD
OAUTHBEAREROAuth 2.0 / OIDC bearer tokenToken (short-lived)Cloud-native, federated identity

PLAIN is the simplest: the client sends a username and password, and the broker validates them against its JAAS config or a custom callback. The password travels in clear text inside the SASL exchange, so it is only acceptable over an encrypted listener.

SCRAM (Salted Challenge Response Authentication Mechanism) is the recommended password-based option. Credentials are stored salted and iterated in the cluster metadata, and the password itself never crosses the network — the client proves knowledge of it via a challenge–response. SCRAM-SHA-512 is the stronger of the two variants and is the sensible default.

GSSAPI integrates with Kerberos, so principals come from your existing KDC or Active Directory. It avoids per-service passwords entirely but requires keytabs, a krb5.conf, and clock synchronisation across the estate.

OAUTHBEARER delegates authentication to an OAuth 2.0 / OIDC provider. Clients present a short-lived bearer token obtained via the client-credentials grant, which suits ephemeral workloads and centralised identity.

SASL/PLAIN sends the password in clear text. Never combine PLAIN with SASL_PLAINTEXT outside an isolated network — always use SASL_SSL. SCRAM is safer because the secret is never transmitted, so prefer it whenever you control the clients.

Configuring SASL_SSL with SCRAM

SCRAM over TLS is the most common production password setup. There are three moving parts: the broker listener and JAAS config, the SCRAM credentials stored in the cluster, and the client configuration.

Broker configuration

Expose a SASL_SSL listener, enable the SCRAM mechanism, and point the broker at its keystore. In KRaft mode the broker authenticates inter-broker and controller traffic too, so give it its own SCRAM identity.

listeners=BROKER://:9094,EXTERNAL://:9093
listener.security.protocol.map=BROKER:SASL_SSL,EXTERNAL:SASL_SSL
inter.broker.listener.name=BROKER

sasl.enabled.mechanisms=SCRAM-SHA-512
sasl.mechanism.inter.broker.protocol=SCRAM-SHA-512

ssl.keystore.location=/etc/kafka/secrets/broker.keystore.jks
ssl.keystore.password=${KEYSTORE_PASSWORD}
ssl.key.password=${KEY_PASSWORD}

listener.name.broker.scram-sha-512.sasl.jaas.config=\
  org.apache.kafka.common.security.scram.ScramLoginModule required \
  username="kafka-broker" \
  password="${BROKER_SCRAM_PASSWORD}";

The listener-prefixed listener.name.<name>.<mechanism>.sasl.jaas.config form keeps the JAAS configuration inline in server.properties, which is cleaner than the older KAFKA_OPTS=-Djava.security.auth.login.config=... external file.

Creating SCRAM credentials

SCRAM credentials live in the cluster metadata, not in a flat file. Create them with kafka-configs.sh. The broker’s own credential is a chicken-and-egg problem in a brand-new cluster, so add it during bootstrap with kafka-storage.sh format --add-scram, then add application users once the cluster is up.

kafka-configs.sh --bootstrap-server broker:9093 \
  --command-config admin.properties \
  --alter --add-config \
  'SCRAM-SHA-512=[password=orders-secret]' \
  --entity-type users --entity-name orders-service

Output:

Completed updating config for user orders-service.

You can confirm the credential exists (the password is never shown, only the salted metadata):

kafka-configs.sh --bootstrap-server broker:9093 \
  --command-config admin.properties \
  --describe --entity-type users --entity-name orders-service

Output:

SCRAM credential configs for user-principal 'orders-service' are SCRAM-SHA-512=salt=...,iterations=4096

Client configuration

A plain kafka-clients producer or consumer needs the security protocol, the mechanism, the JAAS login module with the username and password, and a truststore that trusts the broker’s CA.

security.protocol=SASL_SSL
sasl.mechanism=SCRAM-SHA-512
sasl.jaas.config=org.apache.kafka.common.security.scram.ScramLoginModule required \
  username="orders-service" \
  password="${ORDERS_SECRET}";
ssl.truststore.location=/etc/kafka/secrets/truststore.jks
ssl.truststore.password=${TRUSTSTORE_PASSWORD}

Loaded into a Java client, the same keys apply through ProducerConfig/CommonClientConfigs:

var props = new Properties();
props.put(CommonClientConfigs.BOOTSTRAP_SERVERS_CONFIG, "broker:9093");
props.put(CommonClientConfigs.SECURITY_PROTOCOL_CONFIG, "SASL_SSL");
props.put(SaslConfigs.SASL_MECHANISM, "SCRAM-SHA-512");
props.put(SaslConfigs.SASL_JAAS_CONFIG,
    "org.apache.kafka.common.security.scram.ScramLoginModule required "
        + "username=\"orders-service\" password=\"" + System.getenv("ORDERS_SECRET") + "\";");
props.put(SslConfigs.SSL_TRUSTSTORE_LOCATION_CONFIG, "/etc/kafka/secrets/truststore.jks");
props.put(SslConfigs.SSL_TRUSTSTORE_PASSWORD_CONFIG, System.getenv("TRUSTSTORE_PASSWORD"));

try (var producer = new KafkaProducer<String, String>(
        props, new StringSerializer(), new StringSerializer())) {
    producer.send(new ProducerRecord<>("orders", "order-42", "{\"id\":42}"));
}

For the same configuration in a Spring Boot application, see Securing Spring Kafka.

Switching mechanisms

The client config is nearly identical across mechanisms — only sasl.mechanism and the JAAS login module change. PLAIN uses PlainLoginModule, OAUTHBEARER uses OAuthBearerLoginModule plus a callback handler, and GSSAPI references a keytab.

# OAUTHBEARER
sasl.mechanism=OAUTHBEARER
sasl.login.callback.handler.class=\
  org.apache.kafka.common.security.oauthbearer.OAuthBearerLoginCallbackHandler
sasl.jaas.config=org.apache.kafka.common.security.oauthbearer.OAuthBearerLoginModule required \
  clientId="orders-service" \
  clientSecret="${OAUTH_CLIENT_SECRET}";
sasl.oauthbearer.token.endpoint.url=https://idp.example.com/oauth2/token

Best Practices

  • Default to SCRAM-SHA-512 over SASL_SSL; the password never leaves the client and TLS protects the rest of the exchange.
  • Never run PLAIN or any mechanism on a SASL_PLAINTEXT listener exposed beyond a trusted network — pair SASL with TLS.
  • Give each application its own SCRAM user and a dedicated principal so ACLs and audit logs stay meaningful (see authorization with ACLs).
  • Inject JAAS passwords and keystore secrets from environment variables or a secret manager — keep them out of server.properties committed to source control.
  • Rotate SCRAM credentials on a schedule by adding the new password before retiring the old one to avoid downtime.
  • For cloud-native fleets, prefer OAUTHBEARER with short-lived tokens over long-lived static passwords.
Last updated June 1, 2026
Was this helpful?