From 95e4f9c1e60f1c6b6487c4d7a2b5e063922eaa15 Mon Sep 17 00:00:00 2001 From: dhaura Date: Fri, 3 Nov 2023 12:34:08 +0530 Subject: [PATCH 1/2] Improve exception handling and add unit tests --- .../oidc/OpenIDConnectAuthenticator.java | 13 +- .../oidc/util/OIDCErrorConstants.java | 10 +- .../oidc/util/OIDCTokenValidationUtil.java | 10 +- .../oidc/OpenIDConnectAuthenticatorTest.java | 224 ++++++++++++++++-- .../util/OIDCTokenValidationUtilTest.java | 115 ++++++++- pom.xml | 2 +- 6 files changed, 331 insertions(+), 43 deletions(-) diff --git a/components/org.wso2.carbon.identity.application.authenticator.oidc/src/main/java/org/wso2/carbon/identity/application/authenticator/oidc/OpenIDConnectAuthenticator.java b/components/org.wso2.carbon.identity.application.authenticator.oidc/src/main/java/org/wso2/carbon/identity/application/authenticator/oidc/OpenIDConnectAuthenticator.java index d4a834d4..5d369dde 100644 --- a/components/org.wso2.carbon.identity.application.authenticator.oidc/src/main/java/org/wso2/carbon/identity/application/authenticator/oidc/OpenIDConnectAuthenticator.java +++ b/components/org.wso2.carbon.identity.application.authenticator.oidc/src/main/java/org/wso2/carbon/identity/application/authenticator/oidc/OpenIDConnectAuthenticator.java @@ -73,6 +73,7 @@ import org.wso2.carbon.identity.core.util.IdentityTenantUtil; import org.wso2.carbon.identity.core.util.IdentityUtil; import org.wso2.carbon.identity.oauth.common.OAuthConstants; +import org.wso2.carbon.identity.oauth2.IdentityOAuth2ClientException; import org.wso2.carbon.identity.oauth2.IdentityOAuth2Exception; import org.wso2.carbon.idp.mgt.IdentityProviderManagementException; import org.wso2.carbon.idp.mgt.IdentityProviderManager; @@ -746,8 +747,12 @@ protected OAuthClientResponse requestAccessToken(HttpServletRequest request, Aut String accessToken = request.getParameter(ACCESS_TOKEN_PARAM); try { validateJWTToken(context, idToken); - } catch (ParseException | IdentityOAuth2Exception | JOSEException e) { - throw new AuthenticationFailedException("JWT Token validation Failed."); + } catch (ParseException | IdentityOAuth2ClientException | JOSEException e) { + throw new AuthenticationFailedException(ErrorMessages.INVALID_JWT_TOKEN.getCode(), + ErrorMessages.INVALID_JWT_TOKEN.getMessage()); + } catch (IdentityOAuth2Exception e) { + throw new AuthenticationFailedException(ErrorMessages.JWT_TOKEN_VALIDATION_FAILED.getCode(), + ErrorMessages.JWT_TOKEN_VALIDATION_FAILED.getMessage(), e); } NativeSDKBasedFederatedOAuthClientResponse nativeSDKBasedFederatedOAuthClientResponse = new NativeSDKBasedFederatedOAuthClientResponse(); @@ -784,7 +789,7 @@ private void validateJWTToken(AuthenticationContext context, String idToken) thr IdentityProvider identityProvider = getIdentityProvider(idpIdentifier, tenantDomain); OIDCTokenValidationUtil.validateSignature(signedJWT, identityProvider); - OIDCTokenValidationUtil.validateAudience(claimsSet.getAudience(), identityProvider , tenantDomain); + OIDCTokenValidationUtil.validateAudience(claimsSet.getAudience(), identityProvider, tenantDomain); } /** @@ -1645,7 +1650,7 @@ private boolean isTrustedTokenIssuer(AuthenticationContext context) { IdentityProviderProperty[] identityProviderProperties = externalIdentityProvider.getIdpProperties(); for (IdentityProviderProperty identityProviderProperty: identityProviderProperties) { - if (identityProviderProperty.getName().equals(IdPManagementConstants.IS_TRUSTED_TOKEN_ISSUER)) { + if (IdPManagementConstants.IS_TRUSTED_TOKEN_ISSUER.equals(identityProviderProperty.getName())) { return Boolean.parseBoolean(identityProviderProperty.getValue()); } } diff --git a/components/org.wso2.carbon.identity.application.authenticator.oidc/src/main/java/org/wso2/carbon/identity/application/authenticator/oidc/util/OIDCErrorConstants.java b/components/org.wso2.carbon.identity.application.authenticator.oidc/src/main/java/org/wso2/carbon/identity/application/authenticator/oidc/util/OIDCErrorConstants.java index af1e0fa0..d8a3e1c7 100644 --- a/components/org.wso2.carbon.identity.application.authenticator.oidc/src/main/java/org/wso2/carbon/identity/application/authenticator/oidc/util/OIDCErrorConstants.java +++ b/components/org.wso2.carbon.identity.application.authenticator.oidc/src/main/java/org/wso2/carbon/identity/application/authenticator/oidc/util/OIDCErrorConstants.java @@ -40,6 +40,9 @@ public enum ErrorMessages { "Cannot find the userId from the id_token sent by the federated IDP."), NONCE_MISMATCH("OID-60016", "The nonce claim of the ID token is not equal to the nonce value " + "sent in the authentication request"), + INVALID_JWT_TOKEN("OID-60017", "JWT token is invalid."), + JWT_TOKEN_AUD_CLAIM_VALIDATION_FAILED("OID-60018", + "None of the audience values matched the token endpoint alias: %s."), // Federated IdP initiated back-channel logout client errors. LOGOUT_TOKEN_EMPTY_OR_NULL("OID-60006", "Logout token is empty or null. Pass a valid logout token"), @@ -93,10 +96,9 @@ public enum ErrorMessages { LOGOUT_SERVER_EXCEPTION("OID-65015", "Back channel logout failed due to server error"), JWT_TOKEN_ISS_CLAIM_VALIDATION_FAILED( "OID-65016", "Error while validating the iss claim in the jwt token"), - JWT_TOKEN_SIGNATURE_VALIDATION_FAILED("OID-65016", - "Error while validating the JWT token signature"), - JWT_TOKEN_AUD_CLAIM_VALIDATION_FAILED("OID-65017", - "Audience claim validation failed."); + JWT_TOKEN_VALIDATION_FAILED("OID-65016", "JWT token validation Failed."), + JWT_TOKEN_SIGNATURE_VALIDATION_FAILED("OID-65017", + "Error while validating the JWT token signature"); private final String code; private final String message; diff --git a/components/org.wso2.carbon.identity.application.authenticator.oidc/src/main/java/org/wso2/carbon/identity/application/authenticator/oidc/util/OIDCTokenValidationUtil.java b/components/org.wso2.carbon.identity.application.authenticator.oidc/src/main/java/org/wso2/carbon/identity/application/authenticator/oidc/util/OIDCTokenValidationUtil.java index 0e935456..a97a7b59 100644 --- a/components/org.wso2.carbon.identity.application.authenticator.oidc/src/main/java/org/wso2/carbon/identity/application/authenticator/oidc/util/OIDCTokenValidationUtil.java +++ b/components/org.wso2.carbon.identity.application.authenticator.oidc/src/main/java/org/wso2/carbon/identity/application/authenticator/oidc/util/OIDCTokenValidationUtil.java @@ -61,7 +61,9 @@ public static String getIssuer(JWTClaimsSet claimsSet) throws AuthenticationFail * * @param audienceList - list containing audience values. * @param idp - identity provider. - * @Param tenantDomain - the tenant domain + * @param tenantDomain - the tenant domain + * + * @throws AuthenticationFailedException if none of the audience values matched the tokenEndpoint alias */ public static void validateAudience(List audienceList, IdentityProvider idp, String tenantDomain) throws AuthenticationFailedException { @@ -78,8 +80,10 @@ public static void validateAudience(List audienceList, IdentityProvider } } if (!audienceFound) { - throw new AuthenticationFailedException ("None of the audience values matched the tokenEndpoint Alias " - + tokenEndPointAlias); + throw new AuthenticationFailedException ( + OIDCErrorConstants.ErrorMessages.JWT_TOKEN_AUD_CLAIM_VALIDATION_FAILED.getCode(), + String.format(OIDCErrorConstants.ErrorMessages.JWT_TOKEN_AUD_CLAIM_VALIDATION_FAILED.getMessage(), + tokenEndPointAlias)); } } diff --git a/components/org.wso2.carbon.identity.application.authenticator.oidc/src/test/java/org/wso2/carbon/identity/application/authenticator/oidc/OpenIDConnectAuthenticatorTest.java b/components/org.wso2.carbon.identity.application.authenticator.oidc/src/test/java/org/wso2/carbon/identity/application/authenticator/oidc/OpenIDConnectAuthenticatorTest.java index 7c529c66..ecdc92e2 100644 --- a/components/org.wso2.carbon.identity.application.authenticator.oidc/src/test/java/org/wso2/carbon/identity/application/authenticator/oidc/OpenIDConnectAuthenticatorTest.java +++ b/components/org.wso2.carbon.identity.application.authenticator.oidc/src/test/java/org/wso2/carbon/identity/application/authenticator/oidc/OpenIDConnectAuthenticatorTest.java @@ -18,6 +18,9 @@ package org.wso2.carbon.identity.application.authenticator.oidc; +import com.nimbusds.jose.JOSEException; +import com.nimbusds.jwt.JWTClaimsSet; +import com.nimbusds.jwt.SignedJWT; import org.apache.commons.io.IOUtils; import org.apache.commons.lang.StringUtils; import org.apache.commons.logging.LogFactory; @@ -31,6 +34,7 @@ import org.mockito.Matchers; import org.mockito.Mock; import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.core.classloader.annotations.SuppressStaticInitializationFor; import org.powermock.modules.testng.PowerMockTestCase; import org.powermock.reflect.Whitebox; import org.testng.Assert; @@ -48,6 +52,7 @@ import org.wso2.carbon.identity.application.authentication.framework.util.FrameworkConstants; import org.wso2.carbon.identity.application.authentication.framework.util.FrameworkUtils; import org.wso2.carbon.identity.application.authenticator.oidc.internal.OpenIDConnectAuthenticatorDataHolder; +import org.wso2.carbon.identity.application.authenticator.oidc.util.OIDCTokenValidationUtil; import org.wso2.carbon.identity.application.common.model.ClaimMapping; import org.wso2.carbon.identity.application.common.model.IdentityProvider; import org.wso2.carbon.identity.application.common.model.IdentityProviderProperty; @@ -58,6 +63,8 @@ import org.wso2.carbon.identity.core.ServiceURLBuilder; import org.wso2.carbon.identity.core.util.IdentityCoreConstants; import org.wso2.carbon.identity.core.util.IdentityUtil; +import org.wso2.carbon.identity.oauth2.IdentityOAuth2Exception; +import org.wso2.carbon.idp.mgt.IdentityProviderManager; import org.wso2.carbon.idp.mgt.util.IdPManagementConstants; import org.wso2.carbon.user.api.RealmConfiguration; import org.wso2.carbon.user.api.UserStoreException; @@ -71,16 +78,20 @@ import java.net.HttpURLConnection; import java.net.URL; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.Optional; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; -import static org.mockito.Matchers.any; -import static org.mockito.Matchers.anyInt; -import static org.mockito.Matchers.anyString; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.spy; +import static org.powermock.api.mockito.PowerMockito.doNothing; import static org.powermock.api.mockito.PowerMockito.doReturn; +import static org.powermock.api.mockito.PowerMockito.doThrow; import static org.powermock.api.mockito.PowerMockito.mock; import static org.powermock.api.mockito.PowerMockito.mockStatic; import static org.powermock.api.mockito.PowerMockito.when; @@ -102,7 +113,9 @@ @PrepareForTest({LogFactory.class, OAuthClient.class, URL.class, FrameworkUtils.class, OpenIDConnectAuthenticatorDataHolder.class, OAuthAuthzResponse.class, OAuthClientRequest.class, OAuthClientResponse.class, IdentityUtil.class, OpenIDConnectAuthenticator.class, ServiceURLBuilder.class, - LoggerUtils.class}) + LoggerUtils.class, OIDCTokenValidationUtil.class, IdentityProviderManager.class}) +@SuppressStaticInitializationFor({"org.wso2.carbon.idp.mgt.IdentityProviderManager", + "org.wso2.carbon.identity.application.authentication.framework.exception.AuthenticationFailedException"}) public class OpenIDConnectAuthenticatorTest extends PowerMockTestCase { @Mock @@ -156,6 +169,9 @@ public class OpenIDConnectAuthenticatorTest extends PowerMockTestCase { @Mock private IdentityProvider identityProvider; + @Mock + private IdentityProviderManager identityProviderManager; + @Mock private OpenIDConnectAuthenticatorDataHolder openIDConnectAuthenticatorDataHolder; @@ -169,7 +185,7 @@ public class OpenIDConnectAuthenticatorTest extends PowerMockTestCase { private static Map authenticatorProperties; private static Map authenticatorParamProperties; - private static String clientId = "DpPQt4KnvcehtUuQ0Jf6i0gl0E0a"; + private static String clientId = "u5FIfG5xzLvBGiamoAYzzcqpBqga"; private static String accessToken = "4952b467-86b2-31df-b63c-0bf25cec4f86s"; private static String idToken = "eyJ4NXQiOiJOVEF4Wm1NeE5ETXlaRGczTVRVMVpHTTBNekV6T0RKaFpXSTRORE5" + "sWkRVMU9HRmtOakZpTVEiLCJraWQiOiJOVEF4Wm1NeE5ETXlaRGczTVRVMVpHTTBNekV6T0RKaFpXSTRORE5sWkRVMU9" + @@ -181,6 +197,7 @@ public class OpenIDConnectAuthenticatorTest extends PowerMockTestCase { "oZxDH7JbIkPpKBe0cnYQWBxfHuGTUWhvnu629ek6v2YLkaHlb_Lm04xLD9FNxuZUNQFw83pQtDVpoX5r1V-F0DdUc7gA1RKN3" + "xMVYgRyfslRDveGYplxVVNQ1LU3lrZhgaTfcMEsC6rdbd1HjdzG71EPS4674HCSAUelOisNKGa2NgORpldDQsj376QD0G9Mhc8WtW" + "oguftrCCGjBy1kKT4VqFLOqlA-8wUhOj_rZT9SUIBQRDPu0RZobvsskqYo40GEZrUoa"; + private static String invalidIdToken = "invalid_id_token"; private static String sessionDataKey = "7b1c8131-c6bd-4682-892e-1a948a9e57e8"; private static String nonce = "0ed8f1b3-e83f-46c0-8d52-f0d2e7925f98"; private static String invalidNonce = "7ed8f1b3-e83f-46c0-8d52-f0d2e7925f98"; @@ -188,6 +205,7 @@ public class OpenIDConnectAuthenticatorTest extends PowerMockTestCase { "response_type=code&redirect_uri=https%3A%2F%2Flocalhost%3A9443%2Fcommonauth&" + "state=958e9049-8cd2-4580-8745-6679ac8d33f6%2COIDC&nonce=0ed8f1b3-e83f-46c0-8d52-f0d2e7925f98&" + "client_id=sample.client-id"; + private static String superTenantDomain = "carbon.super"; private static OAuthClientResponse token; private Map paramValueMap; private int TENANT_ID = 1234; @@ -203,7 +221,7 @@ public void init() { "scope=openid&state=OIDC&loginType=basic"); authenticatorProperties.put(IdentityApplicationConstants.Authenticator.OIDC.SCOPES, "openid email profile"); authenticatorProperties.put("UserInfoUrl", "https://localhost:9443/oauth2/userinfo"); - authenticatorProperties.put(OIDCAuthenticatorConstants.CLIENT_ID, "u5FIfG5xzLvBGiamoAYzzcqpBqga"); + authenticatorProperties.put(OIDCAuthenticatorConstants.CLIENT_ID, clientId); authenticatorProperties.put(OIDCAuthenticatorConstants.CLIENT_SECRET, "_kLtobqi08GytnypVW_Mmy1niAIa"); authenticatorProperties.put( OIDCAuthenticatorConstants.OAUTH2_TOKEN_URL, "https://localhost:9443/oauth2/token"); @@ -428,7 +446,7 @@ public void testGetQueryStringWithMultipleAuthenticatorParam() throws Exception "login_hint=$authparam{username}&domain=$authparam{fidp}"), "login_hint=testUser&domain=google"); } - @Test(expectedExceptions = AuthenticationFailedException.class) + @Test public void testInitiateAuthenticationRequestNullProperties() throws OAuthSystemException, OAuthProblemException, AuthenticationFailedException, UserStoreException { @@ -436,8 +454,12 @@ public void testInitiateAuthenticationRequestNullProperties() throws OAuthSystem when(LoggerUtils.isDiagnosticLogsEnabled()).thenReturn(true); mockAuthenticationRequestContext(mockAuthenticationContext); when(mockAuthenticationContext.getAuthenticatorProperties()).thenReturn(null); - openIDConnectAuthenticator.initiateAuthenticationRequest(mockServletRequest, mockServletResponse, - mockAuthenticationContext); + + Assert.assertThrows( + AuthenticationFailedException.class, + () -> openIDConnectAuthenticator.initiateAuthenticationRequest(mockServletRequest, mockServletResponse, + mockAuthenticationContext) + ); } @Test @@ -503,14 +525,18 @@ public void testPassProcessAuthenticationResponseWithNonce() throws Exception { "Invalid Id token in the authentication context."); } - @Test(expectedExceptions = AuthenticationFailedException.class) + @Test public void testPassProcessAuthenticationResponseWithoutAccessToken() throws Exception { setupTest(); // Empty access token and id token setParametersForOAuthClientResponse(mockOAuthClientResponse, "", ""); - openIDConnectAuthenticator.processAuthenticationResponse(mockServletRequest, - mockServletResponse, mockAuthenticationContext); + + Assert.assertThrows( + AuthenticationFailedException.class, + () -> openIDConnectAuthenticator.processAuthenticationResponse(mockServletRequest, + mockServletResponse, mockAuthenticationContext) + ); } @Test @@ -543,7 +569,7 @@ public void testPassProcessAuthenticationWithBlankCallBack() throws Exception { mockServletResponse, mockAuthenticationContext); } - @Test(expectedExceptions = AuthenticationFailedException.class) + @Test public void testFailProcessAuthenticationWhenNonceMisMatch() throws Exception { setupTest(); @@ -564,8 +590,12 @@ public void testFailProcessAuthenticationWhenNonceMisMatch() throws Exception { when(mockOAuthClient.accessToken(any())).thenReturn(mockOAuthJSONAccessTokenResponse); when(mockAuthenticationContext.getProperty(OIDC_FEDERATION_NONCE)).thenReturn(invalidNonce); when(mockOAuthJSONAccessTokenResponse.getParam(anyString())).thenReturn(idToken); - openIDConnectAuthenticator.processAuthenticationResponse(mockServletRequest, - mockServletResponse, mockAuthenticationContext); + + Assert.assertThrows( + AuthenticationFailedException.class, + () -> openIDConnectAuthenticator.processAuthenticationResponse(mockServletRequest, + mockServletResponse, mockAuthenticationContext) + ); } @Test @@ -598,6 +628,145 @@ public void testPassProcessAuthenticationWithParamValue() throws Exception { mockServletResponse, mockAuthenticationContext); } + @Test + public void testPassProcessAuthenticationResponseWithNativeSDKBaseFederation() throws Exception { + + setupTest(); + + IdentityProviderProperty property = new IdentityProviderProperty(); + property.setName(IdPManagementConstants.IS_TRUSTED_TOKEN_ISSUER); + property.setValue("true"); + IdentityProviderProperty[] identityProviderProperties = new IdentityProviderProperty[1]; + identityProviderProperties[0] = property; + when(mockAuthenticationContext.getExternalIdP()).thenReturn(externalIdPConfig); + when(externalIdPConfig.getIdentityProvider()).thenReturn(identityProvider); + when(identityProvider.getIdpProperties()).thenReturn(identityProviderProperties); + when(mockAuthenticationContext.getExternalIdP()).thenReturn(externalIdPConfig); + when(mockServletRequest.getParameter(OIDCAuthenticatorConstants.ID_TOKEN_PARAM)) + .thenReturn(idToken); + when(mockServletRequest.getParameter(OIDCAuthenticatorConstants.ACCESS_TOKEN_PARAM)) + .thenReturn(accessToken); + + mockStatic(OIDCTokenValidationUtil.class); + doNothing().when(OIDCTokenValidationUtil.class, "validateIssuerClaim", any(JWTClaimsSet.class)); + when(mockAuthenticationContext.getTenantDomain()).thenReturn(superTenantDomain); + mockStatic(IdentityProviderManager.class); + when(IdentityProviderManager.getInstance()).thenReturn(identityProviderManager); + when(identityProviderManager.getIdPByMetadataProperty( + eq(IdentityApplicationConstants.IDP_ISSUER_NAME), anyString(), eq(superTenantDomain), eq(false))) + .thenReturn(identityProvider); + doNothing().when(OIDCTokenValidationUtil.class, "validateSignature", any(SignedJWT.class), + any(IdentityProvider.class)); + doNothing().when(OIDCTokenValidationUtil.class, "validateAudience", any(List.class), any(), anyString()); + + when(openIDConnectAuthenticatorDataHolder.getClaimMetadataManagementService()).thenReturn + (claimMetadataManagementService); + openIDConnectAuthenticator.processAuthenticationResponse(mockServletRequest, + mockServletResponse, mockAuthenticationContext); + + assertEquals(mockAuthenticationContext.getProperty(OIDCAuthenticatorConstants.ACCESS_TOKEN), + accessToken, "Invalid access token in the authentication context."); + + assertEquals(mockAuthenticationContext.getProperty(OIDCAuthenticatorConstants.ID_TOKEN), idToken, + "Invalid Id token in the authentication context."); + } + + @Test + public void testFailProcessAuthenticationResponseWithNativeSDKBaseFederation() throws Exception { + + setupTest(); + + IdentityProviderProperty property = new IdentityProviderProperty(); + property.setName(IdPManagementConstants.IS_TRUSTED_TOKEN_ISSUER); + property.setValue("true"); + IdentityProviderProperty[] identityProviderProperties = new IdentityProviderProperty[1]; + identityProviderProperties[0] = property; + when(mockAuthenticationContext.getExternalIdP()).thenReturn(externalIdPConfig); + when(externalIdPConfig.getIdentityProvider()).thenReturn(identityProvider); + when(identityProvider.getIdpProperties()).thenReturn(identityProviderProperties); + when(mockAuthenticationContext.getExternalIdP()).thenReturn(externalIdPConfig); + when(mockServletRequest.getParameter(OIDCAuthenticatorConstants.ID_TOKEN_PARAM)) + .thenReturn(idToken); + when(mockServletRequest.getParameter(OIDCAuthenticatorConstants.ACCESS_TOKEN_PARAM)) + .thenReturn(accessToken); + + mockStatic(OIDCTokenValidationUtil.class); + doNothing().when(OIDCTokenValidationUtil.class, "validateIssuerClaim", any(JWTClaimsSet.class)); + when(mockAuthenticationContext.getTenantDomain()).thenReturn(superTenantDomain); + mockStatic(IdentityProviderManager.class); + when(IdentityProviderManager.getInstance()).thenReturn(identityProviderManager); + when(identityProviderManager.getIdPByMetadataProperty( + eq(IdentityApplicationConstants.IDP_ISSUER_NAME), anyString(), eq(superTenantDomain), eq(false))) + .thenReturn(identityProvider); + doNothing().when(OIDCTokenValidationUtil.class, "validateSignature", any(SignedJWT.class), + any(IdentityProvider.class)); + + // If none of the audience values matched the token endpoint alias + doThrow(mock(AuthenticationFailedException.class)) + .when(OIDCTokenValidationUtil.class, "validateAudience", any(List.class), + any(), eq(superTenantDomain)); + + Assert.assertThrows( + AuthenticationFailedException.class, + () -> openIDConnectAuthenticator.processAuthenticationResponse(mockServletRequest, + mockServletResponse, mockAuthenticationContext) + ); + + // If signature validation fails. + doThrow(mock(AuthenticationFailedException.class)) + .when(OIDCTokenValidationUtil.class, "validateSignature", any(SignedJWT.class), + any(IdentityProvider.class)); + + Assert.assertThrows( + AuthenticationFailedException.class, + () -> openIDConnectAuthenticator.processAuthenticationResponse(mockServletRequest, + mockServletResponse, mockAuthenticationContext) + ); + + // If there is an issue while validating the signature. + doThrow(mock(IdentityOAuth2Exception.class)) + .when(OIDCTokenValidationUtil.class, "validateSignature", any(SignedJWT.class), + any(IdentityProvider.class)); + + Assert.assertThrows( + AuthenticationFailedException.class, + () -> openIDConnectAuthenticator.processAuthenticationResponse(mockServletRequest, + mockServletResponse, mockAuthenticationContext) + ); + + // If there is an issue while verifying the singed JWT. + doThrow(mock(JOSEException.class)) + .when(OIDCTokenValidationUtil.class, "validateSignature", any(SignedJWT.class), + any(IdentityProvider.class)); + + Assert.assertThrows( + AuthenticationFailedException.class, + () -> openIDConnectAuthenticator.processAuthenticationResponse(mockServletRequest, + mockServletResponse, mockAuthenticationContext) + ); + + // If there is an issue while validating the issuer. + mockStatic(OIDCTokenValidationUtil.class); + doThrow(mock(AuthenticationFailedException.class)) + .when(OIDCTokenValidationUtil.class, "validateIssuerClaim", any(JWTClaimsSet.class)); + + Assert.assertThrows( + AuthenticationFailedException.class, + () -> openIDConnectAuthenticator.processAuthenticationResponse(mockServletRequest, + mockServletResponse, mockAuthenticationContext) + ); + + // If the sent id token is invalid. + when(mockServletRequest.getParameter(OIDCAuthenticatorConstants.ID_TOKEN_PARAM)) + .thenReturn(invalidIdToken); + + Assert.assertThrows( + AuthenticationFailedException.class, + () -> openIDConnectAuthenticator.processAuthenticationResponse(mockServletRequest, + mockServletResponse, mockAuthenticationContext) + ); + } + @Test(dataProvider = "seperator") public void testBuildClaimMappings(String separator) throws Exception { @@ -726,24 +895,32 @@ public void testGetOauthResponseWithoutExceptions() throws OAuthSystemException, Assert.assertNotNull(openIDConnectAuthenticator.getOauthResponse(mockOAuthClient, mockOAuthClientRequest)); } - @Test(expectedExceptions = AuthenticationFailedException.class) + @Test public void testGetOauthResponseWithExceptions() throws OAuthSystemException, OAuthProblemException, AuthenticationFailedException { OAuthClientRequest oAuthClientRequest = mock(OAuthClientRequest.class); OAuthClient oAuthClient = mock(OAuthClient.class); when(oAuthClient.accessToken(oAuthClientRequest)).thenThrow(OAuthSystemException.class); - openIDConnectAuthenticator.getOauthResponse(oAuthClient, oAuthClientRequest); + + Assert.assertThrows( + AuthenticationFailedException.class, + () -> openIDConnectAuthenticator.getOauthResponse(oAuthClient, oAuthClientRequest) + ); } - @Test(expectedExceptions = AuthenticationFailedException.class) + @Test public void testGetOauthResponseWithOAuthProblemExceptions() throws OAuthSystemException, OAuthProblemException, AuthenticationFailedException { OAuthClientRequest oAuthClientRequest = mock(OAuthClientRequest.class); OAuthClient oAuthClient = mock(OAuthClient.class); when(oAuthClient.accessToken(oAuthClientRequest)).thenThrow(OAuthProblemException.class); - openIDConnectAuthenticator.getOauthResponse(oAuthClient, oAuthClientRequest); + + Assert.assertThrows( + AuthenticationFailedException.class, + () -> openIDConnectAuthenticator.getOauthResponse(oAuthClient, oAuthClientRequest) + ); } @ObjectFactory @@ -763,7 +940,7 @@ private void setupTest() throws Exception { mockStatic(OAuthAuthzResponse.class); when(OAuthAuthzResponse.oauthCodeAuthzResponse(mockServletRequest)).thenReturn(mockOAuthzResponse); - when(mockServletRequest.getParameter("domain")).thenReturn("carbon.super"); + when(mockServletRequest.getParameter("domain")).thenReturn(superTenantDomain); mockAuthenticationRequestContext(mockAuthenticationContext); when(mockOAuthzResponse.getCode()).thenReturn("200"); when(mockAuthenticationContext.getProperty(OIDCAuthenticatorConstants.ACCESS_TOKEN)).thenReturn(accessToken); @@ -889,6 +1066,13 @@ public void testGetAuthInitiationDataForNativeSDKBasedFederation() { .getAdditionalAuthenticationParams().get(OIDCAuthenticatorConstants.CLIENT_ID_PARAM), clientId); } + @Test + public void testGetI18nKey() { + + String oidcI18nKey = openIDConnectAuthenticator.getI18nKey(); + Assert.assertEquals(oidcI18nKey, AUTHENTICATOR_OIDC); + } + private ExternalIdPConfig getDummyExternalIdPConfig() { IdentityProvider identityProvider = new IdentityProvider(); diff --git a/components/org.wso2.carbon.identity.application.authenticator.oidc/src/test/java/org/wso2/carbon/identity/application/authenticator/oidc/util/OIDCTokenValidationUtilTest.java b/components/org.wso2.carbon.identity.application.authenticator.oidc/src/test/java/org/wso2/carbon/identity/application/authenticator/oidc/util/OIDCTokenValidationUtilTest.java index 00793857..e3f622ff 100644 --- a/components/org.wso2.carbon.identity.application.authenticator.oidc/src/test/java/org/wso2/carbon/identity/application/authenticator/oidc/util/OIDCTokenValidationUtilTest.java +++ b/components/org.wso2.carbon.identity.application.authenticator.oidc/src/test/java/org/wso2/carbon/identity/application/authenticator/oidc/util/OIDCTokenValidationUtilTest.java @@ -22,30 +22,54 @@ import com.nimbusds.jwt.SignedJWT; import org.mockito.Mock; import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.core.classloader.annotations.SuppressStaticInitializationFor; import org.powermock.modules.testng.PowerMockTestCase; import org.testng.Assert; import org.testng.annotations.BeforeTest; import org.testng.annotations.Test; import org.wso2.carbon.identity.application.authentication.framework.exception.AuthenticationFailedException; +import org.wso2.carbon.identity.application.common.model.FederatedAuthenticatorConfig; import org.wso2.carbon.identity.application.common.model.IdentityProvider; +import org.wso2.carbon.identity.application.common.model.Property; +import org.wso2.carbon.identity.application.common.util.IdentityApplicationConstants; +import org.wso2.carbon.identity.application.common.util.IdentityApplicationManagementUtil; import org.wso2.carbon.identity.oauth2.util.JWTSignatureValidationUtils; +import org.wso2.carbon.idp.mgt.IdentityProviderManager; import java.util.ArrayList; import java.util.List; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; import static org.powermock.api.mockito.PowerMockito.mockStatic; import static org.powermock.api.mockito.PowerMockito.when; -import static org.powermock.api.mockito.PowerMockito.whenNew; /*** * Unit test class for OIDCTokenValidationUtil class. */ -@PrepareForTest({JWTSignatureValidationUtils.class}) +@PrepareForTest({ + JWTSignatureValidationUtils.class, + IdentityProviderManager.class, + IdentityApplicationManagementUtil.class +}) +@SuppressStaticInitializationFor({ + "org.wso2.carbon.idp.mgt.IdentityProviderManager", + "org.wso2.carbon.identity.application.authentication.framework.exception.AuthenticationFailedException" +}) public class OIDCTokenValidationUtilTest extends PowerMockTestCase { @Mock private IdentityProvider identityProvider; + @Mock + private IdentityProviderManager identityProviderManager; + + @Mock + private FederatedAuthenticatorConfig mockedOauthAuthenticatorConfig; + + @Mock + private Property mockedOauthTokenURL; + private static String idToken = "eyJ4NXQiOiJOVEF4Wm1NeE5ETXlaRGczTVRVMVpHTTBNekV6T0RKaFpXSTRORE5" + "sWkRVMU9HRmtOakZpTVEiLCJraWQiOiJOVEF4Wm1NeE5ETXlaRGczTVRVMVpHTTBNekV6T0RKaFpXSTRORE5sWkRVMU9" + "HRmtOakZpTVEiLCJhbGciOiJSUzI1NiJ9.eyJzdWIiOiJhZG1pbiIsImF1ZCI6WyJ1NUZJZkc1eHpMdkJHaWFtb0FZenpjc" + @@ -75,6 +99,34 @@ public void testGetIssuer() throws Exception { Assert.assertEquals(OIDCTokenValidationUtil.getIssuer(claimsSet), idpIdentifier); } + @Test + public void testPassValidateAudienceResident() throws Exception { + + when(identityProvider.getIdentityProviderName()).thenReturn("LOCAL"); + + mockStatic(IdentityProviderManager.class); + when(IdentityProviderManager.getInstance()).thenReturn(identityProviderManager); + when(identityProviderManager.getResidentIdP(tenantDomain)).thenReturn(identityProvider); + FederatedAuthenticatorConfig[] fedAuthnConfigs = new FederatedAuthenticatorConfig[1]; + fedAuthnConfigs[0] = mockedOauthAuthenticatorConfig; + when(identityProvider.getFederatedAuthenticatorConfigs()).thenReturn(fedAuthnConfigs); + + mockStatic(IdentityApplicationManagementUtil.class); + when(IdentityApplicationManagementUtil.class, "getFederatedAuthenticator", + any(FederatedAuthenticatorConfig[].class), + eq(IdentityApplicationConstants.Authenticator.OIDC.NAME)).thenReturn(mockedOauthAuthenticatorConfig); + Property[] properties = new Property[]{new Property()}; + when(mockedOauthAuthenticatorConfig.getProperties()).thenReturn(properties); + when(IdentityApplicationManagementUtil.getProperty(any(Property[].class), + eq(IdentityApplicationConstants.Authenticator.OIDC.OAUTH2_TOKEN_URL))).thenReturn(mockedOauthTokenURL); + when(mockedOauthTokenURL.getValue()).thenReturn(alias); + + List audienceList = new ArrayList<>(); + audienceList.add(alias); + + OIDCTokenValidationUtil.validateAudience(audienceList, identityProvider, tenantDomain); + } + @Test public void testPassValidateAudienceExternal() throws Exception { @@ -84,10 +136,40 @@ public void testPassValidateAudienceExternal() throws Exception { List audienceList = new ArrayList<>(); audienceList.add(alias); - OIDCTokenValidationUtil.validateAudience(audienceList, identityProvider,tenantDomain); + OIDCTokenValidationUtil.validateAudience(audienceList, identityProvider, tenantDomain); + } + + @Test + public void testFailValidateAudienceResident() throws Exception { + + when(identityProvider.getIdentityProviderName()).thenReturn("LOCAL"); + + mockStatic(IdentityProviderManager.class); + when(IdentityProviderManager.getInstance()).thenReturn(identityProviderManager); + when(identityProviderManager.getResidentIdP(tenantDomain)).thenReturn(identityProvider); + FederatedAuthenticatorConfig[] fedAuthnConfigs = new FederatedAuthenticatorConfig[1]; + fedAuthnConfigs[0] = mockedOauthAuthenticatorConfig; + when(identityProvider.getFederatedAuthenticatorConfigs()).thenReturn(fedAuthnConfigs); + + mockStatic(IdentityApplicationManagementUtil.class); + when(IdentityApplicationManagementUtil.class, "getFederatedAuthenticator", + any(FederatedAuthenticatorConfig[].class), + eq(IdentityApplicationConstants.Authenticator.OIDC.NAME)).thenReturn(mockedOauthAuthenticatorConfig); + Property[] properties = new Property[]{new Property()}; + when(mockedOauthAuthenticatorConfig.getProperties()).thenReturn(properties); + when(IdentityApplicationManagementUtil.getProperty(any(Property[].class), + eq(IdentityApplicationConstants.Authenticator.OIDC.OAUTH2_TOKEN_URL))).thenReturn(mockedOauthTokenURL); + when(mockedOauthTokenURL.getValue()).thenReturn(alias); + + List audienceList = new ArrayList<>(); + + Assert.assertThrows( + AuthenticationFailedException.class, + () -> OIDCTokenValidationUtil.validateAudience(audienceList, identityProvider, tenantDomain) + ); } - @Test(expectedExceptions = AuthenticationFailedException.class) + @Test public void testFailValidateAudienceExternal() throws Exception { when(identityProvider.getIdentityProviderName()).thenReturn("Google"); @@ -95,7 +177,10 @@ public void testFailValidateAudienceExternal() throws Exception { List audienceList = new ArrayList<>(); - OIDCTokenValidationUtil.validateAudience(audienceList, identityProvider,tenantDomain); + Assert.assertThrows( + AuthenticationFailedException.class, + () -> OIDCTokenValidationUtil.validateAudience(audienceList, identityProvider, tenantDomain) + ); } @Test @@ -103,19 +188,24 @@ public void testPassValidateSignature() throws Exception { SignedJWT signedJWT = SignedJWT.parse(idToken); mockStatic(JWTSignatureValidationUtils.class); - when(JWTSignatureValidationUtils.validateSignature(signedJWT, identityProvider)).thenReturn(true); + when(JWTSignatureValidationUtils.class, "validateSignature", signedJWT, identityProvider) + .thenReturn(true); OIDCTokenValidationUtil.validateSignature(signedJWT, identityProvider); } - @Test(expectedExceptions = AuthenticationFailedException.class) + @Test public void testFailValidateSignature() throws Exception { SignedJWT signedJWT = SignedJWT.parse(idToken); mockStatic(JWTSignatureValidationUtils.class); - when(JWTSignatureValidationUtils.validateSignature(signedJWT, identityProvider)).thenReturn(false); + when(JWTSignatureValidationUtils.class, "validateSignature", signedJWT, identityProvider) + .thenReturn(false); - OIDCTokenValidationUtil.validateSignature(signedJWT, identityProvider); + Assert.assertThrows( + AuthenticationFailedException.class, + () -> OIDCTokenValidationUtil.validateSignature(signedJWT, identityProvider) + ); } @Test @@ -127,12 +217,15 @@ public void testPassValidateIssuerClaim() throws Exception { OIDCTokenValidationUtil.validateIssuerClaim(claimsSet); } - @Test(expectedExceptions = AuthenticationFailedException.class) + @Test public void testFailValidateIssuerClaim() throws Exception { SignedJWT signedJWT = SignedJWT.parse(invlaidIdToken); JWTClaimsSet claimsSet = signedJWT.getJWTClaimsSet(); - OIDCTokenValidationUtil.validateIssuerClaim(claimsSet); + Assert.assertThrows( + AuthenticationFailedException.class, + () -> OIDCTokenValidationUtil.validateIssuerClaim(claimsSet) + ); } } diff --git a/pom.xml b/pom.xml index 615c7bff..72d2a412 100644 --- a/pom.xml +++ b/pom.xml @@ -304,7 +304,7 @@ ${project.version} - 5.25.431 + 5.25.484 1.0.0.wso2v3 2.4.7 3.0.0.wso2v2 From 616123e266d98b9a76c20cf730cab56174cec9e7 Mon Sep 17 00:00:00 2001 From: dhaura Date: Fri, 3 Nov 2023 12:39:32 +0530 Subject: [PATCH 2/2] Bump framework version --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 72d2a412..7747036d 100644 --- a/pom.xml +++ b/pom.xml @@ -304,7 +304,7 @@ ${project.version} - 5.25.484 + 5.25.491 1.0.0.wso2v3 2.4.7 3.0.0.wso2v2