All Downloads are FREE. Search and download functionalities are using the official Maven repository.
Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
io.smallrye.jwt.auth.principal.DefaultJWTTokenParser Maven / Gradle / Ivy
/*
* Copyright 2019 Red Hat, Inc, and individual contributors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package io.smallrye.jwt.auth.principal;
import static java.util.Collections.emptyList;
import java.util.HashSet;
import java.util.Set;
import java.util.stream.Collectors;
import org.eclipse.microprofile.jwt.Claims;
import org.jose4j.jwa.AlgorithmConstraints;
import org.jose4j.jwe.JsonWebEncryption;
import org.jose4j.jwt.JwtClaims;
import org.jose4j.jwt.NumericDate;
import org.jose4j.jwt.consumer.InvalidJwtException;
import org.jose4j.jwt.consumer.JwtConsumer;
import org.jose4j.jwt.consumer.JwtConsumerBuilder;
import org.jose4j.jwt.consumer.JwtContext;
import org.jose4j.keys.resolvers.DecryptionKeyResolver;
import org.jose4j.keys.resolvers.VerificationKeyResolver;
import org.jose4j.lang.JoseException;
import org.jose4j.lang.UnresolvableKeyException;
import io.smallrye.jwt.KeyProvider;
import io.smallrye.jwt.algorithm.KeyEncryptionAlgorithm;
/**
* Default JWT token validator
*
*/
public class DefaultJWTTokenParser {
private volatile VerificationKeyResolver keyResolver;
private volatile DecryptionKeyResolver decryptionKeyResolver;
public JwtContext parse(final String token, final JWTAuthContextInfo authContextInfo) throws ParseException {
String tokenSequence = token;
ProtectionLevel level = getProtectionLevel(authContextInfo);
if (level == ProtectionLevel.SIGN_ENCRYPT) {
tokenSequence = decryptSignedToken(tokenSequence, authContextInfo);
level = ProtectionLevel.SIGN;
}
return parseClaims(tokenSequence, authContextInfo, level);
}
private String decryptSignedToken(String token, JWTAuthContextInfo authContextInfo) throws ParseException {
try {
JsonWebEncryption jwe = new JsonWebEncryption();
jwe.setAlgorithmConstraints(
new AlgorithmConstraints(AlgorithmConstraints.ConstraintType.PERMIT,
encryptionAlgorithms(authContextInfo)));
if (authContextInfo.getPrivateDecryptionKey() != null) {
jwe.setKey(authContextInfo.getPrivateDecryptionKey());
} else if (authContextInfo.getSecretDecryptionKey() != null) {
jwe.setKey(authContextInfo.getSecretDecryptionKey());
} else {
jwe.setKey(getDecryptionKeyResolver(authContextInfo).resolveKey(jwe, null));
}
jwe.setCompactSerialization(token);
if (!"JWT".equals(jwe.getContentTypeHeaderValue())) {
PrincipalLogging.log.encryptedTokenMissingContentType();
throw PrincipalMessages.msg.encryptedTokenMissingContentType();
}
return jwe.getPlaintextString();
} catch (UnresolvableKeyException e) {
PrincipalLogging.log.decryptionKeyUnresolvable();
throw PrincipalMessages.msg.decryptionKeyUnresolvable(e);
} catch (JoseException e) {
PrincipalLogging.log.encryptedTokenSequenceInvalid();
throw PrincipalMessages.msg.encryptedTokenSequenceInvalid(e);
}
}
private String[] encryptionAlgorithms(JWTAuthContextInfo authContextInfo) {
Set algorithms = new HashSet<>();
for (KeyEncryptionAlgorithm keyEncAlgo : authContextInfo.getKeyEncryptionAlgorithm()) {
algorithms.add(keyEncAlgo.getAlgorithm());
}
return algorithms.toArray(new String[] {});
}
private JwtContext parseClaims(String token, JWTAuthContextInfo authContextInfo, ProtectionLevel level)
throws ParseException {
try {
JwtConsumerBuilder builder = new JwtConsumerBuilder();
if (level == ProtectionLevel.SIGN) {
if (authContextInfo.getPublicVerificationKey() != null) {
builder.setVerificationKey(authContextInfo.getPublicVerificationKey());
} else if (authContextInfo.getSecretVerificationKey() != null) {
builder.setVerificationKey(authContextInfo.getSecretVerificationKey());
} else {
builder.setVerificationKeyResolver(getVerificationKeyResolver(authContextInfo));
}
builder.setJwsAlgorithmConstraints(
new AlgorithmConstraints(AlgorithmConstraints.ConstraintType.PERMIT,
authContextInfo.getSignatureAlgorithm().getAlgorithm()));
} else {
builder.setEnableRequireEncryption();
builder.setDisableRequireSignature();
if (authContextInfo.getPrivateDecryptionKey() != null) {
builder.setDecryptionKey(authContextInfo.getPrivateDecryptionKey());
} else if (authContextInfo.getSecretDecryptionKey() != null) {
builder.setDecryptionKey(authContextInfo.getSecretDecryptionKey());
} else {
builder.setDecryptionKeyResolver(getDecryptionKeyResolver(authContextInfo));
}
builder.setJweAlgorithmConstraints(
new AlgorithmConstraints(AlgorithmConstraints.ConstraintType.PERMIT,
encryptionAlgorithms(authContextInfo)));
}
builder.setRequireExpirationTime();
final boolean issuedAtRequired = authContextInfo.getMaxTimeToLiveSecs() == null
|| authContextInfo.getMaxTimeToLiveSecs() > 0 || authContextInfo.getTokenAge() != null;
if (issuedAtRequired) {
builder.setRequireIssuedAt();
}
if (authContextInfo.getIssuedBy() != null) {
builder.setExpectedIssuer(authContextInfo.getIssuedBy());
}
if (authContextInfo.getExpGracePeriodSecs() > 0) {
builder.setAllowedClockSkewInSeconds(authContextInfo.getExpGracePeriodSecs());
} else if (authContextInfo.getClockSkew() > 0) {
builder.setAllowedClockSkewInSeconds(authContextInfo.getClockSkew());
}
setExpectedAudience(builder, authContextInfo);
if (authContextInfo.isRelaxVerificationKeyValidation()) {
builder.setRelaxVerificationKeyValidation();
}
JwtConsumer jwtConsumer = builder.build();
// Validate the JWT and process it to the Claims
JwtContext jwtContext = jwtConsumer.process(token);
JwtClaims claimsSet = jwtContext.getJwtClaims();
if (issuedAtRequired) {
verifyIatAndExpAndTimeToLive(authContextInfo, claimsSet);
}
verifyRequiredClaims(authContextInfo, jwtContext);
PrincipalUtils.setClaims(claimsSet, token, authContextInfo);
if (authContextInfo.isRequireNamedPrincipal()) {
checkNameClaims(jwtContext);
}
return jwtContext;
} catch (InvalidJwtException e) {
if (e.getCause() instanceof UnresolvableKeyException) {
PrincipalLogging.log.verificationKeyUnresolvable();
throw PrincipalMessages.msg.failedToVerifyToken(e.getCause());
} else {
PrincipalLogging.log.tokenInvalid();
throw PrincipalMessages.msg.failedToVerifyToken(e);
}
} catch (UnresolvableKeyException e) {
PrincipalLogging.log.verificationKeyUnresolvable();
throw PrincipalMessages.msg.failedToVerifyToken(e);
}
}
void setExpectedAudience(JwtConsumerBuilder builder, JWTAuthContextInfo authContextInfo) {
final Set expectedAudience = authContextInfo.getExpectedAudience();
if (expectedAudience != null) {
builder.setExpectedAudience(expectedAudience.toArray(new String[0]));
} else {
builder.setSkipDefaultAudienceValidation();
}
}
private void checkNameClaims(JwtContext jwtContext) throws InvalidJwtException {
JwtClaims claimsSet = jwtContext.getJwtClaims();
final boolean hasPrincipalClaim = claimsSet.getClaimValue(Claims.sub.name()) != null ||
claimsSet.getClaimValue(Claims.upn.name()) != null ||
claimsSet.getClaimValue(Claims.preferred_username.name()) != null;
if (!hasPrincipalClaim) {
throw PrincipalMessages.msg.claimNotFound(s -> new InvalidJwtException(s, emptyList(), jwtContext));
}
}
private void verifyIatAndExpAndTimeToLive(JWTAuthContextInfo authContextInfo, JwtClaims claimsSet) throws ParseException {
NumericDate iat;
NumericDate exp;
try {
iat = claimsSet.getIssuedAt();
exp = claimsSet.getExpirationTime();
} catch (Exception ex) {
throw PrincipalMessages.msg.invalidIatExp();
}
if (iat.getValue() > exp.getValue()) {
throw PrincipalMessages.msg.failedToVerifyIatExp(exp, iat);
}
final Long maxTimeToLiveSecs = authContextInfo.getMaxTimeToLiveSecs();
if (maxTimeToLiveSecs != null) {
if (exp.getValue() - iat.getValue() > maxTimeToLiveSecs) {
throw PrincipalMessages.msg.expExceeded(exp, maxTimeToLiveSecs, iat);
}
}
final Long tokenAge = authContextInfo.getTokenAge();
if (tokenAge != null) {
long now = System.currentTimeMillis() / 1000;
if (now - iat.getValue() > tokenAge) {
throw PrincipalMessages.msg.tokenAgeExceeded(tokenAge);
}
}
}
private void verifyRequiredClaims(JWTAuthContextInfo authContextInfo, JwtContext jwtContext) throws InvalidJwtException {
final Set requiredClaims = authContextInfo.getRequiredClaims();
if (requiredClaims != null) {
if (!jwtContext.getJwtClaims().getClaimsMap().keySet().containsAll(requiredClaims)) {
if (PrincipalLogging.log.isDebugEnabled()) {
final String missingClaims = requiredClaims.stream()
.filter(claim -> !jwtContext.getJwtClaims().getClaimsMap().containsKey(claim))
.collect(Collectors.joining(","));
PrincipalLogging.log.missingClaims(missingClaims);
}
throw PrincipalMessages.msg.missingClaims(s -> new InvalidJwtException(s, emptyList(), jwtContext));
}
}
}
protected VerificationKeyResolver getVerificationKeyResolver(JWTAuthContextInfo authContextInfo)
throws UnresolvableKeyException {
if (keyResolver == null) {
synchronized (this) {
if (keyResolver == null)
if (KeyProvider.AWS_ALB == authContextInfo.getKeyProvider()) {
keyResolver = new AwsAlbKeyResolver(authContextInfo);
} else {
keyResolver = authContextInfo.isVerifyCertificateThumbprint()
? new X509KeyLocationResolver(authContextInfo)
: new KeyLocationResolver(authContextInfo);
}
}
}
return keyResolver;
}
protected DecryptionKeyResolver getDecryptionKeyResolver(JWTAuthContextInfo authContextInfo)
throws UnresolvableKeyException {
if (decryptionKeyResolver == null) {
synchronized (this) {
if (decryptionKeyResolver == null)
decryptionKeyResolver = new DecryptionKeyLocationResolver(authContextInfo);
}
}
return decryptionKeyResolver;
}
protected ProtectionLevel getProtectionLevel(JWTAuthContextInfo authContextInfo) {
if (authContextInfo.getDecryptionKeyLocation() != null
|| authContextInfo.getDecryptionKeyContent() != null
|| authContextInfo.getPrivateDecryptionKey() != null
|| authContextInfo.getSecretDecryptionKey() != null) {
boolean sign = authContextInfo.getPublicVerificationKey() != null
|| authContextInfo.getSecretVerificationKey() != null
|| authContextInfo.getPublicKeyContent() != null
|| authContextInfo.getPublicKeyLocation() != null;
return sign ? ProtectionLevel.SIGN_ENCRYPT : ProtectionLevel.ENCRYPT;
} else {
return ProtectionLevel.SIGN;
}
}
protected enum ProtectionLevel {
SIGN,
ENCRYPT,
SIGN_ENCRYPT
}
}