diff --git a/build.gradle b/build.gradle index 3a1ca8dff2..74cd76a1ad 100644 --- a/build.gradle +++ b/build.gradle @@ -621,6 +621,11 @@ dependencies { implementation "org.opensaml:opensaml-storage-api:${open_saml_version}" implementation "com.nulab-inc:zxcvbn:1.9.0" + implementation 'com.nimbusds:oauth2-oidc-sdk:11.18' + implementation 'net.minidev:json-smart:2.5.1' + implementation 'com.nimbusds:content-type:2.3' + + testImplementation 'org.apache.camel:camel-xmlsecurity:3.22.2' runtimeOnly 'com.google.guava:failureaccess:1.0.2' runtimeOnly 'org.apache.commons:commons-text:1.11.0' diff --git a/src/main/java/com/amazon/dlic/auth/http/jwt/keybyoidc/HTTPOpenIdAuthenticator.java b/src/main/java/com/amazon/dlic/auth/http/jwt/keybyoidc/HTTPOpenIdAuthenticator.java index 0089b95376..3c077dfd2c 100644 --- a/src/main/java/com/amazon/dlic/auth/http/jwt/keybyoidc/HTTPOpenIdAuthenticator.java +++ b/src/main/java/com/amazon/dlic/auth/http/jwt/keybyoidc/HTTPOpenIdAuthenticator.java @@ -11,25 +11,23 @@ package com.amazon.dlic.auth.http.jwt.keybyoidc; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.nio.charset.StandardCharsets; -import java.nio.file.Path; -import java.text.ParseException; -import java.util.Map; -import java.util.Optional; - -import org.apache.http.HttpEntity; -import org.apache.http.client.config.RequestConfig; -import org.apache.http.client.methods.CloseableHttpResponse; -import org.apache.http.client.methods.HttpGet; +import com.amazon.dlic.auth.http.jwt.AbstractHTTPJwtAuthenticator; +import com.amazon.dlic.util.SettingsBasedSSLConfigurator; +import com.nimbusds.common.contenttype.ContentType; +import com.nimbusds.jwt.JWTClaimsSet; +import com.nimbusds.jwt.SignedJWT; +import com.nimbusds.oauth2.sdk.http.HTTPRequest; +import com.nimbusds.oauth2.sdk.http.HTTPResponse; +import com.nimbusds.oauth2.sdk.token.AccessToken; +import com.nimbusds.oauth2.sdk.token.AccessTokenType; +import com.nimbusds.openid.connect.sdk.UserInfoRequest; +import com.nimbusds.openid.connect.sdk.UserInfoResponse; +import org.apache.commons.lang3.StringUtils; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.client.HttpClients; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; - import org.opensearch.OpenSearchSecurityException; import org.opensearch.common.settings.Settings; import org.opensearch.common.util.concurrent.ThreadContext; @@ -38,17 +36,29 @@ import org.opensearch.security.filter.SecurityResponse; import org.opensearch.security.user.AuthCredentials; -import com.amazon.dlic.auth.http.jwt.AbstractHTTPJwtAuthenticator; -import com.amazon.dlic.util.SettingsBasedSSLConfigurator; +import com.nimbusds.common.contenttype.ContentType; import com.nimbusds.jwt.JWTClaimsSet; import com.nimbusds.jwt.SignedJWT; +import com.nimbusds.oauth2.sdk.http.HTTPRequest; +import com.nimbusds.oauth2.sdk.http.HTTPResponse; +import com.nimbusds.oauth2.sdk.token.AccessToken; +import com.nimbusds.oauth2.sdk.token.AccessTokenType; +import com.nimbusds.oauth2.sdk.util.StringUtils; +import com.nimbusds.openid.connect.sdk.UserInfoRequest; +import com.nimbusds.openid.connect.sdk.UserInfoResponse; + +import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; +import java.nio.file.Path; +import java.text.ParseException; +import java.util.Map; +import java.util.Optional; -import static org.apache.http.HttpHeaders.AUTHORIZATION; -import static org.apache.http.entity.ContentType.APPLICATION_JSON; -import static com.amazon.dlic.auth.http.jwt.keybyoidc.OpenIdConstants.APPLICATION_JWT; import static com.amazon.dlic.auth.http.jwt.keybyoidc.OpenIdConstants.CLIENT_ID; import static com.amazon.dlic.auth.http.jwt.keybyoidc.OpenIdConstants.ISSUER_ID_URL; import static com.amazon.dlic.auth.http.jwt.keybyoidc.OpenIdConstants.SUB_CLAIM; +import static org.apache.http.HttpHeaders.AUTHORIZATION; public class HTTPOpenIdAuthenticator implements HTTPAuthenticator { @@ -100,18 +110,6 @@ public Optional reRequestAuthentication(SecurityRequest reques return Optional.empty(); } - // Public for testing - public CloseableHttpClient createHttpClient() { - HttpClientBuilder builder; - builder = HttpClients.custom(); - builder.useSystemProperties(); - if (sslConfig != null) { - builder.setSSLSocketFactory(sslConfig.toSSLConnectionSocketFactory()); - } - - return builder.build(); - } - /** * This method performs the logic required for making use of the userinfo_endpoint OIDC feature. * Per the spec: https://openid.net/specs/openid-connect-core-1_0.html#UserInfo there are 10 verification steps we must perform @@ -132,74 +130,65 @@ public CloseableHttpClient createHttpClient() { */ public AuthCredentials extractCredentials0(SecurityRequest request, ThreadContext context) throws OpenSearchSecurityException { - try (CloseableHttpClient httpClient = createHttpClient()) { - - HttpGet httpGet = new HttpGet(this.userInfoEndpoint); + try { - RequestConfig requestConfig = RequestConfig.custom() - .setConnectionRequestTimeout(requestTimeoutMs) - .setConnectTimeout(requestTimeoutMs) - .build(); + URI userInfoEndpointURI = new URI(this.userInfoEndpoint); - httpGet.setConfig(requestConfig); - httpGet.addHeader(AUTHORIZATION, request.getHeaders().get(AUTHORIZATION).get(0)); + String bearerHeader = request.getHeaders().get(AUTHORIZATION).getFirst(); + if (!StringUtils.isBlank(bearerHeader)) { + if (bearerHeader.contains("Bearer ")) { + bearerHeader = bearerHeader.substring(7); + } + } - // HTTPGet should internally verify the appropriate TLS cert. - try (CloseableHttpResponse response = httpClient.execute(httpGet)) { + String finalBearerHeader = bearerHeader; - if (response.getStatusLine().getStatusCode() < 200 || response.getStatusLine().getStatusCode() >= 300) { - throw new AuthenticatorUnavailableException( - "Error while getting " + this.userInfoEndpoint + ": Invalid status code " + response.getStatusLine().getStatusCode() - ); + AccessToken accessToken = new AccessToken(AccessTokenType.BEARER, finalBearerHeader) { + @Override + public String toAuthorizationHeader() { + return "Bearer " + finalBearerHeader; } + }; - HttpEntity httpEntity = response.getEntity(); + UserInfoRequest userInfoRequest = new UserInfoRequest(userInfoEndpointURI, accessToken); - if (httpEntity == null) { - throw new AuthenticatorUnavailableException("Error while getting " + this.userInfoEndpoint + ": Empty response entity"); - } + HTTPRequest httpRequest = userInfoRequest.toHTTPRequest(); - String contentType = httpEntity.getContentType().getValue(); - if (!contentType.contains(APPLICATION_JSON.getMimeType()) && !contentType.contains(APPLICATION_JWT)) { - throw new AuthenticatorUnavailableException( - "Error while getting " + this.userInfoEndpoint + ": Invalid content type in response" - ); - } + HTTPResponse httpResponse = httpRequest.send(); + if (httpResponse.getStatusCode() < 200 || httpResponse.getStatusCode() >= 300) { + throw new AuthenticatorUnavailableException( + "Error while getting " + this.userInfoEndpoint + ": " + httpResponse.getStatusMessage() + ); + } - String userinfoContent; - - try ( - // got this from ChatGpt & Amazon Q - InputStream inputStream = httpEntity.getContent(); - InputStreamReader reader = new InputStreamReader(inputStream, StandardCharsets.UTF_8) - ) { - StringBuilder content = new StringBuilder(); - char[] buffer = new char[8192]; - int bytesRead; - while ((bytesRead = reader.read(buffer)) != -1) { - content.append(buffer, 0, bytesRead); - } - userinfoContent = content.toString(); - } catch (IOException e) { + try { + + UserInfoResponse userInfoResponse = UserInfoResponse.parse(httpResponse); + + if (!userInfoResponse.indicatesSuccess()) { throw new AuthenticatorUnavailableException( - "Error while getting " + this.userInfoEndpoint + ": Unable to read response content" + "Error while getting " + this.userInfoEndpoint + ": " + userInfoResponse.toErrorResponse() ); } + String contentType = String.valueOf(httpResponse.getHeaderValues("content-type")); + JWTClaimsSet claims; - boolean isSigned = contentType.contains(APPLICATION_JWT); - if (contentType.contains(APPLICATION_JWT)) { // We don't need the userinfo_encrypted_response_alg since the + boolean isSigned = contentType.contains(ContentType.APPLICATION_JWT.toString()); + if (isSigned) { // We don't need the userinfo_encrypted_response_alg since the // selfRefreshingKeyProvider has access to the keys - claims = openIdJwtAuthenticator.getJwtClaimsSetFromInfoContent(userinfoContent); + claims = openIdJwtAuthenticator.getJwtClaimsSetFromInfoContent( + userInfoResponse.toSuccessResponse().getUserInfoJWT().getParsedString() + ); } else { - claims = JWTClaimsSet.parse(userinfoContent); + claims = JWTClaimsSet.parse(userInfoResponse.toSuccessResponse().getUserInfo().toString()); } String id = openIdJwtAuthenticator.getJwtClaimsSet(request).getSubject(); String missing = validateResponseClaims(claims, id, isSigned); if (!missing.isBlank()) { throw new AuthenticatorUnavailableException( - "Error while getting " + this.userInfoEndpoint + ": Missing or invalid required claims in response: " + missing + "Error while getting " + this.userInfoEndpoint + ": Missing or invalid required claims in response: " + missing ); } @@ -221,7 +210,7 @@ public AuthCredentials extractCredentials0(SecurityRequest request, ThreadContex } catch (ParseException e) { throw new RuntimeException(e); } - } catch (IOException e) { + } catch (IOException | URISyntaxException | com.nimbusds.oauth2.sdk.ParseException e) { throw new AuthenticatorUnavailableException("Error while getting " + this.userInfoEndpoint + ": " + e, e); } } @@ -239,8 +228,8 @@ private String validateResponseClaims(JWTClaimsSet claims, String id, boolean is missing = missing.concat("iss"); } if (claims.getAudience() == null - || claims.getAudience().toString().isBlank() - || !claims.getAudience().contains(settings.get(CLIENT_ID))) { + || claims.getAudience().toString().isBlank() + || !claims.getAudience().contains(settings.get(CLIENT_ID))) { missing = missing.concat("aud"); } } diff --git a/src/main/java/com/amazon/dlic/auth/http/jwt/keybyoidc/OpenIdConstants.java b/src/main/java/com/amazon/dlic/auth/http/jwt/keybyoidc/OpenIdConstants.java index c20159eeab..112ec44540 100644 --- a/src/main/java/com/amazon/dlic/auth/http/jwt/keybyoidc/OpenIdConstants.java +++ b/src/main/java/com/amazon/dlic/auth/http/jwt/keybyoidc/OpenIdConstants.java @@ -13,7 +13,6 @@ public class OpenIdConstants { - public static final String APPLICATION_JWT = "application/jwt"; public static final String CLIENT_ID = "client_id"; public static final String ISSUER_ID_URL = "issuer_id_url"; public static final String SUB_CLAIM = "sub"; diff --git a/src/test/java/com/amazon/dlic/auth/http/jwt/keybyoidc/HTTPOpenIdAuthenticatorTests.java b/src/test/java/com/amazon/dlic/auth/http/jwt/keybyoidc/HTTPOpenIdAuthenticatorTests.java index 9d66a99aea..7ba9c023be 100644 --- a/src/test/java/com/amazon/dlic/auth/http/jwt/keybyoidc/HTTPOpenIdAuthenticatorTests.java +++ b/src/test/java/com/amazon/dlic/auth/http/jwt/keybyoidc/HTTPOpenIdAuthenticatorTests.java @@ -24,15 +24,18 @@ import org.opensearch.security.user.AuthCredentials; import org.opensearch.security.util.FakeRestRequest; +import com.nimbusds.common.contenttype.ContentType; +import com.nimbusds.oauth2.sdk.util.StringUtils; + import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.is; -import static com.amazon.dlic.auth.http.jwt.keybyoidc.OpenIdConstants.APPLICATION_JWT; import static com.amazon.dlic.auth.http.jwt.keybyoidc.OpenIdConstants.CLIENT_ID; import static com.amazon.dlic.auth.http.jwt.keybyoidc.OpenIdConstants.ISSUER_ID_URL; import static com.amazon.dlic.auth.http.jwt.keybyoidc.TestJwts.MCCOY_SUBJECT; import static com.amazon.dlic.auth.http.jwt.keybyoidc.TestJwts.OIDC_TEST_AUD; import static com.amazon.dlic.auth.http.jwt.keybyoidc.TestJwts.OIDC_TEST_ISS; import static com.amazon.dlic.auth.http.jwt.keybyoidc.TestJwts.ROLES_CLAIM; +import static com.amazon.dlic.auth.http.jwt.keybyoidc.TestJwts.STEPHEN_SUBJECT; import static org.junit.Assert.assertTrue; import static org.mockito.Mockito.spy; @@ -57,16 +60,16 @@ public static void tearDown() { @Test public void basicTest() throws Exception { Settings settings = Settings.builder() - .put("openid_connect_url", mockIdpServer.getDiscoverUri()) - .put("required_issuer", TestJwts.TEST_ISSUER) - .put("required_audience", TestJwts.TEST_AUDIENCE) - .build(); + .put("openid_connect_url", mockIdpServer.getDiscoverUri()) + .put("required_issuer", TestJwts.TEST_ISSUER) + .put("required_audience", TestJwts.TEST_AUDIENCE) + .build(); HTTPOpenIdAuthenticator openIdAuthenticator = new HTTPOpenIdAuthenticator(settings, null); AuthCredentials creds = openIdAuthenticator.extractCredentials( - new FakeRestRequest(ImmutableMap.of("Authorization", TestJwts.MC_COY_SIGNED_OCT_1), new HashMap<>()).asSecurityRequest(), - null + new FakeRestRequest(ImmutableMap.of("Authorization", TestJwts.MC_COY_SIGNED_OCT_1), new HashMap<>()).asSecurityRequest(), + null ); Assert.assertNotNull(creds); @@ -79,16 +82,16 @@ public void basicTest() throws Exception { @Test public void jwksUriTest() throws Exception { Settings settings = Settings.builder() - .put("jwks_uri", mockIdpServer.getJwksUri()) - .put("required_issuer", TestJwts.TEST_ISSUER) - .put("required_audience", TestJwts.TEST_AUDIENCE) - .build(); + .put("jwks_uri", mockIdpServer.getJwksUri()) + .put("required_issuer", TestJwts.TEST_ISSUER) + .put("required_audience", TestJwts.TEST_AUDIENCE) + .build(); HTTPOpenIdAuthenticator openIdAuthenticator = new HTTPOpenIdAuthenticator(settings, null); AuthCredentials creds = openIdAuthenticator.extractCredentials( - new FakeRestRequest(ImmutableMap.of("Authorization", TestJwts.MC_COY_SIGNED_OCT_2), new HashMap<>()).asSecurityRequest(), - null + new FakeRestRequest(ImmutableMap.of("Authorization", TestJwts.MC_COY_SIGNED_OCT_2), new HashMap<>()).asSecurityRequest(), + null ); Assert.assertNotNull(creds); @@ -101,16 +104,16 @@ public void jwksUriTest() throws Exception { @Test public void jwksMissingRequiredIssuerInClaimTest() throws Exception { Settings settings = Settings.builder() - .put("jwks_uri", mockIdpServer.getJwksUri()) - .put("required_issuer", TestJwts.TEST_ISSUER) - .build(); + .put("jwks_uri", mockIdpServer.getJwksUri()) + .put("required_issuer", TestJwts.TEST_ISSUER) + .build(); HTTPOpenIdAuthenticator openIdAuthenticator = new HTTPOpenIdAuthenticator(settings, null); AuthCredentials creds = openIdAuthenticator.extractCredentials( - new FakeRestRequest(ImmutableMap.of("Authorization", TestJwts.MC_COY_SIGNED_NO_ISSUER_OCT_1), new HashMap<>()) - .asSecurityRequest(), - null + new FakeRestRequest(ImmutableMap.of("Authorization", TestJwts.MC_COY_SIGNED_NO_ISSUER_OCT_1), new HashMap<>()) + .asSecurityRequest(), + null ); Assert.assertNull(creds); @@ -123,8 +126,8 @@ public void jwksNotMatchingRequiredIssuerInClaimTest() throws Exception { HTTPOpenIdAuthenticator openIdAuthenticator = new HTTPOpenIdAuthenticator(settings, null); AuthCredentials creds = openIdAuthenticator.extractCredentials( - new FakeRestRequest(ImmutableMap.of("Authorization", TestJwts.MC_COY_SIGNED_OCT_2), new HashMap<>()).asSecurityRequest(), - null + new FakeRestRequest(ImmutableMap.of("Authorization", TestJwts.MC_COY_SIGNED_OCT_2), new HashMap<>()).asSecurityRequest(), + null ); Assert.assertNull(creds); @@ -133,16 +136,16 @@ public void jwksNotMatchingRequiredIssuerInClaimTest() throws Exception { @Test public void jwksMatchAtLeastOneRequiredAudienceInClaimTest() throws Exception { Settings settings = Settings.builder() - .put("openid_connect_url", mockIdpServer.getDiscoverUri()) - .put("required_issuer", TestJwts.TEST_ISSUER) - .put("required_audience", TestJwts.TEST_AUDIENCE + ",another_audience") - .build(); + .put("openid_connect_url", mockIdpServer.getDiscoverUri()) + .put("required_issuer", TestJwts.TEST_ISSUER) + .put("required_audience", TestJwts.TEST_AUDIENCE + ",another_audience") + .build(); HTTPOpenIdAuthenticator openIdAuthenticator = new HTTPOpenIdAuthenticator(settings, null); AuthCredentials creds = openIdAuthenticator.extractCredentials( - new FakeRestRequest(ImmutableMap.of("Authorization", TestJwts.MC_COY_SIGNED_OCT_1), new HashMap<>()).asSecurityRequest(), - null + new FakeRestRequest(ImmutableMap.of("Authorization", TestJwts.MC_COY_SIGNED_OCT_1), new HashMap<>()).asSecurityRequest(), + null ); Assert.assertNotNull(creds); @@ -155,16 +158,16 @@ public void jwksMatchAtLeastOneRequiredAudienceInClaimTest() throws Exception { @Test public void jwksMissingRequiredAudienceInClaimTest() throws Exception { Settings settings = Settings.builder() - .put("jwks_uri", mockIdpServer.getJwksUri()) - .put("required_audience", TestJwts.TEST_AUDIENCE) - .build(); + .put("jwks_uri", mockIdpServer.getJwksUri()) + .put("required_audience", TestJwts.TEST_AUDIENCE) + .build(); HTTPOpenIdAuthenticator openIdAuthenticator = new HTTPOpenIdAuthenticator(settings, null); AuthCredentials creds = openIdAuthenticator.extractCredentials( - new FakeRestRequest(ImmutableMap.of("Authorization", TestJwts.MC_COY_SIGNED_NO_AUDIENCE_OCT_1), new HashMap<>()) - .asSecurityRequest(), - null + new FakeRestRequest(ImmutableMap.of("Authorization", TestJwts.MC_COY_SIGNED_NO_AUDIENCE_OCT_1), new HashMap<>()) + .asSecurityRequest(), + null ); Assert.assertNull(creds); @@ -173,15 +176,15 @@ public void jwksMissingRequiredAudienceInClaimTest() throws Exception { @Test public void jwksNotMatchingRequiredAudienceInClaimTest() throws Exception { Settings settings = Settings.builder() - .put("jwks_uri", mockIdpServer.getJwksUri()) - .put("required_audience", "Wrong Audience") - .build(); + .put("jwks_uri", mockIdpServer.getJwksUri()) + .put("required_audience", "Wrong Audience") + .build(); HTTPOpenIdAuthenticator openIdAuthenticator = new HTTPOpenIdAuthenticator(settings, null); AuthCredentials creds = openIdAuthenticator.extractCredentials( - new FakeRestRequest(ImmutableMap.of("Authorization", TestJwts.MC_COY_SIGNED_OCT_2), new HashMap<>()).asSecurityRequest(), - null + new FakeRestRequest(ImmutableMap.of("Authorization", TestJwts.MC_COY_SIGNED_OCT_2), new HashMap<>()).asSecurityRequest(), + null ); Assert.assertNull(creds); @@ -192,8 +195,8 @@ public void jwksUriMissingTest() { var exception = Assert.assertThrows(Exception.class, () -> { HTTPOpenIdAuthenticator jwtAuth = new HTTPOpenIdAuthenticator(Settings.builder().build(), null); jwtAuth.extractCredentials( - new FakeRestRequest(ImmutableMap.of("Authorization", TestJwts.MC_COY_SIGNED_OCT_1), new HashMap<>()).asSecurityRequest(), - null + new FakeRestRequest(ImmutableMap.of("Authorization", TestJwts.MC_COY_SIGNED_OCT_1), new HashMap<>()).asSecurityRequest(), + null ); }); @@ -204,19 +207,19 @@ public void jwksUriMissingTest() { @Test public void testEscapeKid() throws Exception { Settings settings = Settings.builder() - .put("openid_connect_url", mockIdpServer.getDiscoverUri()) - .put("required_issuer", TestJwts.TEST_ISSUER) - .put("required_audience", TestJwts.TEST_AUDIENCE) - .build(); + .put("openid_connect_url", mockIdpServer.getDiscoverUri()) + .put("required_issuer", TestJwts.TEST_ISSUER) + .put("required_audience", TestJwts.TEST_AUDIENCE) + .build(); HTTPOpenIdAuthenticator openIdAuthenticator = new HTTPOpenIdAuthenticator(settings, null); AuthCredentials creds = openIdAuthenticator.extractCredentials( - new FakeRestRequest( - ImmutableMap.of("Authorization", "Bearer " + TestJwts.MC_COY_SIGNED_OCT_1_INVALID_KID), - new HashMap() - ).asSecurityRequest(), - null + new FakeRestRequest( + ImmutableMap.of("Authorization", "Bearer " + TestJwts.MC_COY_SIGNED_OCT_1_INVALID_KID), + new HashMap() + ).asSecurityRequest(), + null ); Assert.assertNotNull(creds); @@ -229,17 +232,17 @@ public void testEscapeKid() throws Exception { @Test public void bearerTest() throws Exception { Settings settings = Settings.builder() - .put("openid_connect_url", mockIdpServer.getDiscoverUri()) - .put("required_issuer", TestJwts.TEST_ISSUER) - .put("required_audience", TestJwts.TEST_AUDIENCE) - .build(); + .put("openid_connect_url", mockIdpServer.getDiscoverUri()) + .put("required_issuer", TestJwts.TEST_ISSUER) + .put("required_audience", TestJwts.TEST_AUDIENCE) + .build(); HTTPOpenIdAuthenticator openIdAuthenticator = new HTTPOpenIdAuthenticator(settings, null); AuthCredentials creds = openIdAuthenticator.extractCredentials( - new FakeRestRequest(ImmutableMap.of("Authorization", "Bearer " + TestJwts.MC_COY_SIGNED_OCT_1), new HashMap()) - .asSecurityRequest(), - null + new FakeRestRequest(ImmutableMap.of("Authorization", "Bearer " + TestJwts.MC_COY_SIGNED_OCT_1), new HashMap()) + .asSecurityRequest(), + null ); Assert.assertNotNull(creds); @@ -252,18 +255,18 @@ public void bearerTest() throws Exception { @Test public void testRoles() throws Exception { Settings settings = Settings.builder() - .put("openid_connect_url", mockIdpServer.getDiscoverUri()) - .put("roles_key", ROLES_CLAIM) - .put("required_issuer", TestJwts.TEST_ISSUER) - .put("required_audience", TestJwts.TEST_AUDIENCE) - .build(); + .put("openid_connect_url", mockIdpServer.getDiscoverUri()) + .put("roles_key", ROLES_CLAIM) + .put("required_issuer", TestJwts.TEST_ISSUER) + .put("required_audience", TestJwts.TEST_AUDIENCE) + .build(); HTTPOpenIdAuthenticator openIdAuthenticator = new HTTPOpenIdAuthenticator(settings, null); AuthCredentials creds = openIdAuthenticator.extractCredentials( - new FakeRestRequest(ImmutableMap.of("Authorization", TestJwts.MC_COY_SIGNED_OCT_1), new HashMap()) - .asSecurityRequest(), - null + new FakeRestRequest(ImmutableMap.of("Authorization", TestJwts.MC_COY_SIGNED_OCT_1), new HashMap()) + .asSecurityRequest(), + null ); Assert.assertNotNull(creds); @@ -278,9 +281,9 @@ public void testExp() throws Exception { HTTPOpenIdAuthenticator openIdAuthenticator = new HTTPOpenIdAuthenticator(settings, null); AuthCredentials creds = openIdAuthenticator.extractCredentials( - new FakeRestRequest(ImmutableMap.of("Authorization", "Bearer " + TestJwts.MC_COY_EXPIRED_SIGNED_OCT_1), new HashMap<>()) - .asSecurityRequest(), - null + new FakeRestRequest(ImmutableMap.of("Authorization", "Bearer " + TestJwts.MC_COY_EXPIRED_SIGNED_OCT_1), new HashMap<>()) + .asSecurityRequest(), + null ); Assert.assertNull(creds); @@ -289,11 +292,11 @@ public void testExp() throws Exception { @Test public void testExpInSkew() throws Exception { Settings settings = Settings.builder() - .put("openid_connect_url", mockIdpServer.getDiscoverUri()) - .put("jwt_clock_skew_tolerance_seconds", "10") - .put("required_issuer", TestJwts.TEST_ISSUER) - .put("required_audience", TestJwts.TEST_AUDIENCE) - .build(); + .put("openid_connect_url", mockIdpServer.getDiscoverUri()) + .put("jwt_clock_skew_tolerance_seconds", "10") + .put("required_issuer", TestJwts.TEST_ISSUER) + .put("required_audience", TestJwts.TEST_AUDIENCE) + .build(); HTTPOpenIdAuthenticator openIdAuthenticator = new HTTPOpenIdAuthenticator(settings, null); @@ -301,11 +304,11 @@ public void testExpInSkew() throws Exception { long notBeforeDate = System.currentTimeMillis() / 1000 - 25; AuthCredentials creds = openIdAuthenticator.extractCredentials( - new FakeRestRequest( - ImmutableMap.of("Authorization", "Bearer " + TestJwts.createMcCoySignedOct1(notBeforeDate, expiringDate)), - new HashMap<>() - ).asSecurityRequest(), - null + new FakeRestRequest( + ImmutableMap.of("Authorization", "Bearer " + TestJwts.createMcCoySignedOct1(notBeforeDate, expiringDate)), + new HashMap<>() + ).asSecurityRequest(), + null ); Assert.assertNotNull(creds); @@ -314,11 +317,11 @@ public void testExpInSkew() throws Exception { @Test public void testNbf() throws Exception { Settings settings = Settings.builder() - .put("openid_connect_url", mockIdpServer.getDiscoverUri()) - .put("jwt_clock_skew_tolerance_seconds", "0") - .put("required_issuer", TestJwts.TEST_ISSUER) - .put("required_audience", TestJwts.TEST_AUDIENCE) - .build(); + .put("openid_connect_url", mockIdpServer.getDiscoverUri()) + .put("jwt_clock_skew_tolerance_seconds", "0") + .put("required_issuer", TestJwts.TEST_ISSUER) + .put("required_audience", TestJwts.TEST_AUDIENCE) + .build(); HTTPOpenIdAuthenticator openIdAuthenticator = new HTTPOpenIdAuthenticator(settings, null); @@ -326,11 +329,11 @@ public void testNbf() throws Exception { long notBeforeDate = 5 + System.currentTimeMillis() / 1000; AuthCredentials creds = openIdAuthenticator.extractCredentials( - new FakeRestRequest( - ImmutableMap.of("Authorization", "Bearer " + TestJwts.createMcCoySignedOct1(notBeforeDate, expiringDate)), - new HashMap<>() - ).asSecurityRequest(), - null + new FakeRestRequest( + ImmutableMap.of("Authorization", "Bearer " + TestJwts.createMcCoySignedOct1(notBeforeDate, expiringDate)), + new HashMap<>() + ).asSecurityRequest(), + null ); Assert.assertNull(creds); @@ -339,11 +342,11 @@ public void testNbf() throws Exception { @Test public void testNbfInSkew() throws Exception { Settings settings = Settings.builder() - .put("openid_connect_url", mockIdpServer.getDiscoverUri()) - .put("jwt_clock_skew_tolerance_seconds", "10") - .put("required_issuer", TestJwts.TEST_ISSUER) - .put("required_audience", TestJwts.TEST_AUDIENCE) - .build(); + .put("openid_connect_url", mockIdpServer.getDiscoverUri()) + .put("jwt_clock_skew_tolerance_seconds", "10") + .put("required_issuer", TestJwts.TEST_ISSUER) + .put("required_audience", TestJwts.TEST_AUDIENCE) + .build(); HTTPOpenIdAuthenticator openIdAuthenticator = new HTTPOpenIdAuthenticator(settings, null); @@ -351,11 +354,11 @@ public void testNbfInSkew() throws Exception { long notBeforeDate = 5 + System.currentTimeMillis() / 1000; AuthCredentials creds = openIdAuthenticator.extractCredentials( - new FakeRestRequest( - ImmutableMap.of("Authorization", "Bearer " + TestJwts.createMcCoySignedOct1(notBeforeDate, expiringDate)), - new HashMap<>() - ).asSecurityRequest(), - null + new FakeRestRequest( + ImmutableMap.of("Authorization", "Bearer " + TestJwts.createMcCoySignedOct1(notBeforeDate, expiringDate)), + new HashMap<>() + ).asSecurityRequest(), + null ); Assert.assertNotNull(creds); @@ -365,16 +368,16 @@ public void testNbfInSkew() throws Exception { public void testRS256() throws Exception { Settings settings = Settings.builder() - .put("openid_connect_url", mockIdpServer.getDiscoverUri()) - .put("required_issuer", TestJwts.TEST_ISSUER) - .put("required_audience", TestJwts.TEST_AUDIENCE) - .build(); + .put("openid_connect_url", mockIdpServer.getDiscoverUri()) + .put("required_issuer", TestJwts.TEST_ISSUER) + .put("required_audience", TestJwts.TEST_AUDIENCE) + .build(); HTTPOpenIdAuthenticator openIdAuthenticator = new HTTPOpenIdAuthenticator(settings, null); AuthCredentials creds = openIdAuthenticator.extractCredentials( - new FakeRestRequest(ImmutableMap.of("Authorization", TestJwts.MC_COY_SIGNED_RSA_1), new HashMap<>()).asSecurityRequest(), - null + new FakeRestRequest(ImmutableMap.of("Authorization", TestJwts.MC_COY_SIGNED_RSA_1), new HashMap<>()).asSecurityRequest(), + null ); Assert.assertNotNull(creds); @@ -392,8 +395,8 @@ public void testBadSignature() throws Exception { HTTPOpenIdAuthenticator openIdAuthenticator = new HTTPOpenIdAuthenticator(settings, null); AuthCredentials creds = openIdAuthenticator.extractCredentials( - new FakeRestRequest(ImmutableMap.of("Authorization", TestJwts.MC_COY_SIGNED_RSA_X), new HashMap<>()).asSecurityRequest(), - null + new FakeRestRequest(ImmutableMap.of("Authorization", TestJwts.MC_COY_SIGNED_RSA_X), new HashMap<>()).asSecurityRequest(), + null ); Assert.assertNull(creds); @@ -402,17 +405,17 @@ public void testBadSignature() throws Exception { @Test public void testPeculiarJsonEscaping() throws Exception { Settings settings = Settings.builder() - .put("openid_connect_url", mockIdpServer.getDiscoverUri()) - .put("required_issuer", TestJwts.TEST_ISSUER) - .put("required_audience", TestJwts.TEST_AUDIENCE) - .build(); + .put("openid_connect_url", mockIdpServer.getDiscoverUri()) + .put("required_issuer", TestJwts.TEST_ISSUER) + .put("required_audience", TestJwts.TEST_AUDIENCE) + .build(); HTTPOpenIdAuthenticator openIdAuthenticator = new HTTPOpenIdAuthenticator(settings, null); AuthCredentials creds = openIdAuthenticator.extractCredentials( - new FakeRestRequest(ImmutableMap.of("Authorization", TestJwts.PeculiarEscaping.MC_COY_SIGNED_RSA_1), new HashMap<>()) - .asSecurityRequest(), - null + new FakeRestRequest(ImmutableMap.of("Authorization", TestJwts.PeculiarEscaping.MC_COY_SIGNED_RSA_1), new HashMap<>()) + .asSecurityRequest(), + null ); Assert.assertNotNull(creds); @@ -424,22 +427,12 @@ public void testPeculiarJsonEscaping() throws Exception { @Test public void userinfoEndpointReturnsJwtWithAllRequirementsTest() throws Exception { - Settings settings = Settings.builder() - .put("openid_connect_url", mockIdpServer.getDiscoverUri()) - .put("userinfo_endpoint", mockIdpServer.getUserinfoSignedUri()) - .put(CLIENT_ID, OIDC_TEST_AUD) - .put(ISSUER_ID_URL, OIDC_TEST_ISS) - .build(); + Settings settings = userInfoSettingsBuilder(mockIdpServer.getUserinfoSignedUri(), OIDC_TEST_AUD, OIDC_TEST_ISS, null, null); HTTPOpenIdAuthenticator openIdAuthenticator = spy(new HTTPOpenIdAuthenticator(settings, null)); - AuthCredentials creds = openIdAuthenticator.extractCredentials( - new FakeRestRequest( - ImmutableMap.of("Authorization", "Bearer " + TestJwts.MC_COY_SIGNED_OCT_1, "Content-Type", APPLICATION_JWT), - new HashMap<>() - ).asSecurityRequest(), - null - ); + AuthCredentials creds = fetchCreds(openIdAuthenticator, TestJwts.MC_COY_SIGNED_OCT_1, ContentType.APPLICATION_JWT.toString()); + Assert.assertNotNull(creds); assertThat(creds.getUsername(), is(MCCOY_SUBJECT)); assertThat(creds.getBackendRoles().size(), is(0)); @@ -450,27 +443,20 @@ public void userinfoEndpointReturnsJwtWithAllRequirementsTest() throws Exception public void userinfoEndpointReturnsJwtWithRequiredAudIssFailsTest() throws Exception { // Setting a required issuer or audience // alongside userinfo endpoint settings causes // failures in signed response cases - Settings settings = Settings.builder() - .put("openid_connect_url", mockIdpServer.getDiscoverUri()) - .put("userinfo_endpoint", mockIdpServer.getUserinfoSignedUri()) - .put(CLIENT_ID, OIDC_TEST_AUD) - .put(ISSUER_ID_URL, OIDC_TEST_ISS) - .put("required_issuer", TestJwts.TEST_ISSUER) - .put("required_audience", TestJwts.TEST_AUDIENCE) - .build(); + Settings settings = userInfoSettingsBuilder( + mockIdpServer.getUserinfoSignedUri(), + OIDC_TEST_AUD, + OIDC_TEST_ISS, + TestJwts.TEST_ISSUER, + TestJwts.TEST_AUDIENCE + ); HTTPOpenIdAuthenticator openIdAuthenticator = new HTTPOpenIdAuthenticator(settings, null); AuthCredentials creds = null; String message = ""; try { - creds = openIdAuthenticator.extractCredentials( - new FakeRestRequest( - ImmutableMap.of("Authorization", "Bearer " + TestJwts.MC_COY_SIGNED_OCT_1, "Content-Type", APPLICATION_JWT), - new HashMap<>() - ).asSecurityRequest(), - null - ); + creds = fetchCreds(openIdAuthenticator, TestJwts.MC_COY_SIGNED_OCT_1, ContentType.APPLICATION_JWT.toString()); } catch (RuntimeException e) { message = e.getMessage(); } @@ -480,24 +466,19 @@ public void userinfoEndpointReturnsJwtWithRequiredAudIssFailsTest() throws Excep @Test public void userinfoEndpointReturnsJwtWithMatchingRequiredAudIssPassesTest() throws Exception { - Settings settings = Settings.builder() - .put("openid_connect_url", mockIdpServer.getDiscoverUri()) - .put("userinfo_endpoint", mockIdpServer.getUserinfoSignedUri()) - .put(CLIENT_ID, OIDC_TEST_AUD) - .put(ISSUER_ID_URL, OIDC_TEST_ISS) - .put("required_issuer", OIDC_TEST_ISS) - .put("required_audience", OIDC_TEST_AUD) - .build(); + + Settings settings = userInfoSettingsBuilder( + mockIdpServer.getUserinfoSignedUri(), + OIDC_TEST_AUD, + OIDC_TEST_ISS, + OIDC_TEST_AUD, + OIDC_TEST_ISS + ); HTTPOpenIdAuthenticator openIdAuthenticator = new HTTPOpenIdAuthenticator(settings, null); - AuthCredentials creds = openIdAuthenticator.extractCredentials( - new FakeRestRequest( - ImmutableMap.of("Authorization", "Bearer " + TestJwts.MC_COY_SIGNED_OCT_1_OIDC, "Content-Type", APPLICATION_JWT), - new HashMap<>() - ).asSecurityRequest(), - null - ); + AuthCredentials creds = fetchCreds(openIdAuthenticator, TestJwts.MC_COY_SIGNED_OCT_1_OIDC, ContentType.APPLICATION_JWT.toString()); + Assert.assertNotNull(creds); assertThat(creds.getUsername(), is(MCCOY_SUBJECT)); assertThat(creds.getBackendRoles().size(), is(0)); @@ -506,25 +487,20 @@ public void userinfoEndpointReturnsJwtWithMatchingRequiredAudIssPassesTest() thr @Test public void userinfoEndpointReturnsJwtMissingIssuerTest() throws Exception { - Settings settings = Settings.builder() - .put("openid_connect_url", mockIdpServer.getDiscoverUri()) - .put("userinfo_endpoint", mockIdpServer.getUserinfoSignedUri()) - .put(CLIENT_ID, OIDC_TEST_AUD) - .put(ISSUER_ID_URL, "http://www.differentexample.com") - .build(); + Settings settings = userInfoSettingsBuilder( + mockIdpServer.getUserinfoSignedUri(), + OIDC_TEST_AUD, + "http://www.differentexample.com", + null, + null + ); HTTPOpenIdAuthenticator openIdAuthenticator = new HTTPOpenIdAuthenticator(settings, null); AuthCredentials creds = null; String message = ""; try { - creds = openIdAuthenticator.extractCredentials( - new FakeRestRequest( - ImmutableMap.of("Authorization", "Bearer " + TestJwts.MC_COY_SIGNED_OCT_1, "Content-Type", APPLICATION_JWT), - new HashMap<>() - ).asSecurityRequest(), - null - ); + creds = fetchCreds(openIdAuthenticator, TestJwts.MC_COY_SIGNED_OCT_1, ContentType.APPLICATION_JWT.toString()); } catch (AuthenticatorUnavailableException e) { message = e.getMessage(); } @@ -534,25 +510,20 @@ public void userinfoEndpointReturnsJwtMissingIssuerTest() throws Exception { @Test public void userinfoEndpointReturnsJwtMissingAudienceTest() throws Exception { - Settings settings = Settings.builder() - .put("openid_connect_url", mockIdpServer.getDiscoverUri()) - .put("userinfo_endpoint", mockIdpServer.getUserinfoSignedUri()) - .put(CLIENT_ID, "aDifferentTestClient") - .put(ISSUER_ID_URL, "http://www.example.com") - .build(); + Settings settings = userInfoSettingsBuilder( + mockIdpServer.getUserinfoSignedUri(), + "aDifferentTestClient", + OIDC_TEST_ISS, + null, + null + ); HTTPOpenIdAuthenticator openIdAuthenticator = new HTTPOpenIdAuthenticator(settings, null); AuthCredentials creds = null; String message = ""; try { - creds = openIdAuthenticator.extractCredentials( - new FakeRestRequest( - ImmutableMap.of("Authorization", "Bearer " + TestJwts.MC_COY_SIGNED_OCT_1, "Content-Type", APPLICATION_JWT), - new HashMap<>() - ).asSecurityRequest(), - null - ); + creds = fetchCreds(openIdAuthenticator, TestJwts.MC_COY_SIGNED_OCT_1, ContentType.APPLICATION_JWT.toString()); } catch (AuthenticatorUnavailableException e) { message = e.getMessage(); } @@ -562,24 +533,14 @@ public void userinfoEndpointReturnsJwtMissingAudienceTest() throws Exception { @Test public void userinfoEndpointReturnsJwtMismatchedSubTest() throws Exception { - Settings settings = Settings.builder() - .put("openid_connect_url", mockIdpServer.getDiscoverUri()) - .put("userinfo_endpoint", mockIdpServer.getUserinfoSignedUri()) - .put(CLIENT_ID, "testClient") - .put(ISSUER_ID_URL, "http://www.example.com") - .build(); + + Settings settings = userInfoSettingsBuilder(mockIdpServer.getUserinfoSignedUri(), OIDC_TEST_AUD, OIDC_TEST_ISS, null, null); HTTPOpenIdAuthenticator openIdAuthenticator = new HTTPOpenIdAuthenticator(settings, null); AuthCredentials creds = null; String message = ""; try { - creds = openIdAuthenticator.extractCredentials( - new FakeRestRequest( - ImmutableMap.of("Authorization", "Bearer " + TestJwts.STEPHEN_RSA_1, "Content-Type", APPLICATION_JWT), - new HashMap<>() - ).asSecurityRequest(), - null - ); + creds = fetchCreds(openIdAuthenticator, TestJwts.STEPHEN_RSA_1, ContentType.APPLICATION_JWT.toString()); } catch (AuthenticatorUnavailableException e) { message = e.getMessage(); } @@ -589,22 +550,13 @@ public void userinfoEndpointReturnsJwtMismatchedSubTest() throws Exception { @Test public void userinfoEndpointReturnsJsonWithAllRequirementsTest() throws Exception { - Settings settings = Settings.builder() - .put("openid_connect_url", mockIdpServer.getDiscoverUri()) - .put("userinfo_endpoint", mockIdpServer.getUserinfoUri()) - .put(CLIENT_ID, "testClient") - .put(ISSUER_ID_URL, "http://www.example.com") - .build(); + + Settings settings = userInfoSettingsBuilder(mockIdpServer.getUserinfoUri(), OIDC_TEST_AUD, OIDC_TEST_ISS, null, null); HTTPOpenIdAuthenticator openIdAuthenticator = spy(new HTTPOpenIdAuthenticator(settings, null)); - AuthCredentials creds = openIdAuthenticator.extractCredentials( - new FakeRestRequest( - ImmutableMap.of("Authorization", "Bearer " + TestJwts.MC_COY_SIGNED_OCT_1, "Content-Type", APPLICATION_JWT), - new HashMap<>() - ).asSecurityRequest(), - null - ); + AuthCredentials creds = fetchCreds(openIdAuthenticator, TestJwts.MC_COY_SIGNED_OCT_1, ContentType.APPLICATION_JWT.toString()); + Assert.assertNotNull(creds); assertThat(creds.getUsername(), is(MCCOY_SUBJECT)); assertThat(creds.getBackendRoles().size(), is(0)); @@ -613,24 +565,13 @@ public void userinfoEndpointReturnsJsonWithAllRequirementsTest() throws Exceptio @Test public void userinfoEndpointReturnsJsonMismatchedSubTest() throws Exception { - Settings settings = Settings.builder() - .put("openid_connect_url", mockIdpServer.getDiscoverUri()) - .put("userinfo_endpoint", mockIdpServer.getUserinfoUri()) - .put(CLIENT_ID, "testClient") - .put(ISSUER_ID_URL, "http://www.example.com") - .build(); + Settings settings = userInfoSettingsBuilder(mockIdpServer.getUserinfoUri(), OIDC_TEST_AUD, OIDC_TEST_ISS, null, null); HTTPOpenIdAuthenticator openIdAuthenticator = new HTTPOpenIdAuthenticator(settings, null); AuthCredentials creds = null; String message = ""; try { - creds = openIdAuthenticator.extractCredentials( - new FakeRestRequest( - ImmutableMap.of("Authorization", "Bearer " + TestJwts.STEPHEN_RSA_1, "Content-Type", APPLICATION_JWT), - new HashMap<>() - ).asSecurityRequest(), - null - ); + creds = fetchCreds(openIdAuthenticator, TestJwts.STEPHEN_RSA_1, ContentType.APPLICATION_JWT.toString()); } catch (AuthenticatorUnavailableException e) { message = e.getMessage(); } @@ -640,24 +581,19 @@ public void userinfoEndpointReturnsJsonMismatchedSubTest() throws Exception { @Test public void userinfoEndpointReturnsResponseNot2xxTest() throws Exception { - Settings settings = Settings.builder() - .put("openid_connect_url", mockIdpServer.getDiscoverUri()) - .put("userinfo_endpoint", mockIdpServer.getUserinfoUri()) - .put("required_issuer", TestJwts.TEST_ISSUER) - .put("required_audience", TestJwts.TEST_AUDIENCE + ",another_audience") - .build(); + Settings settings = userInfoSettingsBuilder( + mockIdpServer.getBadUserInfoUri(), + "", + "", + TestJwts.TEST_ISSUER, + TestJwts.TEST_AUDIENCE + ",another_audience" + ); HTTPOpenIdAuthenticator openIdAuthenticator = new HTTPOpenIdAuthenticator(settings, null); AuthCredentials creds = null; String message = ""; try { - creds = openIdAuthenticator.extractCredentials( - new FakeRestRequest( - ImmutableMap.of("Authorization", TestJwts.MC_COY_SIGNED_OCT_1, "Content-Type", APPLICATION_JWT), - new HashMap<>() - ).asSecurityRequest(), - null - ); + creds = fetchCreds(openIdAuthenticator, STEPHEN_SUBJECT, ContentType.APPLICATION_JWT.toString()); } catch (AuthenticatorUnavailableException e) { message = e.getMessage(); } @@ -667,27 +603,48 @@ public void userinfoEndpointReturnsResponseNot2xxTest() throws Exception { @Test public void userinfoEndpointReturnsJsonWithRequiredAudIssPassesTest() throws Exception { - Settings settings = Settings.builder() - .put("openid_connect_url", mockIdpServer.getDiscoverUri()) - .put("userinfo_endpoint", mockIdpServer.getUserinfoUri()) - .put(CLIENT_ID, "testClient") - .put(ISSUER_ID_URL, "http://www.example.com") - .put("required_issuer", TestJwts.TEST_ISSUER) - .put("required_audience", TestJwts.TEST_AUDIENCE) - .build(); + Settings settings = userInfoSettingsBuilder( + mockIdpServer.getUserinfoUri(), + OIDC_TEST_AUD, + OIDC_TEST_ISS, + TestJwts.TEST_AUDIENCE, + TestJwts.TEST_ISSUER + ); HTTPOpenIdAuthenticator openIdAuthenticator = new HTTPOpenIdAuthenticator(settings, null); - AuthCredentials creds = openIdAuthenticator.extractCredentials( - new FakeRestRequest( - ImmutableMap.of("Authorization", "Bearer " + TestJwts.MC_COY_SIGNED_OCT_1, "Content-Type", APPLICATION_JWT), - new HashMap<>() - ).asSecurityRequest(), - null - ); + AuthCredentials creds = fetchCreds(openIdAuthenticator, TestJwts.MC_COY_SIGNED_OCT_1, ContentType.APPLICATION_JWT.toString()); + Assert.assertNotNull(creds); assertThat(creds.getUsername(), is(MCCOY_SUBJECT)); assertThat(creds.getBackendRoles().size(), is(0)); assertThat(creds.getAttributes().size(), is(2)); } + + AuthCredentials fetchCreds(HTTPOpenIdAuthenticator openIdAuthenticator, String bearer, String contentType) { + return openIdAuthenticator.extractCredentials( + new FakeRestRequest(ImmutableMap.of("Authorization", "Bearer " + bearer, "Content-Type", contentType), new HashMap<>()) + .asSecurityRequest(), + null + ); + } + + Settings userInfoSettingsBuilder(String userInfoEndpoint, String client, String issuer, String requiredAud, String requiredIss) { + + Settings.Builder settings = Settings.builder() + .put("openid_connect_url", mockIdpServer.getDiscoverUri()) + .put("userinfo_endpoint", userInfoEndpoint) + .put(CLIENT_ID, client) + .put(ISSUER_ID_URL, issuer); + + if (StringUtils.isNotBlank(requiredAud)) { + settings.put("required_audience", requiredAud); + } + + if (StringUtils.isNotBlank(requiredIss)) { + settings.put("required_issuer", requiredIss); + } + + return settings.build(); + } } diff --git a/src/test/java/com/amazon/dlic/auth/http/jwt/keybyoidc/MockIpdServer.java b/src/test/java/com/amazon/dlic/auth/http/jwt/keybyoidc/MockIpdServer.java index 3bcbfa7d2d..40c0d7b9e9 100644 --- a/src/test/java/com/amazon/dlic/auth/http/jwt/keybyoidc/MockIpdServer.java +++ b/src/test/java/com/amazon/dlic/auth/http/jwt/keybyoidc/MockIpdServer.java @@ -11,24 +11,9 @@ package com.amazon.dlic.auth.http.jwt.keybyoidc; -import java.io.Closeable; -import java.io.FileInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.net.Socket; -import java.nio.charset.CharsetDecoder; -import java.nio.charset.CharsetEncoder; -import java.security.GeneralSecurityException; -import java.security.KeyStore; -import java.security.cert.Certificate; -import javax.net.ssl.KeyManagerFactory; -import javax.net.ssl.SSLContext; -import javax.net.ssl.SSLException; -import javax.net.ssl.SSLPeerUnverifiedException; -import javax.net.ssl.SSLServerSocket; -import javax.net.ssl.SSLSocket; -import javax.net.ssl.TrustManagerFactory; - +import com.nimbusds.common.contenttype.ContentType; +import com.nimbusds.jose.jwk.JWKSet; +import com.nimbusds.jwt.JWTClaimsSet; import org.apache.http.Header; import org.apache.http.HttpConnectionFactory; import org.apache.http.HttpException; @@ -47,15 +32,27 @@ import org.apache.http.io.HttpMessageWriterFactory; import org.apache.http.protocol.HttpContext; import org.apache.http.protocol.HttpRequestHandler; - import org.opensearch.security.test.helper.file.FileHelper; import org.opensearch.security.test.helper.network.SocketUtils; -import com.nimbusds.jose.jwk.JWKSet; -import com.nimbusds.jwt.JWTClaimsSet; +import java.io.Closeable; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.net.Socket; +import java.nio.charset.CharsetDecoder; +import java.nio.charset.CharsetEncoder; +import java.security.GeneralSecurityException; +import java.security.KeyStore; +import java.security.cert.Certificate; +import javax.net.ssl.KeyManagerFactory; +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLException; +import javax.net.ssl.SSLPeerUnverifiedException; +import javax.net.ssl.SSLServerSocket; +import javax.net.ssl.SSLSocket; +import javax.net.ssl.TrustManagerFactory; -import static org.apache.http.entity.ContentType.APPLICATION_JSON; -import static com.amazon.dlic.auth.http.jwt.keybyoidc.OpenIdConstants.APPLICATION_JWT; import static com.amazon.dlic.auth.http.jwt.keybyoidc.TestJwts.MCCOY_SUBJECT; import static com.amazon.dlic.auth.http.jwt.keybyoidc.TestJwts.TEST_ROLES_STRING; import static com.amazon.dlic.auth.http.jwt.keybyoidc.TestJwts.createSigned; @@ -65,6 +62,7 @@ class MockIpdServer implements Closeable { final static String CTX_KEYS = "/api/oauth/keys"; final static String CTX_USERINFO_SIGNED = "/api/oauth/userinfo/signed"; final static String CTX_USERINFO = "/api/oauth/userinfo"; + final static String CTX_BADUSERINFO = "/api/oauth/userinfo/bad"; private final HttpServer httpServer; private final int port; @@ -114,6 +112,13 @@ public void handle(HttpRequest request, HttpResponse response, HttpContext conte public void handle(HttpRequest request, HttpResponse response, HttpContext context) throws HttpException, IOException { handleUserinfoRequestSigned(request, response, context); } + }) + .registerHandler(CTX_BADUSERINFO, new HttpRequestHandler() { + @Override + public void handle(HttpRequest request, HttpResponse response, HttpContext context) throws HttpException, + IOException { + handleBadUserInfoRequest(request, response, context); + } }); if (ssl) { @@ -180,6 +185,10 @@ public String getUserinfoSignedUri() { return uri + CTX_USERINFO_SIGNED; } + public String getBadUserInfoUri() { + return uri + CTX_BADUSERINFO; + } + public int getPort() { return port; } @@ -210,7 +219,7 @@ protected void handleUserinfoRequestSigned(HttpRequest request, HttpResponse res } response.setStatusCode(200); - response.setHeader("content-type", APPLICATION_JWT); + response.setHeader("content-type", ContentType.APPLICATION_JWT.getType()); // We have to manually form the response content since we don't want to need to pass settings info into the test class JWTClaimsSet claims = new JWTClaimsSet.Builder().claim("sub", MCCOY_SUBJECT) @@ -236,7 +245,7 @@ protected void handleUserinfoRequest(HttpRequest request, HttpResponse response, } response.setStatusCode(200); - response.setHeader("content-type", APPLICATION_JSON.getMimeType()); + response.setHeader("content-type", ContentType.APPLICATION_JSON.getType()); // We have to manually form the response content since we don't want to need to pass settings info into the test class JWTClaimsSet claims = new JWTClaimsSet.Builder().claim("sub", MCCOY_SUBJECT).claim("roles", TEST_ROLES_STRING).build(); @@ -248,6 +257,11 @@ protected void handleKeysRequest(HttpRequest request, HttpResponse response, Htt response.setEntity(new StringEntity(jwks.toString(false))); } + protected void handleBadUserInfoRequest(HttpRequest request, HttpResponse response, HttpContext context) throws HttpException, + IOException { + response.setStatusCode(401); + } + private SSLContext createSSLContext() { if (!this.ssl) { return null;