use of org.apache.kafka.common.security.oauthbearer.OAuthBearerToken in project kafka by apache.
the class OAuthBearerSaslServer method process.
private byte[] process(String tokenValue, String authorizationId, SaslExtensions extensions) throws SaslException {
OAuthBearerValidatorCallback callback = new OAuthBearerValidatorCallback(tokenValue);
try {
callbackHandler.handle(new Callback[] { callback });
} catch (IOException | UnsupportedCallbackException e) {
handleCallbackError(e);
}
OAuthBearerToken token = callback.token();
if (token == null) {
errorMessage = jsonErrorResponse(callback.errorStatus(), callback.errorScope(), callback.errorOpenIDConfiguration());
log.debug(errorMessage);
return errorMessage.getBytes(StandardCharsets.UTF_8);
}
/*
* We support the client specifying an authorization ID as per the SASL
* specification, but it must match the principal name if it is specified.
*/
if (!authorizationId.isEmpty() && !authorizationId.equals(token.principalName()))
throw new SaslAuthenticationException(String.format("Authentication failed: Client requested an authorization id (%s) that is different from the token's principal name (%s)", authorizationId, token.principalName()));
Map<String, String> validExtensions = processExtensions(token, extensions);
tokenForNegotiatedProperty = token;
this.extensions = new SaslExtensions(validExtensions);
complete = true;
log.debug("Successfully authenticate User={}", token.principalName());
return new byte[0];
}
use of org.apache.kafka.common.security.oauthbearer.OAuthBearerToken in project kafka by apache.
the class LoginAccessTokenValidator method validate.
/**
* Accepts an OAuth JWT access token in base-64 encoded format, validates, and returns an
* OAuthBearerToken.
*
* @param accessToken Non-<code>null</code> JWT access token
* @return {@link OAuthBearerToken}
* @throws ValidateException Thrown on errors performing validation of given token
*/
@SuppressWarnings("unchecked")
public OAuthBearerToken validate(String accessToken) throws ValidateException {
SerializedJwt serializedJwt = new SerializedJwt(accessToken);
Map<String, Object> payload;
try {
payload = OAuthBearerUnsecuredJws.toMap(serializedJwt.getPayload());
} catch (OAuthBearerIllegalTokenException e) {
throw new ValidateException(String.format("Could not validate the access token: %s", e.getMessage()), e);
}
Object scopeRaw = getClaim(payload, scopeClaimName);
Collection<String> scopeRawCollection;
if (scopeRaw instanceof String)
scopeRawCollection = Collections.singletonList((String) scopeRaw);
else if (scopeRaw instanceof Collection)
scopeRawCollection = (Collection<String>) scopeRaw;
else
scopeRawCollection = Collections.emptySet();
Number expirationRaw = (Number) getClaim(payload, EXPIRATION_CLAIM_NAME);
String subRaw = (String) getClaim(payload, subClaimName);
Number issuedAtRaw = (Number) getClaim(payload, ISSUED_AT_CLAIM_NAME);
Set<String> scopes = ClaimValidationUtils.validateScopes(scopeClaimName, scopeRawCollection);
long expiration = ClaimValidationUtils.validateExpiration(EXPIRATION_CLAIM_NAME, expirationRaw != null ? expirationRaw.longValue() * 1000L : null);
String subject = ClaimValidationUtils.validateSubject(subClaimName, subRaw);
Long issuedAt = ClaimValidationUtils.validateIssuedAt(ISSUED_AT_CLAIM_NAME, issuedAtRaw != null ? issuedAtRaw.longValue() * 1000L : null);
OAuthBearerToken token = new BasicOAuthBearerToken(accessToken, scopes, expiration, subject, issuedAt);
return token;
}
use of org.apache.kafka.common.security.oauthbearer.OAuthBearerToken in project kafka by apache.
the class OAuthBearerValidatorCallbackHandler method handleValidatorCallback.
private void handleValidatorCallback(OAuthBearerValidatorCallback callback) {
checkInitialized();
OAuthBearerToken token;
try {
token = accessTokenValidator.validate(callback.tokenValue());
callback.token(token);
} catch (ValidateException e) {
log.warn(e.getMessage(), e);
callback.error("invalid_token", null, null);
}
}
use of org.apache.kafka.common.security.oauthbearer.OAuthBearerToken in project kafka by apache.
the class OAuthBearerRefreshingLogin method configure.
@Override
public void configure(Map<String, ?> configs, String contextName, Configuration configuration, AuthenticateCallbackHandler loginCallbackHandler) {
/*
* Specify this class as the one to synchronize on so that only one OAuth 2
* Bearer Token is refreshed at a given time. Specify null if we don't mind
* multiple simultaneously refreshes. Refreshes happen on the order of minutes
* rather than seconds or milliseconds, and there are typically minutes of
* lifetime remaining when the refresh occurs, so serializing them seems
* reasonable.
*/
Class<OAuthBearerRefreshingLogin> classToSynchronizeOnPriorToRefresh = OAuthBearerRefreshingLogin.class;
expiringCredentialRefreshingLogin = new ExpiringCredentialRefreshingLogin(contextName, configuration, new ExpiringCredentialRefreshConfig(configs, true), loginCallbackHandler, classToSynchronizeOnPriorToRefresh) {
@Override
public ExpiringCredential expiringCredential() {
Set<OAuthBearerToken> privateCredentialTokens = expiringCredentialRefreshingLogin.subject().getPrivateCredentials(OAuthBearerToken.class);
if (privateCredentialTokens.isEmpty())
return null;
final OAuthBearerToken token = privateCredentialTokens.iterator().next();
if (log.isDebugEnabled())
log.debug("Found expiring credential with principal '{}'.", token.principalName());
return new ExpiringCredential() {
@Override
public String principalName() {
return token.principalName();
}
@Override
public Long startTimeMs() {
return token.startTimeMs();
}
@Override
public long expireTimeMs() {
return token.lifetimeMs();
}
@Override
public Long absoluteLastRefreshTimeMs() {
return null;
}
};
}
};
}
use of org.apache.kafka.common.security.oauthbearer.OAuthBearerToken in project kafka by apache.
the class OAuthBearerSaslClientCallbackHandler method handleCallback.
private void handleCallback(OAuthBearerTokenCallback callback) throws IOException {
if (callback.token() != null)
throw new IllegalArgumentException("Callback had a token already");
Subject subject = Subject.getSubject(AccessController.getContext());
Set<OAuthBearerToken> privateCredentials = subject != null ? subject.getPrivateCredentials(OAuthBearerToken.class) : Collections.emptySet();
if (privateCredentials.size() == 0)
throw new IOException("No OAuth Bearer tokens in Subject's private credentials");
if (privateCredentials.size() == 1)
callback.token(privateCredentials.iterator().next());
else {
/*
* There a very small window of time upon token refresh (on the order of milliseconds)
* where both an old and a new token appear on the Subject's private credentials.
* Rather than implement a lock to eliminate this window, we will deal with it by
* checking for the existence of multiple tokens and choosing the one that has the
* longest lifetime. It is also possible that a bug could cause multiple tokens to
* exist (e.g. KAFKA-7902), so dealing with the unlikely possibility that occurs
* during normal operation also allows us to deal more robustly with potential bugs.
*/
SortedSet<OAuthBearerToken> sortedByLifetime = new TreeSet<>(new Comparator<OAuthBearerToken>() {
@Override
public int compare(OAuthBearerToken o1, OAuthBearerToken o2) {
return Long.compare(o1.lifetimeMs(), o2.lifetimeMs());
}
});
sortedByLifetime.addAll(privateCredentials);
log.warn("Found {} OAuth Bearer tokens in Subject's private credentials; the oldest expires at {}, will use the newest, which expires at {}", sortedByLifetime.size(), new Date(sortedByLifetime.first().lifetimeMs()), new Date(sortedByLifetime.last().lifetimeMs()));
callback.token(sortedByLifetime.last());
}
}
Aggregations