org.jose4j.jwt.consumer.JwtConsumerBuilder Maven / Gradle / Ivy
Show all versions of jose4j Show documentation
/*
* Copyright 2012-2017 Brian Campbell
*
* 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 org.jose4j.jwt.consumer;
import org.jose4j.jca.ProviderContext;
import org.jose4j.jwa.AlgorithmConstraints;
import org.jose4j.jwt.NumericDate;
import org.jose4j.keys.resolvers.DecryptionKeyResolver;
import org.jose4j.keys.resolvers.VerificationKeyResolver;
import java.security.Key;
import java.util.*;
/**
*
* Use the JwtConsumerBuilder to create the appropriate JwtConsumer for your JWT processing needs.
*
*
* The specific validation requirements for a JWT are context dependent, however,
* it typically advisable to require a (reasonable) expiration time, a trusted issuer, and
* and audience that identifies your system as the intended recipient.
* For example, a {@code JwtConsumer} might be set up and used like this:
*
*
*
* JwtConsumer jwtConsumer = new JwtConsumerBuilder()
.setRequireExpirationTime() // the JWT must have an expiration time
.setMaxFutureValidityInMinutes(300) // but the expiration time can't be too crazy
.setExpectedIssuer("Issuer") // whom the JWT needs to have been issued by
.setExpectedAudience("Audience") // to whom the JWT is intended for
.setVerificationKey(publicKey) // verify the signature with the public key
.build(); // create the JwtConsumer instance
try
{
// Validate the JWT and process it to the Claims
JwtClaims jwtClaims = jwtConsumer.processToClaims(jwt);
System.out.println("JWT validation succeeded! " + jwtClaims);
}
catch (InvalidJwtException e)
{
// InvalidJwtException will be thrown, if the JWT failed processing or validation in anyway.
// Hopefully with meaningful explanations(s) about what went wrong.
System.out.println("Invalid JWT! " + e);
}
*
*
*
*
* JwtConsumer instances created from this are thread safe and reusable (as long as
* any custom Validators or Customizers used are also thread safe).
*
*/
public class JwtConsumerBuilder
{
private VerificationKeyResolver verificationKeyResolver = new SimpleKeyResolver(null);
private DecryptionKeyResolver decryptionKeyResolver = new SimpleKeyResolver(null);
private AlgorithmConstraints jwsAlgorithmConstraints;
private AlgorithmConstraints jweAlgorithmConstraints;
private AlgorithmConstraints jweContentEncryptionAlgorithmConstraints;
private boolean skipDefaultAudienceValidation;
private AudValidator audValidator;
private IssValidator issValidator;
private boolean requireSubject;
private String expectedSubject;
private boolean requireJti;
private NumericDateValidator dateClaimsValidator = new NumericDateValidator();
private List customValidators = new ArrayList<>();
private boolean requireSignature = true;
private boolean requireEncryption;
private boolean skipSignatureVerification = false;
private boolean relaxVerificationKeyValidation;
private boolean skipVerificationKeyResolutionOnNone;
private boolean relaxDecryptionKeyValidation;
private boolean skipAllValidators = false;
private boolean skipAllDefaultValidators = false;
private boolean liberalContentTypeHandling;
private ProviderContext jwsProviderContext;
private ProviderContext jweProviderContext;
private JwsCustomizer jwsCustomizer;
private JweCustomizer jweCustomizer;
/**
* Creates a new JwtConsumerBuilder, which is set up by default to build a JwtConsumer
* that requires a signature and will validate the core JWT claims when they
* are present. The various methods on the builder should be used to customize
* the JwtConsumer's behavior as appropriate.
*/
public JwtConsumerBuilder()
{
super();
}
/**
* Require that the JWT be encrypted, which is not required by default.
* @return the same JwtConsumerBuilder
*/
public JwtConsumerBuilder setEnableRequireEncryption()
{
requireEncryption = true;
return this;
}
/**
* Because integrity protection is needed in most usages of JWT, a signature on the JWT is required by default.
* Calling this turns that requirement off. It may be necessary, for example, when integrity is ensured though
* other means like a JWE using a symmetric key management algorithm.
* @return the same JwtConsumerBuilder
*/
public JwtConsumerBuilder setDisableRequireSignature()
{
requireSignature = false;
return this;
}
/**
*
* According to section 5.2 of the JWT spec,
* when nested signing or encryption is employed with a JWT, the "cty" header parameter has to be present and
* have a value of "JWT" to indicate that a nested JWT is the payload of the outer JWT.
*
*
* Not all JWTs follow that requirement of the spec and this provides a work around for
* consuming non-compliant JWTs.
* Calling this method tells the JwtConsumer to be a bit more liberal in processing and
* make a best effort when the "cty" header isn’t present and the payload doesn't parse as JSON
* but can be parsed into a JOSE object.
*
* @return the same JwtConsumerBuilder
*/
public JwtConsumerBuilder setEnableLiberalContentTypeHandling()
{
liberalContentTypeHandling = true;
return this;
}
/**
*
* Skip signature verification.
*
* This might be useful in cases where you don't have enough
* information to set up a validating JWT consumer without cracking open the JWT first. For example,
* in some contexts you might not know who issued the token without looking at the "iss" claim inside the JWT.
* In such a case two JwtConsumers cab be used in a "two-pass" validation of sorts - the first JwtConsumer parses the JWT but
* doesn't validate the signature or claims due to the use of methods like this one and the second JwtConsumers
* does the actual validation.
*
* @return the same JwtConsumerBuilder
*/
public JwtConsumerBuilder setSkipSignatureVerification()
{
skipSignatureVerification = true;
return this;
}
/**
*
* Skip all claims validation.
*
* This might be useful in cases where you don't have enough
* information to set up a validating JWT consumer without cracking open the JWT first. For example,
* in some contexts you might not know who issued the token without looking at the "iss" claim inside the JWT.
* In such a case two JwtConsumers cab be used in a "two-pass" validation of sorts - the first JwtConsumer parses the JWT but
* doesn't validate the signature or claims due to the use of methods like this one and the second JwtConsumers
* does the actual validation.
*
* @return the same JwtConsumerBuilder
*/
public JwtConsumerBuilder setSkipAllValidators()
{
skipAllValidators = true;
return this;
}
/**
* Skip all the default claim validation but not those provided via {@link #registerValidator(Validator)}.
* @return the same JwtConsumerBuilder
*/
public JwtConsumerBuilder setSkipAllDefaultValidators()
{
skipAllDefaultValidators = true;
return this;
}
/**
* Set the JWS algorithm constraints to be applied when processing the JWT.
* @param constraints the AlgorithmConstraints to use for JWS processing
* @return the same JwtConsumerBuilder
*/
public JwtConsumerBuilder setJwsAlgorithmConstraints(AlgorithmConstraints constraints)
{
jwsAlgorithmConstraints = constraints;
return this;
}
/**
* Set the JWE algorithm constraints to be applied to key management when processing the JWT.
* @param constraints the AlgorithmConstraints to use for JWE key management algorithm processing
* @return the same JwtConsumerBuilder
*/
public JwtConsumerBuilder setJweAlgorithmConstraints(AlgorithmConstraints constraints)
{
jweAlgorithmConstraints = constraints;
return this;
}
/**
* Set the JWE algorithm constraints to be applied to content encryption when processing the JWT.
* @param constraints the AlgorithmConstraints to use for JWE content encryption processing
* @return the same JwtConsumerBuilder
*/
public JwtConsumerBuilder setJweContentEncryptionAlgorithmConstraints(AlgorithmConstraints constraints)
{
jweContentEncryptionAlgorithmConstraints = constraints;
return this;
}
/**
* Set the key to be used for JWS signature/MAC verification.
* @param verificationKey the verification key.
* @return the same JwtConsumerBuilder
*/
public JwtConsumerBuilder setVerificationKey(Key verificationKey)
{
return setVerificationKeyResolver(new SimpleKeyResolver(verificationKey));
}
/**
* Set the VerificationKeyResolver to use to select the key for JWS signature/MAC verification.
* A VerificationKeyResolver enables a verification key to be chosen dynamically based on more
* information, like the JWS headers, about the message being processed.
* @param verificationKeyResolver the VerificationKeyResolver
* @return the same JwtConsumerBuilder
* @see org.jose4j.keys.resolvers.HttpsJwksVerificationKeyResolver
* @see org.jose4j.keys.resolvers.JwksVerificationKeyResolver
* @see org.jose4j.keys.resolvers.X509VerificationKeyResolver
*/
public JwtConsumerBuilder setVerificationKeyResolver(VerificationKeyResolver verificationKeyResolver)
{
this.verificationKeyResolver = verificationKeyResolver;
return this;
}
/**
* Indicates that the JwtConsumer will not call the VerificationKeyResolver for a JWS using the
* 'none' algorithm.
* @return the same JwtConsumerBuilder
*/
public JwtConsumerBuilder setSkipVerificationKeyResolutionOnNone()
{
this.skipVerificationKeyResolutionOnNone = true;
return this;
}
/**
* Set the key to be used for JWE decryption.
* @param decryptionKey the decryption key.
* @return the same JwtConsumerBuilder
*/
public JwtConsumerBuilder setDecryptionKey(Key decryptionKey)
{
return setDecryptionKeyResolver(new SimpleKeyResolver(decryptionKey));
}
/**
* Set the DecryptionKeyResolver to use to select the key for JWE decryption.
* A DecryptionKeyResolver enables a decryption key to be chosen dynamically based on more
* information, like the JWE headers, about the message being processed.
* @param decryptionKeyResolver the VerificationKeyResolver
* @return the same JwtConsumerBuilder
* @see org.jose4j.keys.resolvers.JwksDecryptionKeyResolver
*/
public JwtConsumerBuilder setDecryptionKeyResolver(DecryptionKeyResolver decryptionKeyResolver)
{
this.decryptionKeyResolver = decryptionKeyResolver;
return this;
}
/**
*
* Set the audience value(s) to use when validating the audience ("aud") claim of a JWT
* and require that an audience claim be present.
* Audience validation will succeed, if any one of the provided values is equal to any one
* of the values of the "aud" claim in the JWT.
*
*
* From Section 4.1.3 of RFC 7519:
* The "aud" (audience) claim identifies the recipients that the JWT is
* intended for. Each principal intended to process the JWT MUST
* identify itself with a value in the audience claim. If the principal
* processing the claim does not identify itself with a value in the
* "aud" claim when this claim is present, then the JWT MUST be
* rejected. In the general case, the "aud" value is an array of case-
* sensitive strings, each containing a StringOrURI value. In the
* special case when the JWT has one audience, the "aud" value MAY be a
* single case-sensitive string containing a StringOrURI value. The
* interpretation of audience values is generally application specific.
* Use of this claim is OPTIONAL.
*
* Equivalent to calling {@link #setExpectedAudience(boolean, String...)} with {@code true} as the first argument.
* @param audience the audience value(s) that identify valid recipient(s) of a JWT
* @return the same JwtConsumerBuilder
*/
public JwtConsumerBuilder setExpectedAudience(String... audience)
{
return setExpectedAudience(true, audience);
}
/**
* Set the audience value(s) to use when validating the audience ("aud") claim of a JWT.
* Audience validation will succeed, if any one of the provided values is equal to any one
* of the values of the "aud" claim in the JWT.
*
*
* If present, the audience claim will always be validated (unless explicitly disabled). The {@code requireAudienceClaim} parameter
* can be used to indicate whether or not the presence of the audience claim is required. In most cases
* {@code requireAudienceClaim} should be {@code true}.
*
*
* From Section 4.1.3 of RFC 7519:
* The "aud" (audience) claim identifies the recipients that the JWT is
* intended for. Each principal intended to process the JWT MUST
* identify itself with a value in the audience claim. If the principal
* processing the claim does not identify itself with a value in the
* "aud" claim when this claim is present, then the JWT MUST be
* rejected. In the general case, the "aud" value is an array of case-
* sensitive strings, each containing a StringOrURI value. In the
* special case when the JWT has one audience, the "aud" value MAY be a
* single case-sensitive string containing a StringOrURI value. The
* interpretation of audience values is generally application specific.
* Use of this claim is OPTIONAL.
*
* @param requireAudienceClaim true, if an audience claim has to be present for validation to succeed. false, otherwise
* @param audience the audience value(s) that identify valid recipient(s) of a JWT
* @return the same JwtConsumerBuilder
*/
public JwtConsumerBuilder setExpectedAudience(boolean requireAudienceClaim, String... audience)
{
Set acceptableAudiences = new HashSet<>(Arrays.asList(audience));
audValidator = new AudValidator(acceptableAudiences, requireAudienceClaim);
return this;
}
/**
* Skip the default audience validation.
* @return the same JwtConsumerBuilder
*/
public JwtConsumerBuilder setSkipDefaultAudienceValidation()
{
skipDefaultAudienceValidation = true;
return this;
}
/**
* Indicates whether or not the issuer ("iss") claim is required and optionally what the expected values can be.
* @param requireIssuer true if issuer claim is required, false otherwise
* @param expectedIssuers the values, one of which the issuer claim must match to pass validation, {@code null} means that any value is acceptable
* @return the same JwtConsumerBuilder
*/
public JwtConsumerBuilder setExpectedIssuers(boolean requireIssuer, String... expectedIssuers)
{
issValidator = new IssValidator(requireIssuer, expectedIssuers);
return this;
}
/**
* Indicates whether or not the issuer ("iss") claim is required and optionally what the expected value is.
* @param requireIssuer true if issuer is required, false otherwise
* @param expectedIssuer the value that the issuer claim must have to pass validation, {@code null} means that any value is acceptable
* @return the same JwtConsumerBuilder
*/
public JwtConsumerBuilder setExpectedIssuer(boolean requireIssuer, String expectedIssuer)
{
issValidator = new IssValidator(expectedIssuer, requireIssuer);
return this;
}
/**
* Indicates the expected value of the issuer ("iss") claim and that the claim is required.
* Equivalent to calling {@link #setExpectedIssuer(boolean, String)} with {@code true} as the first argument.
* @param expectedIssuer the value that the issuer claim must have to pass validation, {@code null} means that any value is acceptable
* @return the same JwtConsumerBuilder
*/
public JwtConsumerBuilder setExpectedIssuer(String expectedIssuer)
{
return setExpectedIssuer(true, expectedIssuer);
}
/**
* Require that a subject ("sub") claim be present in the JWT.
* @return the same JwtConsumerBuilder
*/
public JwtConsumerBuilder setRequireSubject()
{
this.requireSubject = true;
return this;
}
/**
* Require that a subject ("sub") claim be present in the JWT and that its value
* match that of the provided subject.
* The subject ("sub") claim is defined in Section 4.1.2 of RFC 7519.
*
* @param subject the required value of the subject claim.
* @return the same JwtConsumerBuilder
*/
public JwtConsumerBuilder setExpectedSubject(String subject)
{
this.expectedSubject = subject;
return setRequireSubject();
}
/**
* Require that a JWT ID ("jti") claim be present in the JWT.
* @return the same JwtConsumerBuilder
*/
public JwtConsumerBuilder setRequireJwtId()
{
this.requireJti = true;
return this;
}
/**
* Require that the JWT contain an expiration time ("exp") claim.
* The expiration time is always checked when present (unless explicitly disabled) but
* calling this method strengthens the requirement such that a JWT without an expiration time
* will not pass validation.
* @return the same JwtConsumerBuilder
*/
public JwtConsumerBuilder setRequireExpirationTime()
{
dateClaimsValidator.setRequireExp(true);
return this;
}
/**
* Require that the JWT contain an issued at time ("iat") claim.
* @return the same JwtConsumerBuilder
*/
public JwtConsumerBuilder setRequireIssuedAt()
{
dateClaimsValidator.setRequireIat(true);
return this;
}
/**
* Require that the JWT contain an not before ("nbf") claim.
* @return the same JwtConsumerBuilder
*/
public JwtConsumerBuilder setRequireNotBefore()
{
dateClaimsValidator.setRequireNbf(true);
return this;
}
/**
* Set the time used to validate the expiration time, issued at time, and not before time claims.
* If not set (or null), the current time will be used to validate the date claims.
* @param evaluationTime the time with respect to which to validate the date claims.
* @return the same JwtConsumerBuilder
*/
public JwtConsumerBuilder setEvaluationTime(NumericDate evaluationTime)
{
dateClaimsValidator.setEvaluationTime(evaluationTime);
return this;
}
/**
* Set the amount of clock skew to allow for when validate the expiration time, issued at time, and not before time claims.
* @param secondsOfAllowedClockSkew the number of seconds of leniency in date comparisons
* @return the same JwtConsumerBuilder
*/
public JwtConsumerBuilder setAllowedClockSkewInSeconds(int secondsOfAllowedClockSkew)
{
dateClaimsValidator.setAllowedClockSkewSeconds(secondsOfAllowedClockSkew);
return this;
}
/**
* Set maximum on how far in the future the "exp" claim can be.
* @param maxFutureValidityInMinutes how far is too far (in minutes)
* @return the same JwtConsumerBuilder
*/
public JwtConsumerBuilder setMaxFutureValidityInMinutes(int maxFutureValidityInMinutes)
{
dateClaimsValidator.setMaxFutureValidityInMinutes(maxFutureValidityInMinutes);
return this;
}
/**
* Bypass the strict checks on the verification key. This might be needed, for example, if the
* JWT issuer is using 1024 bit RSA keys or HMAC secrets that are too small (smaller than the size of the hash output).
* @return the same JwtConsumerBuilder
*/
public JwtConsumerBuilder setRelaxVerificationKeyValidation()
{
relaxVerificationKeyValidation = true;
return this;
}
/**
* Bypass the strict checks on the decryption key.
* @return the same JwtConsumerBuilder
*/
public JwtConsumerBuilder setRelaxDecryptionKeyValidation()
{
relaxDecryptionKeyValidation = true;
return this;
}
/**
* Custom Validator implementations, which will be invoked when the {@code JwtConsumer} is validating the JWT claims.
* @param validator the validator
* @return the same JwtConsumerBuilder
*/
public JwtConsumerBuilder registerValidator(Validator validator)
{
customValidators.add(validator);
return this;
}
/**
* Set a callback JwsCustomizer that provides a hook to call arbitrary methods on the/any JsonWebSignature prior
* to the JwsConsumer using it to verify the signature.
* This might be used, for example, to allow for
* critical ("crit") headers vai {@link org.jose4j.jwx.JsonWebStructure#setKnownCriticalHeaders(String...)}
* that the caller knows how to handle and needs to tell the JwsConsumer to allow them.
* @param jwsCustomizer the JwsCustomizer implementation
* @return the same JwtConsumerBuilder
*/
public JwtConsumerBuilder setJwsCustomizer(JwsCustomizer jwsCustomizer)
{
this.jwsCustomizer = jwsCustomizer;
return this;
}
/**
* Set a callback JweCustomizer that provides a hook to call arbitrary methods on the/any JsonWebEncryption prior
* to the JwsConsumer using it for decryption.
* This might be used, for example, to allow for
* critical ("crit") headers vai {@link org.jose4j.jwx.JsonWebStructure#setKnownCriticalHeaders(String...)}
* that the caller knows how to handle and needs to tell the JwsConsumer to allow them.
* @param jweCustomizer the JweCustomizer implementation
* @return the same JwtConsumerBuilder
*/
public JwtConsumerBuilder setJweCustomizer(JweCustomizer jweCustomizer)
{
this.jweCustomizer = jweCustomizer;
return this;
}
/**
* Sets the {@link ProviderContext} for any JWS operations to be done by the JwtConsumer being built.
* This allows for
* a particular Java Cryptography Architecture provider to be indicated by name to be used
* for signature/MAC verification operations.
*
* @param jwsProviderContext the ProviderContext object indicating the Java Cryptography Architecture provider
* to be used for JWS signature/MAC verification operations when consuming a JWT.
* @return the same JwtConsumerBuilder
*/
public JwtConsumerBuilder setJwsProviderContext(ProviderContext jwsProviderContext)
{
this.jwsProviderContext = jwsProviderContext;
return this;
}
/**
* Sets the {@link ProviderContext} for any JWE operations to be done by the JwtConsumer being built.
* This allows for
* a particular Java Cryptography Architecture provider to be indicated by name to be used
* for decryption and related operations.
*
* @param jweProviderContext the ProviderContext object indicating the Java Cryptography Architecture provider
* to be used for decryption and related operations operations when consuming a JWT.
* @return the same JwtConsumerBuilder
*/
public JwtConsumerBuilder setJweProviderContext(ProviderContext jweProviderContext)
{
this.jweProviderContext = jweProviderContext;
return this;
}
/**
* Create the JwtConsumer with the options provided to the builder.
* @return the JwtConsumer
*/
public JwtConsumer build()
{
List validators = new ArrayList<>();
if (!skipAllValidators)
{
if (!skipAllDefaultValidators)
{
if (!skipDefaultAudienceValidation)
{
if (audValidator == null)
{
audValidator = new AudValidator(Collections.emptySet(), false);
}
validators.add(audValidator);
}
if (issValidator == null)
{
issValidator = new IssValidator(null, false);
}
validators.add(issValidator);
validators.add(dateClaimsValidator);
SubValidator subValidator = expectedSubject == null ? new SubValidator(requireSubject) : new SubValidator(expectedSubject);
validators.add(subValidator);
validators.add(new JtiValidator(requireJti));
}
validators.addAll(customValidators);
}
JwtConsumer jwtConsumer = new JwtConsumer();
jwtConsumer.setValidators(validators);
jwtConsumer.setVerificationKeyResolver(verificationKeyResolver);
jwtConsumer.setDecryptionKeyResolver(decryptionKeyResolver);
jwtConsumer.setJwsAlgorithmConstraints(jwsAlgorithmConstraints);
jwtConsumer.setJweAlgorithmConstraints(jweAlgorithmConstraints);
jwtConsumer.setJweContentEncryptionAlgorithmConstraints(jweContentEncryptionAlgorithmConstraints);
jwtConsumer.setRequireSignature(requireSignature);
jwtConsumer.setRequireEncryption(requireEncryption);
jwtConsumer.setLiberalContentTypeHandling(liberalContentTypeHandling);
jwtConsumer.setSkipSignatureVerification(skipSignatureVerification);
jwtConsumer.setSkipVerificationKeyResolutionOnNone(skipVerificationKeyResolutionOnNone);
jwtConsumer.setRelaxVerificationKeyValidation(relaxVerificationKeyValidation);
jwtConsumer.setRelaxDecryptionKeyValidation(relaxDecryptionKeyValidation);
jwtConsumer.setJwsCustomizer(jwsCustomizer);
jwtConsumer.setJweCustomizer(jweCustomizer);
jwtConsumer.setJwsProviderContext(jwsProviderContext);
jwtConsumer.setJweProviderContext(jweProviderContext);
return jwtConsumer;
}
}