net.oauth.jsontoken.JsonTokenParser Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of com.liferay.document.library.opener.onedrive.web
Show all versions of com.liferay.document.library.opener.onedrive.web
Liferay Document Library Opener OneDrive Web
/**
* Copyright 2010 Google Inc.
*
* 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 net.oauth.jsontoken;
import com.google.common.base.Preconditions;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import net.oauth.jsontoken.crypto.AsciiStringVerifier;
import net.oauth.jsontoken.crypto.SignatureAlgorithm;
import net.oauth.jsontoken.crypto.Verifier;
import net.oauth.jsontoken.discovery.VerifierProviders;
import org.apache.commons.codec.binary.Base64;
import org.joda.time.Instant;
import java.security.SignatureException;
import java.util.List;
import java.util.regex.Pattern;
/**
* Class that parses and verifies JSON Tokens.
*/
public class JsonTokenParser {
private final Clock clock;
private final VerifierProviders verifierProviders;
private final Checker[] checkers;
/**
* Creates a new {@link JsonTokenParser} with a default system clock. The default
* system clock tolerates a clock skew of up to {@link SystemClock#DEFAULT_ACCEPTABLE_CLOCK_SKEW}.
*
* @param verifierProviders an object that provides signature verifiers
* based on a signature algorithm, the signer, and key ids.
* @param checker an audience checker that validates the audience in the JSON token.
*/
public JsonTokenParser(VerifierProviders verifierProviders, Checker checker) {
this(new SystemClock(), verifierProviders, checker);
}
/**
* Creates a new {@link JsonTokenParser}.
*
* @param clock a clock object that will decide whether a given token is
* currently valid or not.
* @param verifierProviders an object that provides signature verifiers
* based on a signature algorithm, the signer, and key ids.
* @param checkers an array of checkers that validates the parameters in the JSON token.
*/
public JsonTokenParser(Clock clock, VerifierProviders verifierProviders, Checker... checkers) {
this.clock = Preconditions.checkNotNull(clock);
this.verifierProviders = verifierProviders;
this.checkers = checkers;
}
/**
* Decodes the JWT token string into a JsonToken object. Does not perform
* any validation of headers or claims.
*
* @param tokenString The original encoded representation of a JWT
* @return Unverified contents of the JWT as a JsonToken
*/
public JsonToken deserialize(String tokenString) {
String[] pieces = splitTokenString(tokenString);
String jwtHeaderSegment = pieces[0];
String jwtPayloadSegment = pieces[1];
byte[] signature = Base64.decodeBase64(pieces[2]);
JsonParser parser = new JsonParser();
JsonObject header = parser.parse(JsonTokenUtil.fromBase64ToJsonString(jwtHeaderSegment))
.getAsJsonObject();
JsonObject payload = parser.parse(JsonTokenUtil.fromBase64ToJsonString(jwtPayloadSegment))
.getAsJsonObject();
JsonToken jsonToken = new JsonToken(header, payload, clock, tokenString);
return jsonToken;
}
/**
* Verifies that the jsonToken has a valid signature and valid standard claims
* (iat, exp). Uses VerifierProviders to obtain the secret key.
*
* @param jsonToken
* @throws SignatureException
*/
public void verify(JsonToken jsonToken) throws SignatureException {
List verifiers = provideVerifiers(jsonToken);
verify(jsonToken, verifiers);
}
/**
* Parses, and verifies, a JSON Token.
*
* @param tokenString the serialized token that is to parsed and verified.
* @return the deserialized {@link JsonObject}, suitable for passing to the constructor
* of {@link JsonToken} or equivalent constructor of {@link JsonToken} subclasses.
* @throws SignatureException
*/
public JsonToken verifyAndDeserialize(String tokenString) throws SignatureException {
JsonToken jsonToken = deserialize(tokenString);
verify(jsonToken);
return jsonToken;
}
/**
* Verifies that the jsonToken has a valid signature and valid standard claims
* (iat, exp). Does not need VerifierProviders because verifiers are passed in
* directly.
*
* @param jsonToken the token to verify
* @throws SignatureException when the signature is invalid
* @throws IllegalStateException when exp or iat are invalid
*/
public void verify(JsonToken jsonToken, List verifiers) throws SignatureException {
if (! signatureIsValid(jsonToken.getTokenString(), verifiers)) {
throw new SignatureException("Invalid signature for token: " +
jsonToken.getTokenString());
}
Instant issuedAt = jsonToken.getIssuedAt();
Instant expiration = jsonToken.getExpiration();
if (issuedAt == null && expiration != null) {
issuedAt = new Instant(0);
}
if (issuedAt != null && expiration == null) {
expiration = new Instant(Long.MAX_VALUE);
}
if (issuedAt != null && expiration != null) {
if (issuedAt.isAfter(expiration)
|| ! clock.isCurrentTimeInInterval(issuedAt, expiration)) {
throw new IllegalStateException(String.format("Invalid iat and/or exp. iat: %s exp: %s "
+ "now: %s", jsonToken.getIssuedAt(), jsonToken.getExpiration(), clock.now()));
}
}
if (checkers != null) {
for (Checker checker : checkers) {
checker.check(jsonToken.getPayloadAsJsonObject());
}
}
}
/**
* Verifies that a JSON Web Token's signature is valid.
*
* @param tokenString the encoded and signed JSON Web Token to verify.
* @param verifiers used to verify the signature. These usually encapsulate
* secret keys.
*/
public boolean signatureIsValid(String tokenString, List verifiers) {
String[] pieces = splitTokenString(tokenString);
byte[] signature = Base64.decodeBase64(pieces[2]);
String baseString = JsonTokenUtil.toDotFormat(pieces[0], pieces[1]);
boolean sigVerified = false;
for (Verifier verifier : verifiers) {
AsciiStringVerifier asciiVerifier = new AsciiStringVerifier(verifier);
try {
asciiVerifier.verifySignature(baseString, signature);
sigVerified = true;
break;
} catch (SignatureException e) {
continue;
}
}
return sigVerified;
}
/**
* Verifies that a JSON Web Token is not expired.
*
* @param jsonToken the token to verify
* @param now the instant to use as point of reference for current time
* @return false if the token is expired, true otherwise
*/
public boolean expirationIsValid(JsonToken jsonToken, Instant now) {
Instant expiration = jsonToken.getExpiration();
if ((expiration != null) && now.isAfter(expiration)) {
return false;
}
return true;
}
/**
* Verifies that a JSON Web Token was issued in the past.
*
* @param jsonToken the token to verify
* @param now the instant to use as point of reference for current time
* @return false if the JWT's 'iat' is later than now, true otherwise
*/
public boolean issuedAtIsValid(JsonToken jsonToken, Instant now) {
Instant issuedAt = jsonToken.getIssuedAt();
if ((issuedAt != null) && now.isBefore(issuedAt)) {
return false;
}
return true;
}
/**
* Use VerifierProviders to get a list of verifiers for this token
*
* @param jsonToken
* @return list of verifiers
* @throws SignatureException
*/
private List provideVerifiers(JsonToken jsonToken) throws SignatureException {
Preconditions.checkNotNull(verifierProviders);
JsonObject header = jsonToken.getHeader();
JsonElement keyIdJson = header.get(JsonToken.KEY_ID_HEADER);
String keyId = (keyIdJson == null) ? null : keyIdJson.getAsString();
SignatureAlgorithm sigAlg = jsonToken.getSignatureAlgorithm();
List verifiers = verifierProviders.getVerifierProvider(sigAlg)
.findVerifier(jsonToken.getIssuer(), keyId);
if (verifiers == null) {
throw new IllegalStateException("No valid verifier for issuer: " + jsonToken.getIssuer());
}
return verifiers;
}
/**
* @param tokenString The original encoded representation of a JWT
* @return Three components of the JWT as an array of strings
*/
private String[] splitTokenString(String tokenString) {
String[] pieces = tokenString.split(Pattern.quote(JsonTokenUtil.DELIMITER));
if (pieces.length != 3) {
throw new IllegalStateException("Expected JWT to have 3 segments separated by '" +
JsonTokenUtil.DELIMITER + "', but it has " + pieces.length + " segments");
}
return pieces;
}
}