apache/pulsar · critical · IOException
No secret key was provided for token authentication
Error message
No secret key was provided for token authentication
What it means
During provider initialization, getValidationKey looks for a token validation key in the configuration: first tokenSecretKey (symmetric), then tokenPublicKey (asymmetric). If neither property is set (blank), it throws this IOException because the provider has no key with which to verify token signatures.
Solutions
- Set tokenSecretKey in broker.conf/standalone.conf to the base64-encoded secret key (or data: URL) used to sign tokens, e.g. tokenSecretKey=data:;base64,<key>.
- For asymmetric setups, set tokenPublicKey (and tokenPublicKeyAlg for non-RS256 algorithms) instead.
- Verify the property names are spelled correctly and the values are non-blank after config loading.
- Generate a key if none exists: bin/pulsar tokens create-secret-key --output my-secret.key --base64.
Example fix
// before (broker.conf) # tokenSecretKey= // after tokenSecretKey=data:;base64,base64EncodedSecretKeyHere
Defensive patterns
Strategy: validation
Validate before calling
// Fail fast at deploy time before starting the broker:
String secretKey = (String) conf.getProperty("tokenSecretKey");
String publicKey = (String) conf.getProperty("tokenPublicKey");
if ((secretKey == null || secretKey.isBlank()) && (publicKey == null || publicKey.isBlank())) {
throw new IllegalStateException(
"Token auth requires either tokenSecretKey or tokenPublicKey in the configuration");
} Try / catch
try {
authenticationProvider.initialize(conf);
} catch (IOException e) {
if (e.getMessage().contains("No secret key was provided")) {
throw new IllegalStateException("Set tokenSecretKey or tokenPublicKey in broker.conf before enabling token auth", e);
}
throw e;
} Prevention
- Generate and provision the key (bin/pulsar tokens create-secret-key) as part of deployment before enabling token auth.
- Keep tokenSecretKey/tokenPublicKey in a secrets store and template them into the conf, verifying the value is non-empty after substitution.
- Validate broker.conf in CI with a config lint that requires the key when the token auth provider is selected.
- Document that exactly one of tokenSecretKey / tokenPublicKey must be set.
When it happens
Trigger: The broker/client configuration sets authenticationProvider=org.apache.pulsar.broker.authentication.AuthenticationProviderToken (or the client sets the token auth provider) but neither tokenSecretKey nor tokenPublicKey is provided at initialize time.
Common situations: Enabling token authentication without supplying the secret key file/URL; key property name misspelled in conf; key file path exists but the property value is empty after env-var substitution; copying a config template that left the key fields blank.
Understand the failure class
- Authentication and authorization failures — expired tokens, bad credentials, and missing scopes.
Related errors
- invalid algorithm provided
- Invalid broker configuration. Authentication must be…
- Invalid proxy configuration. Authentication must be enabled…
- No athenz domain name specified
- A v5 authentication plugin
AI-assisted analysis of apache/pulsar@820761864e (2026-09-06).
Data as JSON: /api/errors/e12322d8c75e89b0.
Report an issue: GitHub.
Appendix: source
Thrown at pulsar-broker-common/src/main/java/org/apache/pulsar/broker/authentication/AuthenticationProviderToken.java:300
Optional<String> firstEntry = list.stream().findFirst().map(Object::toString);
return firstEntry.orElse(null);
}
}
/**
* Try to get the validation key for tokens from several possible config options.
*/
private Key getValidationKey(ServiceConfiguration conf) throws IOException {
String tokenSecretKey = (String) conf.getProperty(confTokenSecretKeySettingName);
String tokenPublicKey = (String) conf.getProperty(confTokenPublicKeySettingName);
if (StringUtils.isNotBlank(tokenSecretKey)) {
final byte[] validationKey = AuthTokenUtils.readKeyFromUrl(tokenSecretKey);
return AuthTokenUtils.decodeSecretKey(validationKey);
} else if (StringUtils.isNotBlank(tokenPublicKey)) {
final byte[] validationKey = AuthTokenUtils.readKeyFromUrl(tokenPublicKey);
return AuthTokenUtils.decodePublicKey(validationKey, publicKeyAlg);
} else {
throw new IOException("No secret key was provided for token authentication");
}
}
private String getTokenRoleClaim(ServiceConfiguration conf) throws IOException {
String tokenAuthClaim = (String) conf.getProperty(confTokenAuthClaimSettingName);
if (StringUtils.isNotBlank(tokenAuthClaim)) {
return tokenAuthClaim;
} else {
return Claims.SUBJECT;
}
}
private SignatureAlgorithm getPublicKeyAlgType(ServiceConfiguration conf) throws IllegalArgumentException {
String tokenPublicAlg = (String) conf.getProperty(confTokenPublicAlgSettingName);
if (StringUtils.isNotBlank(tokenPublicAlg)) {
try {
return SignatureAlgorithm.forName(tokenPublicAlg);
} catch (SignatureException ex) {View on GitHub (pinned to 820761864e)