Skip to content

Commit

Permalink
Merge pull request #12745 from ayoho/12409-introspectionRelax
Browse files Browse the repository at this point in the history
PR for Issue 12409: Add config attributes for OAuth introspection claim requirements
  • Loading branch information
ayoho committed Jun 24, 2020
2 parents 15f31e3 + 8381b10 commit 95f403f
Show file tree
Hide file tree
Showing 6 changed files with 111 additions and 27 deletions.
@@ -1,5 +1,5 @@
###############################################################################
# Copyright (c) 2013 IBM Corporation and others.
# Copyright (c) 2013, 2020 IBM Corporation and others.
# All rights reserved. This program and the accompanying materials
# are made available under the terms of the Eclipse Public License v1.0
# which accompanies this distribution, and is available at
Expand Down Expand Up @@ -165,7 +165,7 @@ headerName=Name of the header containing the inbound token
headerName.desc=The name of the header which carries the inbound token in the request.

disableIssChecking=Disable issuer checking
disableIssChecking.desc=Require the issuer claim to be absent when validating the json response for inbound token propagation.
disableIssChecking.desc=Require the issuer claim to be absent when validating the OAuth token introspection response for inbound token propagation.

validationEndpointUrl=Endpoint URL for validation
validationEndpointUrl.desc=The endpoint URL for validating the token inbound propagation. The type of endpoint is decided by the validationMethod.
Expand Down
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Copyright (c) 2013 IBM Corporation and others.
Copyright (c) 2013, 2020 IBM Corporation and others.
All rights reserved. This program and the accompanying materials
are made available under the terms of the Eclipse Public License v1.0
which accompanies this distribution, and is available at
Expand Down Expand Up @@ -134,6 +134,8 @@
<AD id="useAccessTokenAsIdToken" name="internal" description="internal use only" required="false" type="Boolean" default="false"/>
<AD id="tokenReuse" name="%tokenReuse" description="%tokenReuse.desc" required="false" type="Boolean" default="false" />
<AD id="forwardLoginParameter" name="%forwardLoginParameter" description="%forwardLoginParameter.desc" required="false" type="String" cardinality="2147483647" />
<AD id="requireExpClaimForIntrospection" name="internal" description="internal use only" required="false" type="Boolean" default="true" />
<AD id="requireIatClaimForIntrospection" name="internal" description="internal use only" required="false" type="Boolean" default="true" />
</OCD>

<Designate factoryPid="com.ibm.ws.security.openidconnect.client.oidcClientConfig">
Expand Down
@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2016 IBM Corporation and others.
* Copyright (c) 2016, 2020 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
Expand Down Expand Up @@ -632,8 +632,24 @@ protected boolean validateJsonResponse(JSONObject jobj, OidcClientConfig clientC
return false;
}
// ToDo: check exp, iat
Date currentDate = new Date();

if (!isExpValid(jobj, clientConfig)) {
return false;
}
if (!isIatValid(jobj, clientConfig)) {
return false;
}
if (!isIssuerValid(jobj, clientConfig)) {
return false;
}

// TODO see whether we need client id checking

return true;
}

private boolean isExpValid(JSONObject jobj, OidcClientConfig clientConfig) {
Date currentDate = new Date();
Long exp = 0L;
if (jobj.get("exp") != null && (exp = getLong(jobj.get("exp"))) != null) {
if (tc.isDebugEnabled()) {
Expand All @@ -642,13 +658,16 @@ protected boolean validateJsonResponse(JSONObject jobj, OidcClientConfig clientC
if (!verifyExpirationTime(exp, currentDate, clientConfig.getClockSkewInSeconds(), clientConfig)) {
return false;
}
} else {
} else if (clientConfig.requireExpClaimForIntrospection()) {
// required field
logError(clientConfig, "PROPAGATION_TOKEN_MISSING_REQUIRED_CLAIM_ERR", "exp", "iss, iat, exp"); // TODO
// include
// iss?
logError(clientConfig, "PROPAGATION_TOKEN_MISSING_REQUIRED_CLAIM_ERR", "exp", "iss, iat, exp");
return false;
}
return true;
}

private boolean isIatValid(JSONObject jobj, OidcClientConfig clientConfig) {
Date currentDate = new Date();
Long iat = 0L;
if (jobj.get("iat") != null && (iat = getLong(jobj.get("iat"))) != null) {
if (tc.isDebugEnabled()) {
Expand All @@ -657,22 +676,16 @@ protected boolean validateJsonResponse(JSONObject jobj, OidcClientConfig clientC
if (!checkIssueatTime(iat, currentDate, clientConfig.getClockSkewInSeconds(), clientConfig)) {
return false;
}
} else {
} else if (clientConfig.requireIatClaimForIntrospection()) {
// required field
logError(clientConfig, "PROPAGATION_TOKEN_MISSING_REQUIRED_CLAIM_ERR", "iat", "iss, iat, exp");
return false;
}
/*
* String issuer = null; String issuerIdentifier = null; if
* (jobj.get("iss") != null) { issuer = (String)jobj.get("iss"); if
* (issuer.isEmpty() || ((issuerIdentifier =
* getIssuerIdentifier(clientConfig)) == null) ||
* !issuer.equals(issuerIdentifier)) { logError(clientConfig,
* "PROPAGATION_TOKEN_ISS_ERROR", issuerIdentifier, issuer); return
* false; } } else { //required field logError(clientConfig,
* "PROPAGATION_TOKEN_MISSING_REQUIRED_CLAIM_ERR", "iss",
* "iss, iat, exp"); return false; }
*/
return true;
}

private boolean isIssuerValid(JSONObject jobj, OidcClientConfig clientConfig) {
Date currentDate = new Date();
if (issuerChecking(jobj, clientConfig)) {
Long nbf = 0L;
if (jobj.get("nbf") != null && (nbf = getLong(jobj.get("nbf"))) != null) {
Expand All @@ -686,9 +699,6 @@ protected boolean validateJsonResponse(JSONObject jobj, OidcClientConfig clientC
} else {
return false;
}

// TODO see whether we need client id checking

return true;
}

Expand Down
Expand Up @@ -156,6 +156,8 @@ public class OidcClientConfigImpl implements OidcClientConfig {
public static final String CFG_KEY_DISCOVERY_POLLING_RATE = "discoveryPollingRate";
public static final String CFG_KEY_USE_SYSPROPS_FOR_HTTPCLIENT_CONNECTONS = "useSystemPropertiesForHttpClientConnections";
public static final String CFG_KEY_FORWARD_LOGIN_PARAMETER = "forwardLoginParameter";
public static final String CFG_KEY_REQUIRE_EXP_CLAIM = "requireExpClaimForIntrospection";
public static final String CFG_KEY_REQUIRE_IAT_CLAIM = "requireIatClaimForIntrospection";

public static final String OPDISCOVERY_AUTHZ_EP_URL = "authorization_endpoint";
public static final String OPDISCOVERY_TOKEN_EP_URL = "token_endpoint";
Expand Down Expand Up @@ -243,6 +245,8 @@ public class OidcClientConfigImpl implements OidcClientConfig {
private String[] resources;
private boolean useAccessTokenAsIdToken;
private List<String> forwardLoginParameter;
private boolean requireExpClaimForIntrospection = true;
private boolean requireIatClaimForIntrospection = true;

private String oidcClientCookieName;
private boolean authnSessionDisabled;
Expand Down Expand Up @@ -507,6 +511,8 @@ private void processConfigProps(Map<String, Object> props) {
useAccessTokenAsIdToken = configUtils.getBooleanConfigAttribute(props, CFG_KEY_USE_ACCESS_TOKEN_AS_ID_TOKEN, useAccessTokenAsIdToken);
tokenReuse = configUtils.getBooleanConfigAttribute(props, CFG_KEY_TOKEN_REUSE, tokenReuse);
forwardLoginParameter = oidcConfigUtils.readAndSanitizeForwardLoginParameter(props, id, CFG_KEY_FORWARD_LOGIN_PARAMETER);
requireExpClaimForIntrospection = configUtils.getBooleanConfigAttribute(props, CFG_KEY_REQUIRE_EXP_CLAIM, requireExpClaimForIntrospection);
requireIatClaimForIntrospection = configUtils.getBooleanConfigAttribute(props, CFG_KEY_REQUIRE_IAT_CLAIM, requireIatClaimForIntrospection);
// TODO - 3Q16: Check the validationEndpointUrl to make sure it is valid
// before continuing to process this config
// checkValidationEndpointUrl();
Expand Down Expand Up @@ -1843,4 +1849,14 @@ public HashMap<String, String> getJwkRequestParams() {
return jwkRequestParamMap;
}

@Override
public boolean requireExpClaimForIntrospection() {
return requireExpClaimForIntrospection;
}

@Override
public boolean requireIatClaimForIntrospection() {
return requireIatClaimForIntrospection;
}

}
Expand Up @@ -690,12 +690,14 @@ public void testValidateJsonResponse_TimeIsExpired() throws IOException {
}

@Test
public void testValidateJsonResponse_NotExpirationTime() throws IOException {
public void testValidateJsonResponse_noExpirationTime() throws IOException {
final String JSONSTRING = "{\"user\":\"user1\" , \"active\":true}";
JSONObject jobj = JSONObject.parse(JSONSTRING);

mockery.checking(new Expectations() {
{
one(clientConfig).requireExpClaimForIntrospection();
will(returnValue(true));
allowing(clientConfig).getInboundPropagation();
will(returnValue(REQUIRED));
}
Expand All @@ -705,6 +707,30 @@ public void testValidateJsonResponse_NotExpirationTime() throws IOException {
assertFalse("Expected to receive a false value but was received: " + isValid, isValid);
}

@Test
public void testValidateJsonResponse_noExpirationTime_doNotRequireExpClaim() throws IOException {
final String JSONSTRING = "{\"user\":\"user1\" , \"active\":true}";
JSONObject jobj = JSONObject.parse(JSONSTRING);
Calendar cal = Calendar.getInstance();
Long currentDate = cal.getTimeInMillis() / 1000;
jobj.put("iat", currentDate);
jobj.put("iss", GOOD_ISSUER);

mockery.checking(new Expectations() {
{
one(clientConfig).requireExpClaimForIntrospection();
will(returnValue(false));
one(clientConfig).disableIssChecking();
will(returnValue(false));
one(clientConfig).getIssuerIdentifier();
will(returnValue(GOOD_ISSUER));
}
});

Boolean isValid = tokenAuth.validateJsonResponse(jobj, clientConfig);
assertTrue("Response should have been considered valid, but was not.", isValid);
}

@Test
public void testValidateJsonResponse_BadIssueAtTime() throws IOException {
Calendar cal = Calendar.getInstance();
Expand All @@ -728,7 +754,7 @@ public void testValidateJsonResponse_BadIssueAtTime() throws IOException {
}

@Test
public void testValidateJsonResponse_NotIssueAtTime() throws IOException {
public void testValidateJsonResponse_noIssueAtTime() throws IOException {
Calendar cal = Calendar.getInstance();
Long expTime = cal.getTimeInMillis() / 1000;

Expand All @@ -737,6 +763,8 @@ public void testValidateJsonResponse_NotIssueAtTime() throws IOException {

mockery.checking(new Expectations() {
{
one(clientConfig).requireIatClaimForIntrospection();
will(returnValue(true));
allowing(clientConfig).getInboundPropagation();
will(returnValue(REQUIRED));
}
Expand All @@ -746,6 +774,30 @@ public void testValidateJsonResponse_NotIssueAtTime() throws IOException {
assertFalse("Expected to receive a false value but was received: " + isValid, isValid);
}

@Test
public void testValidateJsonResponse_noIssueAtTime_doNotRequireIatClaim() throws IOException {
Calendar cal = Calendar.getInstance();
Long expTime = cal.getTimeInMillis() / 1000;

final String JSONSTRING = getJSONObjectString(true, expTime);
JSONObject jobj = JSONObject.parse(JSONSTRING);
jobj.put("iss", GOOD_ISSUER);

mockery.checking(new Expectations() {
{
one(clientConfig).requireIatClaimForIntrospection();
will(returnValue(false));
one(clientConfig).disableIssChecking();
will(returnValue(false));
one(clientConfig).getIssuerIdentifier();
will(returnValue(GOOD_ISSUER));
}
});

Boolean isValid = tokenAuth.validateJsonResponse(jobj, clientConfig);
assertTrue("Response should have been considered valid, but was not.", isValid);
}

@Test
public void testValidateJsonResponse_IncompatibleIssuer() throws IOException {
mockery.checking(new Expectations() {
Expand Down
@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2013, 2018 IBM Corporation and others.
* Copyright (c) 2013, 2020 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
Expand Down Expand Up @@ -77,4 +77,8 @@ public interface OidcClientConfig extends ConvergedClientConfig {
@Override
String getRedirectUrlWithJunctionPath(String redirect_url);

public boolean requireExpClaimForIntrospection();

public boolean requireIatClaimForIntrospection();

}

0 comments on commit 95f403f

Please sign in to comment.