Skip to content

Commit

Permalink
Merge pull request #24995 from jimmy1wu/24967_ignoreNbfClaim
Browse files Browse the repository at this point in the history
ignore logout token claims that are not understood
  • Loading branch information
jimmy1wu authored Apr 18, 2023
2 parents 04af0db + ca2573b commit fcb5a7c
Show file tree
Hide file tree
Showing 4 changed files with 18 additions and 97 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2022 IBM Corporation and others.
* Copyright (c) 2022, 2023 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
Expand Down Expand Up @@ -579,106 +579,27 @@ public long hoursToSeconds(long hours) throws Exception {
* @throws Exception
*/
@Test
public void LogoutTokenValidationTests_invalid_iat_yesterday_without_exp() throws Exception {
public void LogoutTokenValidationTests_invalid_iat_yesterday() throws Exception {

long yesterday = System.currentTimeMillis() / 1000 - hoursToSeconds(24);

genericAddedUpdatedClaimsTest(Constants.PAYLOAD_ISSUED_AT_TIME_IN_SECS, yesterday);

}

/**
* Test shows that even though the token is "old", the runtime doens't check its value - as long as
* the exp (+clockskew) is still after now
*
* @throws Exception
*/
@Test
public void LogoutTokenValidationTests_invalid_iat_yesterday_with_exp() throws Exception {

JWTTokenBuilder builder = loginAndReturnIdTokenData(defaultClient);

String logutOutEndpoint = buildBackchannelLogoutUri(defaultClient);

long yesterday = System.currentTimeMillis() / 1000 - hoursToSeconds(24);
long soon = System.currentTimeMillis() / 1000 + minutesToSeconds(2);

builder.setClaim(Constants.PAYLOAD_ISSUED_AT_TIME_IN_SECS, yesterday);
builder.setClaim(Constants.PAYLOAD_EXPIRATION_TIME_IN_SECS, soon);

List<endpointSettings> parms = createParmFromBuilder(builder);

List<validationData> expectations = vData.addResponseStatusExpectation(null, Constants.INVOKE_BACK_CHANNEL_LOGOUT_ENDPOINT, Constants.OK_STATUS);

invokeBcl(logutOutEndpoint, parms, expectations);

}

/**
* Test shows that even though the token is very, very "old", the runtime doens't check its value
*
* @throws Exception
*/
@Test
public void LogoutTokenValidationTests_invalid_iat_1971_without_exp() throws Exception {
public void LogoutTokenValidationTests_invalid_iat_1971() throws Exception {

// 02/21/1971
genericAddedUpdatedClaimsTest(Constants.PAYLOAD_ISSUED_AT_TIME_IN_SECS, 36010452);

}

/**
* Test shows that even though the token is very, very "old", the runtime doens't check its value - as long as
* the exp (+clockskew) is still after now
*
* @throws Exception
*/
@Test
public void LogoutTokenValidationTests_invalid_iat_1971_with_exp() throws Exception {

JWTTokenBuilder builder = loginAndReturnIdTokenData(defaultClient);

String logutOutEndpoint = buildBackchannelLogoutUri(defaultClient);

// 02/21/1971
long soon = System.currentTimeMillis() / 1000 + hoursToSeconds(2);

builder.setClaim(Constants.PAYLOAD_ISSUED_AT_TIME_IN_SECS, 36010452);
builder.setClaim(Constants.PAYLOAD_EXPIRATION_TIME_IN_SECS, soon);

List<endpointSettings> parms = createParmFromBuilder(builder);

List<validationData> expectations = vData.addResponseStatusExpectation(null, Constants.INVOKE_BACK_CHANNEL_LOGOUT_ENDPOINT, Constants.OK_STATUS);

invokeBcl(logutOutEndpoint, parms, expectations);

}

/**
* Test that the logout token is valid when it contains an exp that is not earlier than now (+clockskew)
*
* @throws Exception
*/
@Test
public void LogoutTokenValidationTests_optional_exp_notExpired() throws Exception {

long stillValid = System.currentTimeMillis() / 1000 + hoursToSeconds(2);
genericAddedUpdatedClaimsTest(Constants.PAYLOAD_EXPIRATION_TIME_IN_SECS, stillValid);

}

/**
* Test that the backchannelLogout fails when we specify an exp that is prior to the current time (just beyond the clockskew)
*
* @throws Exception
*/
@Test
public void LogoutTokenValidationTests_optional_exp_expired() throws Exception {

long expired = System.currentTimeMillis() / 1000 - hoursToSeconds(1); // time is outside clockskew
genericInvalidClaimTest(Constants.PAYLOAD_EXPIRATION_TIME_IN_SECS, expired, "InvalidJwtException");
}

/**
* Test that the backchannelLogout fails when we omit the required jti claim from the logout token.
*
Expand Down Expand Up @@ -997,8 +918,8 @@ public void LogoutTokenValidationTests_include_ignored_claims() throws Exception
HashMap<String, Object> claimMap = new HashMap<String, Object>() {
{

// TODO - once the runtime validation is updated, re-enable the next line and remove the last check in this method
//put(Constants.PAYLOAD_NOT_BEFORE_TIME_IN_SECS, 2056387597);
put(Constants.PAYLOAD_EXPIRATION_TIME_IN_SECS, System.currentTimeMillis() / 1000 - hoursToSeconds(1)); // time is outside clockskew
put(Constants.PAYLOAD_NOT_BEFORE_TIME_IN_SECS, 2056387597);
put("email", "joe@something.com");
put(Constants.PAYLOAD_AUTHZ_TIME_IN_SECS, 478550797);
put(Constants.PAYLOAD_AUTHORIZED_PARTY, "noOne");
Expand All @@ -1018,9 +939,6 @@ public void LogoutTokenValidationTests_include_ignored_claims() throws Exception
genericAddedUpdatedClaimsTest(entry.getKey(), entry.getValue());
}

// TODO - once the runtime validation is updated, remove the next line and restore the line in the map above
genericInvalidClaimTest(Constants.PAYLOAD_NOT_BEFORE_TIME_IN_SECS, 2056387597, "InvalidJwtException");

}

/*****************************************
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2022 IBM Corporation and others.
* Copyright (c) 2022, 2023 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
Expand Down Expand Up @@ -83,7 +83,7 @@ public JwtClaims validateToken(String logoutTokenString) throws BackchannelLogou
JwtClaims claims = jose4jUtil.validateJwsSignature(jwtContext, config);

verifyAllRequiredClaimsArePresent(claims);
verifyIssAudIatExpClaims(claims);
verifyIssAudIatClaims(claims);
verifySubAndOrSidPresent(claims);
verifyEventsClaim(claims);
verifyNonceClaimNotPresent(claims);
Expand Down Expand Up @@ -125,13 +125,13 @@ void verifyAllRequiredClaimsArePresent(JwtClaims claims) throws MalformedClaimEx
}

/**
* Validate the iss, aud, and iat (and exp) Claims in the same way they are validated in ID Tokens.
* Validate the iss, aud, and iat Claims in the same way they are validated in ID Tokens.
*/
void verifyIssAudIatExpClaims(JwtClaims claims) throws IDTokenValidationFailedException, Exception, MalformedClaimException, JWTTokenValidationFailedException {
void verifyIssAudIatClaims(JwtClaims claims) throws IDTokenValidationFailedException, Exception, MalformedClaimException, JWTTokenValidationFailedException {
Jose4jValidator validator = getJose4jValidator();
validator.verifyIssForIdToken(claims.getIssuer());
validator.verifyAudForIdToken(claims.getAudience());
validator.verifyIatAndExpClaims(claims);
validator.verifyIatAndExpClaims(claims.getIssuedAt(), null, claims.getSubject());
}

Jose4jValidator getJose4jValidator() {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2016, 2020 IBM Corporation and others.
* Copyright (c) 2016, 2023 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
Expand Down Expand Up @@ -247,7 +247,10 @@ public void verifyAudForIdToken(List<String> audiences) throws IDTokenValidation
public void verifyIatAndExpClaims(JwtClaims jwtClaims) throws MalformedClaimException, JWTTokenValidationFailedException {
NumericDate issueAtClaim = jwtClaims.getIssuedAt();
NumericDate expirationClaim = jwtClaims.getExpirationTime();
verifyIatAndExpClaims(issueAtClaim, expirationClaim, jwtClaims.getSubject());
}

public void verifyIatAndExpClaims(NumericDate issueAtClaim, NumericDate expirationClaim, String subject) throws JWTTokenValidationFailedException {
Instant issuedAt = null;
Instant expiration = null;
if (issueAtClaim == null) {
Expand All @@ -268,7 +271,7 @@ public void verifyIatAndExpClaims(JwtClaims jwtClaims) throws MalformedClaimExce
if (issuedAt.isAfter(expiration) ||
!JsonTokenUtil.isCurrentTimeInInterval(clockSkewInSeconds, issuedAt.getMillis(), expiration.getMillis())) {

Object[] objects = new Object[] { this.clientId, jwtClaims.getSubject(), new Instant(System.currentTimeMillis()), expiration, issuedAt };
Object[] objects = new Object[] { this.clientId, subject, new Instant(System.currentTimeMillis()), expiration, issuedAt };
String msgCode = "OIDC_JWT_VERIFY_STATE_ERR";

if (oidcClientRequest != null) {
Expand All @@ -288,7 +291,7 @@ public JwtClaims validateJwsSignature(JsonWebSignature signature, String jwtStri
verifySignAlgOnly(signature);

JwtConsumerBuilder builder = new JwtConsumerBuilder();
builder.setSkipDefaultAudienceValidation();
builder.setSkipAllDefaultValidators();
if (!rpSpecifiedSigningAlgorithm) {
// Signature algorithm is set to "none"; don't check the signature
builder.setDisableRequireSignature()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -380,7 +380,7 @@ public void test_verifyIssAudIatExpClaims_badIss() throws Exception {
try {
setConfigExpectations("HS256", null, 300L, ISSUER);

validator.verifyIssAudIatExpClaims(claims);
validator.verifyIssAudIatClaims(claims);
fail("Should have thrown an exception but didn't.");
} catch (IDTokenValidationFailedException e) {
verifyException(e, CWWKS1751E_OIDC_IDTOKEN_VERIFY_ISSUER_ERR);
Expand All @@ -395,7 +395,7 @@ public void test_verifyIssAudIatExpClaims_badAud() throws Exception {
try {
setConfigExpectations("HS256", null, 300L, ISSUER);

validator.verifyIssAudIatExpClaims(claims);
validator.verifyIssAudIatClaims(claims);
fail("Should have thrown an exception but didn't.");
} catch (IDTokenValidationFailedException e) {
verifyException(e, CWWKS1754E_OIDC_IDTOKEN_VERIFY_AUD_ERR);
Expand Down

0 comments on commit fcb5a7c

Please sign in to comment.