From 8b6506548d8ecdff5cec7efa4c346b46ba92d5b0 Mon Sep 17 00:00:00 2001 From: Chamath Samarawickrama Date: Mon, 26 Oct 2020 12:44:07 +0530 Subject: [PATCH 01/13] Add support for ID token validation. --- io.asgardio.java.oidc.sdk/pom.xml | 13 + .../java/oidc/sdk/OIDCManagerImpl.java | 24 +- .../java/oidc/sdk/SSOAgentConstants.java | 4 + .../config/FileBasedOIDCConfigProvider.java | 19 +- .../sdk/config/model/OIDCAgentConfig.java | 24 ++ .../exception/SSOAgentServerException.java | 12 + .../oidc/sdk/request/OIDCRequestBuilder.java | 22 +- .../oidc/sdk/validators/IDTokenValidator.java | 148 +++++++++ .../java/oidc/sdk/OIDCManagerImplTest.java | 55 +++- .../sdk/request/OIDCRequestBuilderTest.java | 25 +- .../sdk/request/OIDCRequestResolverTest.java | 11 +- .../sdk/validators/IDTokenValidatorTest.java | 292 ++++++++++++++++++ .../src/test/resources/testng.xml | 6 + pom.xml | 19 +- 14 files changed, 642 insertions(+), 32 deletions(-) create mode 100644 io.asgardio.java.oidc.sdk/src/main/java/io/asgardio/java/oidc/sdk/validators/IDTokenValidator.java create mode 100644 io.asgardio.java.oidc.sdk/src/test/java/io/asgardio/java/oidc/sdk/validators/IDTokenValidatorTest.java diff --git a/io.asgardio.java.oidc.sdk/pom.xml b/io.asgardio.java.oidc.sdk/pom.xml index 97feb5c..e06e08b 100644 --- a/io.asgardio.java.oidc.sdk/pom.xml +++ b/io.asgardio.java.oidc.sdk/pom.xml @@ -91,6 +91,19 @@ org.mock-server mockserver-client-java + + org.powermock + powermock-api-mockito2 + test + + + net.jadler + jadler-core + + + net.jadler + jadler-jetty + diff --git a/io.asgardio.java.oidc.sdk/src/main/java/io/asgardio/java/oidc/sdk/OIDCManagerImpl.java b/io.asgardio.java.oidc.sdk/src/main/java/io/asgardio/java/oidc/sdk/OIDCManagerImpl.java index 0a0de3f..33826ce 100644 --- a/io.asgardio.java.oidc.sdk/src/main/java/io/asgardio/java/oidc/sdk/OIDCManagerImpl.java +++ b/io.asgardio.java.oidc.sdk/src/main/java/io/asgardio/java/oidc/sdk/OIDCManagerImpl.java @@ -18,6 +18,7 @@ package io.asgardio.java.oidc.sdk; +import com.nimbusds.jwt.JWT; import com.nimbusds.jwt.JWTClaimsSet; import com.nimbusds.jwt.JWTParser; import com.nimbusds.jwt.SignedJWT; @@ -40,6 +41,8 @@ import com.nimbusds.oauth2.sdk.id.ClientID; import com.nimbusds.oauth2.sdk.token.AccessToken; import com.nimbusds.oauth2.sdk.token.RefreshToken; +import com.nimbusds.openid.connect.sdk.Nonce; +import com.nimbusds.openid.connect.sdk.claims.IDTokenClaimsSet; import io.asgardio.java.oidc.sdk.bean.AuthenticationInfo; import io.asgardio.java.oidc.sdk.bean.User; import io.asgardio.java.oidc.sdk.config.model.OIDCAgentConfig; @@ -48,6 +51,7 @@ import io.asgardio.java.oidc.sdk.exception.SSOAgentServerException; import io.asgardio.java.oidc.sdk.request.OIDCRequestBuilder; import io.asgardio.java.oidc.sdk.request.OIDCRequestResolver; +import io.asgardio.java.oidc.sdk.validators.IDTokenValidator; import net.minidev.json.JSONObject; import org.apache.commons.lang.StringUtils; import org.apache.logging.log4j.Level; @@ -62,6 +66,7 @@ import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpSession; /** * OIDC manager implementation. @@ -86,7 +91,11 @@ public void sendForLogin(HttpServletRequest request, HttpServletResponse respons throws SSOAgentException { OIDCRequestBuilder requestBuilder = new OIDCRequestBuilder(oidcAgentConfig); - String authorizationRequest = requestBuilder.buildAuthorizationRequest(state); +// Nonce nonce = new Nonce("KE4OYeY_gfYwzQbJa9tGhj1hZJMa"); + Nonce nonce = new Nonce(); + //TODO handle with session management. + request.getSession().setAttribute(SSOAgentConstants.NONCE, nonce); + String authorizationRequest = requestBuilder.buildAuthenticationRequest(state, nonce); try { response.sendRedirect(authorizationRequest); } catch (IOException e) { @@ -189,11 +198,16 @@ private void handleSuccessTokenResponse(TokenResponse tokenResponse, Authenticat throw new SSOAgentServerException(SSOAgentConstants.ErrorMessages.ID_TOKEN_NULL.getMessage(), SSOAgentConstants.ErrorMessages.ID_TOKEN_NULL.getCode(), e); } + Nonce nonce = null; + if (session.getAttribute(SSOAgentConstants.NONCE) != null) { + nonce = (Nonce) session.getAttribute(SSOAgentConstants.NONCE); + } try { - JWTClaimsSet claimsSet = SignedJWT.parse(idToken).getJWTClaimsSet(); - User user = new User(claimsSet.getSubject(), getUserAttributes(idToken)); - - authenticationInfo.setIdToken(JWTParser.parse(idToken)); + JWT idTokenJWT = JWTParser.parse(idToken); + IDTokenValidator idTokenValidator = new IDTokenValidator(oidcAgentConfig, idTokenJWT); + IDTokenClaimsSet claimsSet = idTokenValidator.validate(nonce); + User user = new User(claimsSet.getSubject().getValue(), getUserAttributes(idToken)); + authenticationInfo.setIdToken(idTokenJWT); authenticationInfo.setUser(user); authenticationInfo.setAccessToken(accessToken); authenticationInfo.setRefreshToken(refreshToken); diff --git a/io.asgardio.java.oidc.sdk/src/main/java/io/asgardio/java/oidc/sdk/SSOAgentConstants.java b/io.asgardio.java.oidc.sdk/src/main/java/io/asgardio/java/oidc/sdk/SSOAgentConstants.java index 22bb0f9..20e0402 100644 --- a/io.asgardio.java.oidc.sdk/src/main/java/io/asgardio/java/oidc/sdk/SSOAgentConstants.java +++ b/io.asgardio.java.oidc.sdk/src/main/java/io/asgardio/java/oidc/sdk/SSOAgentConstants.java @@ -59,6 +59,10 @@ public class SSOAgentConstants { public static final String AUTHENTICATED = "authenticated"; public static final String AUTHENTICATION_INFO = "authenticationInfo"; public static final String OIDC_OPENID = "openid"; + public static final String AZP = "azp"; + public static final String TRUSTED_AUDIENCE = "trustedAudience"; + public static final String ID_TOKEN_SIGN_ALG = "signatureAlgorithm"; + public static final String NONCE = "nonce"; // Request headers. public static final String REFERER = "referer"; diff --git a/io.asgardio.java.oidc.sdk/src/main/java/io/asgardio/java/oidc/sdk/config/FileBasedOIDCConfigProvider.java b/io.asgardio.java.oidc.sdk/src/main/java/io/asgardio/java/oidc/sdk/config/FileBasedOIDCConfigProvider.java index 6cb3648..c9e6a8b 100644 --- a/io.asgardio.java.oidc.sdk/src/main/java/io/asgardio/java/oidc/sdk/config/FileBasedOIDCConfigProvider.java +++ b/io.asgardio.java.oidc.sdk/src/main/java/io/asgardio/java/oidc/sdk/config/FileBasedOIDCConfigProvider.java @@ -18,6 +18,7 @@ package io.asgardio.java.oidc.sdk.config; +import com.nimbusds.jose.JWSAlgorithm; import com.nimbusds.oauth2.sdk.Scope; import com.nimbusds.oauth2.sdk.auth.Secret; import com.nimbusds.oauth2.sdk.id.ClientID; @@ -34,6 +35,7 @@ import java.io.InputStream; import java.net.URI; import java.net.URISyntaxException; +import java.util.Collections; import java.util.HashSet; import java.util.Properties; import java.util.Set; @@ -67,6 +69,9 @@ private void initConfig(Properties properties) throws SSOAgentClientException { new Secret(properties.getProperty(SSOAgentConstants.CONSUMER_SECRET)) : null; String indexPage = properties.getProperty(SSOAgentConstants.INDEX_PAGE); String logoutURL = properties.getProperty(SSOAgentConstants.LOGOUT_URL); + JWSAlgorithm jwsAlgorithm = + StringUtils.isNotBlank(properties.getProperty(SSOAgentConstants.ID_TOKEN_SIGN_ALG)) ? + new JWSAlgorithm(properties.getProperty(SSOAgentConstants.ID_TOKEN_SIGN_ALG)) : null; try { URI callbackUrl = StringUtils.isNotBlank(properties.getProperty(SSOAgentConstants.CALL_BACK_URL)) ? new URI(properties.getProperty(SSOAgentConstants.CALL_BACK_URL)) : null; @@ -102,14 +107,18 @@ private void initConfig(Properties properties) throws SSOAgentClientException { Scope scope = new Scope(scopeArray); oidcAgentConfig.setScope(scope); } - Set skipURIs = new HashSet(); String skipURIsString = properties.getProperty(SSOAgentConstants.SKIP_URIS); if (StringUtils.isNotBlank(skipURIsString)) { String[] skipURIArray = skipURIsString.split(","); - for (String skipURI : skipURIArray) { - skipURIs.add(skipURI); - } + Collections.addAll(skipURIs, skipURIArray); + } + Set trustedAudience = new HashSet(); + trustedAudience.add(consumerKey.getValue()); + String trustedAudienceString = properties.getProperty(SSOAgentConstants.TRUSTED_AUDIENCE); + if (StringUtils.isNotBlank(trustedAudienceString)) { + String[] trustedAudienceArray = trustedAudienceString.split(","); + Collections.addAll(trustedAudience, trustedAudienceArray); } oidcAgentConfig.setConsumerKey(consumerKey); oidcAgentConfig.setConsumerSecret(consumerSecret); @@ -117,6 +126,8 @@ private void initConfig(Properties properties) throws SSOAgentClientException { oidcAgentConfig.setLogoutURL(logoutURL); oidcAgentConfig.setIssuer(issuer); oidcAgentConfig.setSkipURIs(skipURIs); + oidcAgentConfig.setTrustedAudience(trustedAudience); + oidcAgentConfig.setSignatureAlgorithm(jwsAlgorithm); } /** diff --git a/io.asgardio.java.oidc.sdk/src/main/java/io/asgardio/java/oidc/sdk/config/model/OIDCAgentConfig.java b/io.asgardio.java.oidc.sdk/src/main/java/io/asgardio/java/oidc/sdk/config/model/OIDCAgentConfig.java index e82c15c..a8563bf 100644 --- a/io.asgardio.java.oidc.sdk/src/main/java/io/asgardio/java/oidc/sdk/config/model/OIDCAgentConfig.java +++ b/io.asgardio.java.oidc.sdk/src/main/java/io/asgardio/java/oidc/sdk/config/model/OIDCAgentConfig.java @@ -18,6 +18,7 @@ package io.asgardio.java.oidc.sdk.config.model; +import com.nimbusds.jose.JWSAlgorithm; import com.nimbusds.oauth2.sdk.Scope; import com.nimbusds.oauth2.sdk.auth.Secret; import com.nimbusds.oauth2.sdk.id.ClientID; @@ -42,8 +43,10 @@ public class OIDCAgentConfig { private URI logoutEndpoint; private URI tokenEndpoint; private Issuer issuer; + private Set trustedAudience; private URI jwksEndpoint; private URI postLogoutRedirectURI; + private JWSAlgorithm signatureAlgorithm; private Set skipURIs = new HashSet(); /** @@ -246,6 +249,17 @@ public void setIssuer(Issuer issuer) { this.issuer = issuer; } + + public Set getTrustedAudience() { + + return trustedAudience; + } + + public void setTrustedAudience(Set trustedAudience) { + + this.trustedAudience = trustedAudience; + } + /** * Returns the JWKS endpoint URI of the OIDC agent. * @@ -286,6 +300,16 @@ public void setPostLogoutRedirectURI(URI postLogoutRedirectURI) { this.postLogoutRedirectURI = postLogoutRedirectURI; } + public JWSAlgorithm getSignatureAlgorithm() { + + return signatureAlgorithm; + } + + public void setSignatureAlgorithm(JWSAlgorithm signatureAlgorithm) { + + this.signatureAlgorithm = signatureAlgorithm; + } + /** * Returns the skip URIs of the OIDC agent. * diff --git a/io.asgardio.java.oidc.sdk/src/main/java/io/asgardio/java/oidc/sdk/exception/SSOAgentServerException.java b/io.asgardio.java.oidc.sdk/src/main/java/io/asgardio/java/oidc/sdk/exception/SSOAgentServerException.java index 7e5124c..22298b6 100644 --- a/io.asgardio.java.oidc.sdk/src/main/java/io/asgardio/java/oidc/sdk/exception/SSOAgentServerException.java +++ b/io.asgardio.java.oidc.sdk/src/main/java/io/asgardio/java/oidc/sdk/exception/SSOAgentServerException.java @@ -26,6 +26,18 @@ public class SSOAgentServerException extends SSOAgentException { private static final long serialVersionUID = 4776260071061676883L; + /** + * Constructs a SSOAgentServerException with the specified detail + * message. A detail message is a String that describes this + * particular exception. + * + * @param message The detail message. + */ + public SSOAgentServerException(String message) { + + super(message); + } + /** * Creates a {@code SSOAgentServerException} with the specified * detail message and cause. diff --git a/io.asgardio.java.oidc.sdk/src/main/java/io/asgardio/java/oidc/sdk/request/OIDCRequestBuilder.java b/io.asgardio.java.oidc.sdk/src/main/java/io/asgardio/java/oidc/sdk/request/OIDCRequestBuilder.java index bc4a5e1..54d7453 100644 --- a/io.asgardio.java.oidc.sdk/src/main/java/io/asgardio/java/oidc/sdk/request/OIDCRequestBuilder.java +++ b/io.asgardio.java.oidc.sdk/src/main/java/io/asgardio/java/oidc/sdk/request/OIDCRequestBuilder.java @@ -19,12 +19,13 @@ package io.asgardio.java.oidc.sdk.request; import com.nimbusds.jwt.JWT; -import com.nimbusds.oauth2.sdk.AuthorizationRequest; import com.nimbusds.oauth2.sdk.ResponseType; import com.nimbusds.oauth2.sdk.Scope; import com.nimbusds.oauth2.sdk.id.ClientID; import com.nimbusds.oauth2.sdk.id.State; +import com.nimbusds.openid.connect.sdk.AuthenticationRequest; import com.nimbusds.openid.connect.sdk.LogoutRequest; +import com.nimbusds.openid.connect.sdk.Nonce; import io.asgardio.java.oidc.sdk.OIDCManager; import io.asgardio.java.oidc.sdk.bean.AuthenticationInfo; import io.asgardio.java.oidc.sdk.config.model.OIDCAgentConfig; @@ -69,7 +70,7 @@ public OIDCRequestBuilder(OIDCAgentConfig oidcAgentConfig) { * @param state State parameter. * @return Authorization request. */ - public String buildAuthorizationRequest(String state) { + public String buildAuthenticationRequest(String state, Nonce nonce) { ResponseType responseType = new ResponseType(ResponseType.Value.CODE); ClientID clientID = oidcAgentConfig.getConsumerKey(); @@ -81,13 +82,22 @@ public String buildAuthorizationRequest(String state) { stateParameter = new State(state); } - AuthorizationRequest authorizationRequest = new AuthorizationRequest.Builder(responseType, clientID) - .scope(authScope) + AuthenticationRequest authenticationRequest = new AuthenticationRequest.Builder(responseType, authScope, + clientID, callBackURI) .state(stateParameter) - .redirectionURI(callBackURI) .endpointURI(authorizationEndpoint) + .nonce(nonce) .build(); - return authorizationRequest.toURI().toString(); + + return authenticationRequest.toURI().toString(); + +// AuthorizationRequest authorizationRequest = new AuthorizationRequest.Builder(responseType, clientID) +// .scope(authScope) +// .state(stateParameter) +// .redirectionURI(callBackURI) +// .endpointURI(authorizationEndpoint) +// .build(); +// return authorizationRequest.toURI().toString(); } /** diff --git a/io.asgardio.java.oidc.sdk/src/main/java/io/asgardio/java/oidc/sdk/validators/IDTokenValidator.java b/io.asgardio.java.oidc.sdk/src/main/java/io/asgardio/java/oidc/sdk/validators/IDTokenValidator.java new file mode 100644 index 0000000..2f030af --- /dev/null +++ b/io.asgardio.java.oidc.sdk/src/main/java/io/asgardio/java/oidc/sdk/validators/IDTokenValidator.java @@ -0,0 +1,148 @@ +/* + * Copyright (c) 2020, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package io.asgardio.java.oidc.sdk.validators; + +import com.nimbusds.jose.JOSEException; +import com.nimbusds.jose.JWSAlgorithm; +import com.nimbusds.jose.proc.BadJOSEException; +import com.nimbusds.jwt.JWT; +import com.nimbusds.oauth2.sdk.auth.Secret; +import com.nimbusds.oauth2.sdk.id.Audience; +import com.nimbusds.oauth2.sdk.id.ClientID; +import com.nimbusds.oauth2.sdk.id.Issuer; +import com.nimbusds.openid.connect.sdk.Nonce; +import com.nimbusds.openid.connect.sdk.claims.IDTokenClaimsSet; +import io.asgardio.java.oidc.sdk.SSOAgentConstants; +import io.asgardio.java.oidc.sdk.config.model.OIDCAgentConfig; +import io.asgardio.java.oidc.sdk.exception.SSOAgentServerException; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import java.net.MalformedURLException; +import java.net.URI; +import java.util.List; +import java.util.Set; + +/** + * Validator of ID tokens issued by an OpenID Provider. + * + *

Supports processing of ID tokens with: + * + *

    + *
  • ID tokens signed (JWS) with the OP's RSA or EC key, require the + * OP public JWK set (provided by value or URL) to verify them. + *
  • ID tokens authenticated with a JWS HMAC, require the client's secret + * to verify them. + *
+ */ +public class IDTokenValidator { + + private static final Logger logger = LogManager.getLogger(IDTokenValidator.class); + + private OIDCAgentConfig oidcAgentConfig; + private JWT idToken; + + public IDTokenValidator(OIDCAgentConfig oidcAgentConfig, JWT idToken) { + + this.oidcAgentConfig = oidcAgentConfig; + this.idToken = idToken; + } + + public IDTokenClaimsSet validate(Nonce expectedNonce) throws SSOAgentServerException { + + JWSAlgorithm jwsAlgorithm = validateJWSAlgorithm(oidcAgentConfig, idToken); + com.nimbusds.openid.connect.sdk.validators.IDTokenValidator validator = + getIDTokenValidator(oidcAgentConfig, jwsAlgorithm); + IDTokenClaimsSet claims; + try { + claims = validator.validate(idToken, expectedNonce); + validateAudience(oidcAgentConfig, claims); + } catch (JOSEException | BadJOSEException e) { + throw new SSOAgentServerException(e.getMessage(), e.getCause()); + } + return claims; + } + + private com.nimbusds.openid.connect.sdk.validators.IDTokenValidator getIDTokenValidator( + OIDCAgentConfig oidcAgentConfig, JWSAlgorithm jwsAlgorithm) throws SSOAgentServerException { + + Issuer issuer = oidcAgentConfig.getIssuer(); + URI jwkSetURI = oidcAgentConfig.getJwksEndpoint(); + ClientID clientID = oidcAgentConfig.getConsumerKey(); + Secret clientSecret = oidcAgentConfig.getConsumerSecret(); + com.nimbusds.openid.connect.sdk.validators.IDTokenValidator validator; + + // Creates a new validator for RSA, EC or ED protected ID tokens. + if (JWSAlgorithm.Family.RSA.contains(jwsAlgorithm) || JWSAlgorithm.Family.EC.contains(jwsAlgorithm) || + JWSAlgorithm.Family.ED.contains(jwsAlgorithm)) { + try { + validator = + new com.nimbusds.openid.connect.sdk.validators.IDTokenValidator(issuer, clientID, jwsAlgorithm, + jwkSetURI.toURL()); + } catch (MalformedURLException e) { + throw new SSOAgentServerException(e.getMessage(), e.getCause()); + } + // Creates a new validator for HMAC protected ID tokens. + } else if (JWSAlgorithm.Family.HMAC_SHA.contains(jwsAlgorithm)) { + validator = new com.nimbusds.openid.connect.sdk.validators.IDTokenValidator(issuer, clientID, jwsAlgorithm, + clientSecret); + } else { + throw new SSOAgentServerException(String.format("Unsupported algorithm: %s.", jwsAlgorithm.getName())); + } + return validator; + } + + private JWSAlgorithm validateJWSAlgorithm(OIDCAgentConfig oidcAgentConfig, JWT idToken) + throws SSOAgentServerException { + + JWSAlgorithm jwsAlgorithm = (JWSAlgorithm) idToken.getHeader().getAlgorithm(); + JWSAlgorithm expectedJWSAlgorithm = oidcAgentConfig.getSignatureAlgorithm(); + + if (expectedJWSAlgorithm == null) { + if (JWSAlgorithm.RS256.equals(jwsAlgorithm)) { + return jwsAlgorithm; + } else { + throw new SSOAgentServerException(String.format("Signed JWT rejected. Provided signature algorithm: " + + "%s is not the default of RS256.", jwsAlgorithm.getName())); + } + } else if (!expectedJWSAlgorithm.equals(jwsAlgorithm)) { + throw new SSOAgentServerException(String.format("Signed JWT rejected: Another algorithm expected. " + + "Provided signature algorithm: %s.", jwsAlgorithm.getName())); + } + return jwsAlgorithm; + } + + private void validateAudience(OIDCAgentConfig oidcAgentConfig, IDTokenClaimsSet claimsSet) + throws SSOAgentServerException { + + List audience = claimsSet.getAudience(); + if (audience.size() > 1) { + if (claimsSet.getClaim(SSOAgentConstants.AZP) == null) { + throw new SSOAgentServerException("ID token validation failed. AZP claim cannot be null for multiple " + + "audiences."); + } + Set trustedAudience = oidcAgentConfig.getTrustedAudience(); + for (Audience aud : audience) { + if (!trustedAudience.contains(aud.getValue())) { + throw new SSOAgentServerException("ID token validation failed. Untrusted JWT audience."); + } + } + } + } +} diff --git a/io.asgardio.java.oidc.sdk/src/test/java/io/asgardio/java/oidc/sdk/OIDCManagerImplTest.java b/io.asgardio.java.oidc.sdk/src/test/java/io/asgardio/java/oidc/sdk/OIDCManagerImplTest.java index f55c9d6..e2d77ad 100644 --- a/io.asgardio.java.oidc.sdk/src/test/java/io/asgardio/java/oidc/sdk/OIDCManagerImplTest.java +++ b/io.asgardio.java.oidc.sdk/src/test/java/io/asgardio/java/oidc/sdk/OIDCManagerImplTest.java @@ -24,7 +24,6 @@ import com.nimbusds.oauth2.sdk.AuthorizationCode; import com.nimbusds.oauth2.sdk.AuthorizationResponse; import com.nimbusds.oauth2.sdk.AuthorizationSuccessResponse; -import com.nimbusds.oauth2.sdk.ParseException; import com.nimbusds.oauth2.sdk.Scope; import com.nimbusds.oauth2.sdk.TokenResponse; import com.nimbusds.oauth2.sdk.auth.Secret; @@ -32,22 +31,32 @@ import com.nimbusds.oauth2.sdk.http.HTTPResponse; import com.nimbusds.oauth2.sdk.http.ServletUtils; import com.nimbusds.oauth2.sdk.id.ClientID; +import com.nimbusds.oauth2.sdk.id.Issuer; +import com.nimbusds.oauth2.sdk.id.Subject; import com.nimbusds.oauth2.sdk.token.AccessToken; import com.nimbusds.oauth2.sdk.token.AccessTokenType; import com.nimbusds.oauth2.sdk.token.RefreshToken; import com.nimbusds.oauth2.sdk.token.Tokens; +import com.nimbusds.openid.connect.sdk.Nonce; +import com.nimbusds.openid.connect.sdk.claims.IDTokenClaimsSet; import io.asgardio.java.oidc.sdk.bean.AuthenticationInfo; import io.asgardio.java.oidc.sdk.config.model.OIDCAgentConfig; import io.asgardio.java.oidc.sdk.exception.SSOAgentException; import io.asgardio.java.oidc.sdk.request.OIDCRequestResolver; +import io.asgardio.java.oidc.sdk.validators.IDTokenValidator; import org.mockito.Mock; import org.mockito.MockedStatic; +import org.mockito.Mockito; import org.mockserver.integration.ClientAndServer; +import org.powermock.api.mockito.PowerMockito; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.testng.PowerMockTestCase; +import org.testng.IObjectFactory; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; +import org.testng.annotations.ObjectFactory; import org.testng.annotations.Test; -import java.io.IOException; import java.net.URI; import java.net.URISyntaxException; import java.util.HashMap; @@ -55,6 +64,7 @@ import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpSession; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; @@ -62,7 +72,9 @@ import static org.mockito.Mockito.when; import static org.testng.Assert.assertEquals; -public class OIDCManagerImplTest { +@PrepareForTest({IDTokenValidator.class, IDTokenClaimsSet.class, + com.nimbusds.openid.connect.sdk.validators.IDTokenValidator.class}) +public class OIDCManagerImplTest extends PowerMockTestCase { @Mock HttpServletRequest request; @@ -81,13 +93,15 @@ public class OIDCManagerImplTest { private ClientAndServer mockServer; @BeforeMethod - public void setUp() throws URISyntaxException, java.text.ParseException { + public void setUp() throws Exception { - mockServer = ClientAndServer.startClientAndServer(9443); + mockServer = ClientAndServer.startClientAndServer(9441); + Issuer issuer = new Issuer("issuer"); ClientID clientID = new ClientID("sampleClientId"); - Secret clientSecret = new Secret("sampleClientSceret"); - URI callbackURI = new URI("http://localhost:9443/sampleCallbackURL"); - URI tokenEPURI = new URI("http://localhost:9443/sampleTokenEP"); + Secret clientSecret = new Secret("sampleClientSecret"); + URI callbackURI = new URI("http://localhost:9441/sampleCallbackURL"); + URI tokenEPURI = new URI("http://localhost:9441/sampleTokenEP"); + URI jwksURI = new URI("http://localhost:9441/jwksEP"); URI logoutEP = new URI("http://test/sampleLogoutEP"); Scope scope = new Scope("sampleScope1", "openid"); JWT idToken = JWTParser @@ -105,11 +119,23 @@ public void setUp() throws URISyntaxException, java.text.ParseException { oidcAgentConfig.setTokenEndpoint(tokenEPURI); oidcAgentConfig.setLogoutEndpoint(logoutEP); oidcAgentConfig.setScope(scope); + oidcAgentConfig.setIssuer(issuer); + oidcAgentConfig.setJwksEndpoint(jwksURI); when(authenticationInfo.getIdToken()).thenReturn(idToken); + IDTokenClaimsSet claimsSet = mock(IDTokenClaimsSet.class); + IDTokenValidator idTokenValidator = mock(IDTokenValidator.class); + com.nimbusds.openid.connect.sdk.validators.IDTokenValidator validator = mock( + com.nimbusds.openid.connect.sdk.validators.IDTokenValidator.class); + PowerMockito.whenNew(IDTokenValidator.class).withAnyArguments().thenReturn(idTokenValidator); + PowerMockito.whenNew(com.nimbusds.openid.connect.sdk.validators.IDTokenValidator.class).withAnyArguments() + .thenReturn(validator); + when(validator.validate(any(JWT.class), any(Nonce.class))).thenReturn(claimsSet); + Mockito.when(idTokenValidator.validate(any(Nonce.class))).thenReturn(claimsSet); + Mockito.when(claimsSet.getSubject()).thenReturn(new Subject("alex@carbon.super")); } @Test - public void testHandleOIDCCallback() throws SSOAgentException, IOException, ParseException { + public void testHandleOIDCCallback() throws Exception { AccessToken accessToken = new AccessToken(AccessTokenType.BEARER, "sampleAccessToken") { @Override @@ -132,6 +158,7 @@ public String toAuthorizationHeader() { "hAd3NvMi5jb20ifQ.pHwsQqn64tif2J6iYcRShK_85WO3aBuL7Pz8urcHErXjyh6zvroOqSWD9KbSxJPocyoIshdqWdAEhdURKL" + "tXiw-l73HlvnX4qJKYT71VKXMTC26Z8dlk4TgytXiskmj8OpAcem3czuEWTrTLVbYzIw71p9kx-5Xxb9WNvzBg1YpwGC8MK3dkW" + "TfmUsu6oncIvHyv-gbX3kJebgMserp"; + JWT idToken = JWTParser.parse(parsedIdToken); customParameters.put(SSOAgentConstants.ID_TOKEN, parsedIdToken); when(requestResolver.isError()).thenReturn(false); @@ -156,6 +183,9 @@ public String toAuthorizationHeader() { when(tokenResponse.toSuccessResponse()).thenReturn(accessTokenResponse); when(accessTokenResponse.getTokens()).thenReturn(tokens); when(accessTokenResponse.getCustomParameters()).thenReturn(customParameters); + HttpSession session = mock(HttpSession.class); + when(request.getSession(false)).thenReturn(session); + when(session.getAttribute(SSOAgentConstants.NONCE)).thenReturn(new Nonce()); OIDCManager oidcManager = new OIDCManagerImpl(oidcAgentConfig); AuthenticationInfo authenticationInfo = oidcManager.handleOIDCCallback(request, response); @@ -164,7 +194,6 @@ public String toAuthorizationHeader() { assertEquals(authenticationInfo.getRefreshToken(), refreshToken); assertEquals(authenticationInfo.getIdToken().getParsedString(), parsedIdToken); assertEquals(authenticationInfo.getUser().getSubject(), "alex@carbon.super"); - mockedAuthorizationResponse.close(); mockedServletUtils.close(); mockedTokenResponse.close(); @@ -192,4 +221,10 @@ public void tearDown() { mockServer.stop(); } + + @ObjectFactory + public IObjectFactory getObjectFactory() { + + return new org.powermock.modules.testng.PowerMockObjectFactory(); + } } diff --git a/io.asgardio.java.oidc.sdk/src/test/java/io/asgardio/java/oidc/sdk/request/OIDCRequestBuilderTest.java b/io.asgardio.java.oidc.sdk/src/test/java/io/asgardio/java/oidc/sdk/request/OIDCRequestBuilderTest.java index ab27b49..29088cf 100644 --- a/io.asgardio.java.oidc.sdk/src/test/java/io/asgardio/java/oidc/sdk/request/OIDCRequestBuilderTest.java +++ b/io.asgardio.java.oidc.sdk/src/test/java/io/asgardio/java/oidc/sdk/request/OIDCRequestBuilderTest.java @@ -22,10 +22,15 @@ import com.nimbusds.jwt.JWTParser; import com.nimbusds.oauth2.sdk.Scope; import com.nimbusds.oauth2.sdk.id.ClientID; +import com.nimbusds.openid.connect.sdk.Nonce; import io.asgardio.java.oidc.sdk.bean.AuthenticationInfo; import io.asgardio.java.oidc.sdk.config.model.OIDCAgentConfig; import org.mockito.Mock; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.testng.PowerMockTestCase; +import org.testng.IObjectFactory; import org.testng.annotations.BeforeMethod; +import org.testng.annotations.ObjectFactory; import org.testng.annotations.Test; import java.net.URI; @@ -36,7 +41,8 @@ import static org.mockito.Mockito.when; import static org.testng.Assert.assertEquals; -public class OIDCRequestBuilderTest { +@PrepareForTest({OIDCAgentConfig.class, AuthenticationInfo.class}) +public class OIDCRequestBuilderTest extends PowerMockTestCase { @Mock OIDCAgentConfig oidcAgentConfig; @@ -48,7 +54,7 @@ public class OIDCRequestBuilderTest { public void setUp() throws URISyntaxException, ParseException { ClientID clientID = new ClientID("sampleClientId"); - Scope scope = new Scope("sampleScope1", "sampleScope2"); + Scope scope = new Scope("sampleScope1", "openid"); URI callbackURI = new URI("http://test/sampleCallbackURL"); URI authorizationEndpoint = new URI("http://test/sampleAuthzEP"); URI logoutEP = new URI("http://test/sampleLogoutEP"); @@ -72,9 +78,12 @@ public void setUp() throws URISyntaxException, ParseException { @Test public void testBuildAuthorizationRequest() { - String authorizationRequest = new OIDCRequestBuilder(oidcAgentConfig).buildAuthorizationRequest("state"); - assertEquals(authorizationRequest, "http://test/sampleAuthzEP?response_type=code&redirect_uri=http%3A%2F" + - "%2Ftest%2FsampleCallbackURL&state=state&client_id=sampleClientId&scope=sampleScope1+sampleScope2"); + Nonce nonce = new Nonce("sampleNonce"); + String authorizationRequest = + new OIDCRequestBuilder(oidcAgentConfig).buildAuthenticationRequest("state", nonce); + assertEquals(authorizationRequest, + "http://test/sampleAuthzEP?scope=sampleScope1+openid&response_type=code&redirect_uri=http" + + "%3A%2F%2Ftest%2FsampleCallbackURL&state=state&nonce=sampleNonce&client_id=sampleClientId"); } @Test @@ -86,4 +95,10 @@ public void testBuildLogoutRequest() { "Ftest%2FsampleRedirectionURL&id_token_hint=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3O" + "DkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c"); } + + @ObjectFactory + public IObjectFactory getObjectFactory() { + + return new org.powermock.modules.testng.PowerMockObjectFactory(); + } } diff --git a/io.asgardio.java.oidc.sdk/src/test/java/io/asgardio/java/oidc/sdk/request/OIDCRequestResolverTest.java b/io.asgardio.java.oidc.sdk/src/test/java/io/asgardio/java/oidc/sdk/request/OIDCRequestResolverTest.java index 4e2ad16..a00aa8a 100644 --- a/io.asgardio.java.oidc.sdk/src/test/java/io/asgardio/java/oidc/sdk/request/OIDCRequestResolverTest.java +++ b/io.asgardio.java.oidc.sdk/src/test/java/io/asgardio/java/oidc/sdk/request/OIDCRequestResolverTest.java @@ -27,9 +27,12 @@ import org.mockito.Mock; import org.mockito.MockedStatic; import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.testng.PowerMockTestCase; +import org.testng.IObjectFactory; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.DataProvider; +import org.testng.annotations.ObjectFactory; import org.testng.annotations.Test; import java.io.IOException; @@ -47,7 +50,7 @@ import static org.testng.Assert.assertTrue; @PrepareForTest({AuthorizationResponse.class}) -public class OIDCRequestResolverTest { +public class OIDCRequestResolverTest extends PowerMockTestCase { @Mock OIDCAgentConfig oidcAgentConfig; @@ -149,4 +152,10 @@ public void testGetIndexPage(String indexPageConfig, String contextPath, String public void tearDown() { } + + @ObjectFactory + public IObjectFactory getObjectFactory() { + + return new org.powermock.modules.testng.PowerMockObjectFactory(); + } } diff --git a/io.asgardio.java.oidc.sdk/src/test/java/io/asgardio/java/oidc/sdk/validators/IDTokenValidatorTest.java b/io.asgardio.java.oidc.sdk/src/test/java/io/asgardio/java/oidc/sdk/validators/IDTokenValidatorTest.java new file mode 100644 index 0000000..3b54a9c --- /dev/null +++ b/io.asgardio.java.oidc.sdk/src/test/java/io/asgardio/java/oidc/sdk/validators/IDTokenValidatorTest.java @@ -0,0 +1,292 @@ +/* + * Copyright (c) 2020, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package io.asgardio.java.oidc.sdk.validators; + +import com.nimbusds.jose.JOSEException; +import com.nimbusds.jose.JWSAlgorithm; +import com.nimbusds.jose.JWSHeader; +import com.nimbusds.jose.JWSSigner; +import com.nimbusds.jose.crypto.ECDSASigner; +import com.nimbusds.jose.crypto.RSASSASigner; +import com.nimbusds.jose.jwk.Curve; +import com.nimbusds.jose.jwk.ECKey; +import com.nimbusds.jose.jwk.JWK; +import com.nimbusds.jose.jwk.JWKSet; +import com.nimbusds.jose.jwk.RSAKey; +import com.nimbusds.jwt.JWTClaimsSet; +import com.nimbusds.jwt.SignedJWT; +import com.nimbusds.oauth2.sdk.auth.Secret; +import com.nimbusds.oauth2.sdk.id.Audience; +import com.nimbusds.oauth2.sdk.id.ClientID; +import com.nimbusds.oauth2.sdk.id.Issuer; +import com.nimbusds.openid.connect.sdk.Nonce; +import com.nimbusds.openid.connect.sdk.claims.IDTokenClaimsSet; +import io.asgardio.java.oidc.sdk.config.model.OIDCAgentConfig; +import io.asgardio.java.oidc.sdk.exception.SSOAgentServerException; +import net.jadler.Jadler; +import org.testng.annotations.AfterMethod; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +import java.net.URL; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.NoSuchAlgorithmException; +import java.security.interfaces.ECPrivateKey; +import java.security.interfaces.ECPublicKey; +import java.security.interfaces.RSAPrivateKey; +import java.security.interfaces.RSAPublicKey; +import java.security.spec.ECParameterSpec; +import java.util.Arrays; +import java.util.Collections; +import java.util.Date; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import static net.jadler.Jadler.closeJadler; +import static net.jadler.Jadler.initJadler; +import static net.jadler.Jadler.port; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertTrue; + +public class IDTokenValidatorTest { + + private OIDCAgentConfig config; + private RSAKey key; + + @BeforeMethod + public void setUp() throws Exception { + + initJadler(); + + config = new OIDCAgentConfig(); + JWKSet jwkSet = generateJWKS(); + key = (RSAKey) jwkSet.getKeys().get(0); + + Issuer issuer = new Issuer("issuer"); + ClientID clientID = new ClientID("sampleClientId"); + Secret clientSecret = new Secret("sampleClientSecret"); + URL jwkSetURL = new URL("http://localhost:" + port() + "/jwksEP"); + + config.setIssuer(issuer); + config.setConsumerKey(clientID); + config.setConsumerSecret(clientSecret); + config.setJwksEndpoint(jwkSetURL.toURI()); + + Jadler.onRequest() + .havingMethodEqualTo("GET") + .havingPathEqualTo("/jwksEP") + .respond() + .withStatus(200) + .withHeader("Content-Type", "application/json") + .withBody(jwkSet.toJSONObject(true).toJSONString()); + } + + private JWKSet generateJWKS() throws NoSuchAlgorithmException { + + KeyPairGenerator pairGen = KeyPairGenerator.getInstance("RSA"); + pairGen.initialize(2048); + KeyPair keyPair = pairGen.generateKeyPair(); + + RSAKey rsaJWK1 = new RSAKey.Builder((RSAPublicKey) keyPair.getPublic()) + .privateKey((RSAPrivateKey) keyPair.getPrivate()) + .keyID("1") + .build(); + + keyPair = pairGen.generateKeyPair(); + + RSAKey rsaJWK2 = new RSAKey.Builder((RSAPublicKey) keyPair.getPublic()) + .privateKey((RSAPrivateKey) keyPair.getPrivate()) + .keyID("2") + .build(); + + JWKSet jwkSet = new JWKSet(Arrays.asList((JWK) rsaJWK1, (JWK) rsaJWK2)); + return jwkSet; + } + + private com.nimbusds.jose.jwk.ECKey generateECJWK(final Curve curve) throws Exception { + + ECParameterSpec ecParameterSpec = curve.toECParameterSpec(); + + KeyPairGenerator generator = KeyPairGenerator.getInstance("EC"); + generator.initialize(ecParameterSpec); + KeyPair keyPair = generator.generateKeyPair(); + + return new com.nimbusds.jose.jwk.ECKey.Builder(curve, (ECPublicKey) keyPair.getPublic()). + privateKey((ECPrivateKey) keyPair.getPrivate()). + build(); + } + + @DataProvider(name = "IssuerData") + public Object[][] issuerData() { + + Issuer issuer1 = new Issuer("issuer1"); + Issuer issuer2 = new Issuer("issuer2"); + + return new Object[][]{ + // issuer + // expected + {issuer1, "issuer1"}, + {issuer2, "issuer2"} + }; + } + + @Test(dataProvider = "IssuerData") + public void testIssuer(Issuer issuer, String expectedIssuer) throws SSOAgentServerException, JOSEException { + + config.setIssuer(issuer); + Nonce nonce = new Nonce(); + JWTClaimsSet claims = new JWTClaimsSet.Builder() + .issuer(issuer.getValue()) + .subject("alice") + .audience(config.getConsumerKey().getValue()) + .expirationTime(new Date()) + .issueTime(new Date()) + .claim("nonce", nonce.getValue()) + .build(); + + SignedJWT idToken = new SignedJWT(new JWSHeader(JWSAlgorithm.RS256), claims); + JWSSigner signer = new RSASSASigner(key); + idToken.sign(signer); + + IDTokenValidator validator = new IDTokenValidator(config, idToken); + IDTokenClaimsSet claimsSet = validator.validate(nonce); + assertEquals(claimsSet.getIssuer().getValue(), expectedIssuer); + } + + @DataProvider(name = "AudienceData") + public Object[][] audienceData() { + + String clientID1 = "clientID1"; + List tokenAudience1 = Arrays.asList(clientID1); + Set trustedAudience1 = new HashSet<>(tokenAudience1); + trustedAudience1.add(clientID1); + String azp1 = null; + + String clientID2 = "clientID2"; + List tokenAudience2 = Arrays.asList("aud1", "aud2", "aud3", clientID2); + Set trustedAudience2 = new HashSet<>(tokenAudience2); + String azp2 = clientID2; + + return new Object[][]{ + // token audience + // trusted audience + // client ID + // AZP value + {tokenAudience1, trustedAudience1, clientID1, azp1}, + {tokenAudience2, trustedAudience2, clientID2, azp2} + }; + } + + @Test(dataProvider = "AudienceData") + public void testAudience(List audience, Set trustedAudience, String clientID, String azpValue) + throws SSOAgentServerException, JOSEException { + + Nonce nonce = new Nonce(); + config.setTrustedAudience(trustedAudience); + config.setConsumerKey(new ClientID(clientID)); + JWTClaimsSet claims = new JWTClaimsSet.Builder() + .issuer(config.getIssuer().getValue()) + .subject("alice") + .audience(audience) + .expirationTime(new Date()) + .issueTime(new Date()) + .claim("nonce", nonce.getValue()) + .claim("azp", azpValue) + .build(); + + SignedJWT idToken = new SignedJWT(new JWSHeader(JWSAlgorithm.RS256), claims); + JWSSigner signer = new RSASSASigner(key); + idToken.sign(signer); + + IDTokenValidator validator = new IDTokenValidator(config, idToken); + IDTokenClaimsSet claimsSet = validator.validate(nonce); + List audiences = claimsSet.getAudience(); + audiences.forEach(aud -> assertTrue(trustedAudience.contains(aud.getValue()))); + } + + @DataProvider(name = "AlgorithmData") + public Object[][] algorithmData() throws Exception { + + KeyPairGenerator pairGenRSA = KeyPairGenerator.getInstance("RSA"); + pairGenRSA.initialize(2048); + KeyPair keyPairRSA = pairGenRSA.generateKeyPair(); + + RSAKey rsaJWK = new RSAKey.Builder((RSAPublicKey) keyPairRSA.getPublic()) + .privateKey((RSAPrivateKey) keyPairRSA.getPrivate()) + .keyID("1") + .build(); + + ECKey ecJWK = generateECJWK(Curve.P_256); + + return new Object[][]{ + // algorithm + // key + {"RS256", (JWK) rsaJWK}, + {"ES256", (JWK) ecJWK} + }; + } + + @Test(dataProvider = "AlgorithmData") + public void testJWSAlgorithm(String signatureAlgorithm, JWK key) throws JOSEException, SSOAgentServerException { + + JWKSet jwkSet = new JWKSet(Collections.singletonList(key)); + + Jadler.onRequest() + .havingMethodEqualTo("GET") + .havingPathEqualTo("/jwksEP") + .respond() + .withStatus(200) + .withHeader("Content-Type", "application/json") + .withBody(jwkSet.toJSONObject(true).toJSONString()); + + Nonce nonce = new Nonce(); + JWSAlgorithm jwsAlgorithm = new JWSAlgorithm(signatureAlgorithm); + config.setSignatureAlgorithm(jwsAlgorithm); + JWTClaimsSet claims = new JWTClaimsSet.Builder() + .issuer(config.getIssuer().getValue()) + .subject("alice") + .audience(config.getConsumerKey().getValue()) + .expirationTime(new Date()) + .issueTime(new Date()) + .claim("nonce", nonce.getValue()) + .build(); + + SignedJWT idToken = new SignedJWT(new JWSHeader(jwsAlgorithm), claims); + JWSSigner signer; + if (key instanceof RSAKey) { + signer = new RSASSASigner((RSAKey) key); + } else { + signer = new ECDSASigner((ECKey) key); + } + idToken.sign(signer); + + IDTokenValidator validator = new IDTokenValidator(config, idToken); + IDTokenClaimsSet claimsSet = validator.validate(nonce); + assertEquals(claimsSet.getNonce(), nonce); + } + + @AfterMethod + public void tearDown() { + + closeJadler(); + } +} \ No newline at end of file diff --git a/io.asgardio.java.oidc.sdk/src/test/resources/testng.xml b/io.asgardio.java.oidc.sdk/src/test/resources/testng.xml index 1610a10..575c106 100644 --- a/io.asgardio.java.oidc.sdk/src/test/resources/testng.xml +++ b/io.asgardio.java.oidc.sdk/src/test/resources/testng.xml @@ -25,6 +25,12 @@ + + + + + + diff --git a/pom.xml b/pom.xml index 4233e58..54488c6 100644 --- a/pom.xml +++ b/pom.xml @@ -186,6 +186,22 @@ mockserver-client-java ${mockserver.version} + + org.powermock + powermock-api-mockito2 + ${mockito.api.version} + test + + + net.jadler + jadler-core + ${jadler.version} + + + net.jadler + jadler-jetty + ${jadler.version} + @@ -270,8 +286,9 @@ 7.3.0 2.0.7 3.5.13 - 1.7.4 + 2.0.7 3.10.8 + 1.3.0 From 3f6c58eb30e62d91cfa0224d818d83d88de47518 Mon Sep 17 00:00:00 2001 From: Chamath Samarawickrama Date: Mon, 26 Oct 2020 19:47:54 +0530 Subject: [PATCH 02/13] Add support for session management by the agent. --- ...nagerImpl.java => DefaultOIDCManager.java} | 50 ++++++------ .../asgardio/java/oidc/sdk/OIDCManager.java | 25 +++--- .../java/oidc/sdk/SSOAgentConstants.java | 3 +- .../oidc/sdk/bean/AuthenticationRequest.java | 59 ++++++++++++++ .../java/oidc/sdk/bean/RequestContext.java | 77 +++++++++++++++++++ ...nticationInfo.java => SessionContext.java} | 4 +- .../oidc/sdk/request/OIDCRequestBuilder.java | 53 +++++++------ .../java/oidc/sdk/OIDCManagerImplTest.java | 14 ++-- .../oidc/sdk/bean/AuthenticationInfoTest.java | 2 +- .../sdk/request/OIDCRequestBuilderTest.java | 12 +-- 10 files changed, 217 insertions(+), 82 deletions(-) rename io.asgardio.java.oidc.sdk/src/main/java/io/asgardio/java/oidc/sdk/{OIDCManagerImpl.java => DefaultOIDCManager.java} (88%) create mode 100644 io.asgardio.java.oidc.sdk/src/main/java/io/asgardio/java/oidc/sdk/bean/AuthenticationRequest.java create mode 100644 io.asgardio.java.oidc.sdk/src/main/java/io/asgardio/java/oidc/sdk/bean/RequestContext.java rename io.asgardio.java.oidc.sdk/src/main/java/io/asgardio/java/oidc/sdk/bean/{AuthenticationInfo.java => SessionContext.java} (95%) diff --git a/io.asgardio.java.oidc.sdk/src/main/java/io/asgardio/java/oidc/sdk/OIDCManagerImpl.java b/io.asgardio.java.oidc.sdk/src/main/java/io/asgardio/java/oidc/sdk/DefaultOIDCManager.java similarity index 88% rename from io.asgardio.java.oidc.sdk/src/main/java/io/asgardio/java/oidc/sdk/OIDCManagerImpl.java rename to io.asgardio.java.oidc.sdk/src/main/java/io/asgardio/java/oidc/sdk/DefaultOIDCManager.java index 33826ce..1b8a08f 100644 --- a/io.asgardio.java.oidc.sdk/src/main/java/io/asgardio/java/oidc/sdk/OIDCManagerImpl.java +++ b/io.asgardio.java.oidc.sdk/src/main/java/io/asgardio/java/oidc/sdk/DefaultOIDCManager.java @@ -43,7 +43,9 @@ import com.nimbusds.oauth2.sdk.token.RefreshToken; import com.nimbusds.openid.connect.sdk.Nonce; import com.nimbusds.openid.connect.sdk.claims.IDTokenClaimsSet; -import io.asgardio.java.oidc.sdk.bean.AuthenticationInfo; +import io.asgardio.java.oidc.sdk.bean.RequestContext; +import io.asgardio.java.oidc.sdk.bean.SessionContext; +import io.asgardio.java.oidc.sdk.bean.AuthenticationRequest; import io.asgardio.java.oidc.sdk.bean.User; import io.asgardio.java.oidc.sdk.config.model.OIDCAgentConfig; import io.asgardio.java.oidc.sdk.exception.SSOAgentClientException; @@ -66,18 +68,17 @@ import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; -import javax.servlet.http.HttpSession; /** * OIDC manager implementation. */ -public class OIDCManagerImpl implements OIDCManager { +public class DefaultOIDCManager implements OIDCManager { - private static final Logger logger = LogManager.getLogger(OIDCManagerImpl.class); + private static final Logger logger = LogManager.getLogger(DefaultOIDCManager.class); private OIDCAgentConfig oidcAgentConfig; - public OIDCManagerImpl(OIDCAgentConfig oidcAgentConfig) throws SSOAgentClientException { + public DefaultOIDCManager(OIDCAgentConfig oidcAgentConfig) throws SSOAgentClientException { validateConfig(oidcAgentConfig); this.oidcAgentConfig = oidcAgentConfig; @@ -87,39 +88,37 @@ public OIDCManagerImpl(OIDCAgentConfig oidcAgentConfig) throws SSOAgentClientExc * {@inheritDoc} */ @Override - public void sendForLogin(HttpServletRequest request, HttpServletResponse response, String state) + public RequestContext sendForLogin(HttpServletRequest request, HttpServletResponse response) throws SSOAgentException { OIDCRequestBuilder requestBuilder = new OIDCRequestBuilder(oidcAgentConfig); -// Nonce nonce = new Nonce("KE4OYeY_gfYwzQbJa9tGhj1hZJMa"); - Nonce nonce = new Nonce(); - //TODO handle with session management. - request.getSession().setAttribute(SSOAgentConstants.NONCE, nonce); - String authorizationRequest = requestBuilder.buildAuthenticationRequest(state, nonce); + AuthenticationRequest authenticationRequest = requestBuilder.buildAuthenticationRequest(); try { - response.sendRedirect(authorizationRequest); + response.sendRedirect(authenticationRequest.getAuthenticationRequestURI().toString()); } catch (IOException e) { throw new SSOAgentException(e.getMessage(), e); } + return authenticationRequest.getRequestContext(); } /** * {@inheritDoc} */ @Override - public AuthenticationInfo handleOIDCCallback(HttpServletRequest request, HttpServletResponse response) - throws SSOAgentException { + public SessionContext handleOIDCCallback(HttpServletRequest request, HttpServletResponse response, + RequestContext requestContext) throws SSOAgentException { OIDCRequestResolver requestResolver = new OIDCRequestResolver(request, oidcAgentConfig); - AuthenticationInfo authenticationInfo = new AuthenticationInfo(); + SessionContext sessionContext = new SessionContext(); + Nonce nonce = requestContext.getNonce(); try { if (!requestResolver.isError() && requestResolver.isAuthorizationCodeResponse()) { logger.log(Level.TRACE, "Handling the OIDC Authorization response."); - boolean isAuthenticated = handleAuthentication(request, authenticationInfo); + boolean isAuthenticated = handleAuthentication(request, sessionContext, nonce); if (isAuthenticated) { logger.log(Level.TRACE, "Authentication successful. Redirecting to the target page."); - return authenticationInfo; + return sessionContext; } } logger.log(Level.ERROR, "Authentication unsuccessful. Clearing the active session and redirecting."); @@ -134,8 +133,7 @@ public AuthenticationInfo handleOIDCCallback(HttpServletRequest request, HttpSer * {@inheritDoc} */ @Override - public void logout(AuthenticationInfo authenticationInfo, HttpServletResponse response, String state) - throws SSOAgentException { + public void logout(SessionContext sessionContext, HttpServletResponse response) throws SSOAgentException { if (oidcAgentConfig.getPostLogoutRedirectURI() == null) { logger.info("postLogoutRedirectURI is not configured. Using the callbackURL instead."); @@ -143,7 +141,7 @@ public void logout(AuthenticationInfo authenticationInfo, HttpServletResponse re oidcAgentConfig.setPostLogoutRedirectURI(callbackURI); } OIDCRequestBuilder requestBuilder = new OIDCRequestBuilder(oidcAgentConfig); - String logoutRequest = requestBuilder.buildLogoutRequest(authenticationInfo, state); + String logoutRequest = requestBuilder.buildLogoutRequest(sessionContext); try { response.sendRedirect(logoutRequest); } catch (IOException e) { @@ -152,7 +150,8 @@ public void logout(AuthenticationInfo authenticationInfo, HttpServletResponse re } } - private boolean handleAuthentication(final HttpServletRequest request, AuthenticationInfo authenticationInfo) { + private boolean handleAuthentication(final HttpServletRequest request, SessionContext authenticationInfo, + Nonce nonce) { AuthorizationResponse authorizationResponse; AuthorizationCode authorizationCode; @@ -176,7 +175,7 @@ private boolean handleAuthentication(final HttpServletRequest request, Authentic handleErrorTokenResponse(tokenRequest, tokenResponse); return false; } - handleSuccessTokenResponse(tokenResponse, authenticationInfo); + handleSuccessTokenResponse(tokenResponse, authenticationInfo, nonce); return true; } catch (com.nimbusds.oauth2.sdk.ParseException | SSOAgentServerException | IOException e) { logger.error(e.getMessage(), e); @@ -184,7 +183,8 @@ private boolean handleAuthentication(final HttpServletRequest request, Authentic } } - private void handleSuccessTokenResponse(TokenResponse tokenResponse, AuthenticationInfo authenticationInfo) + private void handleSuccessTokenResponse(TokenResponse tokenResponse, SessionContext authenticationInfo, + Nonce nonce) throws SSOAgentServerException { AccessTokenResponse successResponse = tokenResponse.toSuccessResponse(); @@ -198,10 +198,6 @@ private void handleSuccessTokenResponse(TokenResponse tokenResponse, Authenticat throw new SSOAgentServerException(SSOAgentConstants.ErrorMessages.ID_TOKEN_NULL.getMessage(), SSOAgentConstants.ErrorMessages.ID_TOKEN_NULL.getCode(), e); } - Nonce nonce = null; - if (session.getAttribute(SSOAgentConstants.NONCE) != null) { - nonce = (Nonce) session.getAttribute(SSOAgentConstants.NONCE); - } try { JWT idTokenJWT = JWTParser.parse(idToken); IDTokenValidator idTokenValidator = new IDTokenValidator(oidcAgentConfig, idTokenJWT); diff --git a/io.asgardio.java.oidc.sdk/src/main/java/io/asgardio/java/oidc/sdk/OIDCManager.java b/io.asgardio.java.oidc.sdk/src/main/java/io/asgardio/java/oidc/sdk/OIDCManager.java index dfe9a2a..3624c71 100644 --- a/io.asgardio.java.oidc.sdk/src/main/java/io/asgardio/java/oidc/sdk/OIDCManager.java +++ b/io.asgardio.java.oidc.sdk/src/main/java/io/asgardio/java/oidc/sdk/OIDCManager.java @@ -18,7 +18,9 @@ package io.asgardio.java.oidc.sdk; -import io.asgardio.java.oidc.sdk.bean.AuthenticationInfo; +import io.asgardio.java.oidc.sdk.bean.AuthenticationRequest; +import io.asgardio.java.oidc.sdk.bean.RequestContext; +import io.asgardio.java.oidc.sdk.bean.SessionContext; import io.asgardio.java.oidc.sdk.bean.User; import io.asgardio.java.oidc.sdk.exception.SSOAgentException; @@ -35,33 +37,34 @@ public interface OIDCManager { * * @param request Incoming {@link HttpServletRequest}. * @param response Outgoing {@link HttpServletResponse} - * @param state State parameter for the session. + * @return {@link RequestContext} Object containing details regarding the state ID, nonce value for the + * {@link AuthenticationRequest}. * @throws SSOAgentException */ - void sendForLogin(HttpServletRequest request, HttpServletResponse response, String state) throws SSOAgentException; + RequestContext sendForLogin(HttpServletRequest request, HttpServletResponse response) + throws SSOAgentException; /** * Processes the OIDC callback response and extract the authorization code, builds a token request, sends the * token request and parse the token response where the authenticated user info and tokens would be added to the - * {@link AuthenticationInfo} object and returned. + * {@link SessionContext} object and returned. * * @param request Incoming {@link HttpServletRequest}. * @param response Outgoing {@link HttpServletResponse} - * @return {@link AuthenticationInfo} Object containing the authenticated {@link User}, AccessToken, RefreshToken + * @return {@link SessionContext} Object containing the authenticated {@link User}, AccessToken, RefreshToken * and IDToken. * @throws SSOAgentException Upon failed authentication. */ - AuthenticationInfo handleOIDCCallback(HttpServletRequest request, HttpServletResponse response) + SessionContext handleOIDCCallback(HttpServletRequest request, HttpServletResponse response, + RequestContext authenticationContext) throws SSOAgentException; /** * Builds a logout request and redirects. * - * @param authenticationInfo {@link AuthenticationInfo} of the logged in session. - * @param response Outgoing {@link HttpServletResponse} - * @param state State parameter for the session. + * @param sessionContext {@link SessionContext} of the logged in session. + * @param response Outgoing {@link HttpServletResponse} * @throws SSOAgentException */ - void logout(AuthenticationInfo authenticationInfo, HttpServletResponse response, String state) - throws SSOAgentException; + void logout(SessionContext sessionContext, HttpServletResponse response) throws SSOAgentException; } diff --git a/io.asgardio.java.oidc.sdk/src/main/java/io/asgardio/java/oidc/sdk/SSOAgentConstants.java b/io.asgardio.java.oidc.sdk/src/main/java/io/asgardio/java/oidc/sdk/SSOAgentConstants.java index 20e0402..afa637a 100644 --- a/io.asgardio.java.oidc.sdk/src/main/java/io/asgardio/java/oidc/sdk/SSOAgentConstants.java +++ b/io.asgardio.java.oidc.sdk/src/main/java/io/asgardio/java/oidc/sdk/SSOAgentConstants.java @@ -35,6 +35,8 @@ public class SSOAgentConstants { public static final String ID_TOKEN = "id_token"; public static final String SESSION_STATE = "session_state"; public static final String USER = "user"; + public static final String REQUEST_CONTEXT = "request_context"; + public static final String SESSION_CONTEXT = "session_context"; // Keystore file properties. public static final String KEYSTORE_NAME = "keystorename"; @@ -57,7 +59,6 @@ public class SSOAgentConstants { public static final String OIDC_JWKS_ENDPOINT = "jwksEndpoint"; public static final String POST_LOGOUT_REDIRECTION_URI = "postLogoutRedirectURI"; public static final String AUTHENTICATED = "authenticated"; - public static final String AUTHENTICATION_INFO = "authenticationInfo"; public static final String OIDC_OPENID = "openid"; public static final String AZP = "azp"; public static final String TRUSTED_AUDIENCE = "trustedAudience"; diff --git a/io.asgardio.java.oidc.sdk/src/main/java/io/asgardio/java/oidc/sdk/bean/AuthenticationRequest.java b/io.asgardio.java.oidc.sdk/src/main/java/io/asgardio/java/oidc/sdk/bean/AuthenticationRequest.java new file mode 100644 index 0000000..4d0f327 --- /dev/null +++ b/io.asgardio.java.oidc.sdk/src/main/java/io/asgardio/java/oidc/sdk/bean/AuthenticationRequest.java @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2020, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package io.asgardio.java.oidc.sdk.bean; + +import java.io.Serializable; +import java.net.URI; + +/** + * A data model class to define the Authentication Request element. + */ +public class AuthenticationRequest implements Serializable { + + private static final long serialVersionUID = 7931793096680065576L; + + private URI authenticationRequestURI; + private RequestContext requestContext; + + public AuthenticationRequest(URI authenticationRequestURI, RequestContext requestContext) { + + this.authenticationRequestURI = authenticationRequestURI; + this.requestContext = requestContext; + } + + public URI getAuthenticationRequestURI() { + + return authenticationRequestURI; + } + + public void setAuthenticationRequestURI(URI authenticationRequestURI) { + + this.authenticationRequestURI = authenticationRequestURI; + } + + public RequestContext getRequestContext() { + + return requestContext; + } + + public void setRequestContext(RequestContext requestContext) { + + this.requestContext = requestContext; + } +} diff --git a/io.asgardio.java.oidc.sdk/src/main/java/io/asgardio/java/oidc/sdk/bean/RequestContext.java b/io.asgardio.java.oidc.sdk/src/main/java/io/asgardio/java/oidc/sdk/bean/RequestContext.java new file mode 100644 index 0000000..a409ce6 --- /dev/null +++ b/io.asgardio.java.oidc.sdk/src/main/java/io/asgardio/java/oidc/sdk/bean/RequestContext.java @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2020, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package io.asgardio.java.oidc.sdk.bean; + +import com.nimbusds.oauth2.sdk.id.State; +import com.nimbusds.openid.connect.sdk.Nonce; + +import java.io.Serializable; +import java.util.HashMap; +import java.util.Map; + +/** + * A data model class to define the Request Context element. The Request Context object + * should be used to hold the attributes regarding the authentication flow. The Request + * Context and its attributes would be used from the initiation of the authentication + * request until the authentication completion of the user. + */ +public class RequestContext implements Serializable { + + private static final long serialVersionUID = -3980859739213942559L; + + private State state; + private Nonce nonce; + private Map additionalParams = new HashMap<>(); + + public RequestContext(State state, Nonce nonce) { + + this.state = state; + this.nonce = nonce; + } + + public State getState() { + + return state; + } + + public void setState(State state) { + + this.state = state; + } + + public Nonce getNonce() { + + return nonce; + } + + public void setNonce(Nonce nonce) { + + this.nonce = nonce; + } + + public Object getParameter(String key) { + + return additionalParams.get(key); + } + + public void setParameter(String key, Object value) { + + additionalParams.put(key, value); + } +} diff --git a/io.asgardio.java.oidc.sdk/src/main/java/io/asgardio/java/oidc/sdk/bean/AuthenticationInfo.java b/io.asgardio.java.oidc.sdk/src/main/java/io/asgardio/java/oidc/sdk/bean/SessionContext.java similarity index 95% rename from io.asgardio.java.oidc.sdk/src/main/java/io/asgardio/java/oidc/sdk/bean/AuthenticationInfo.java rename to io.asgardio.java.oidc.sdk/src/main/java/io/asgardio/java/oidc/sdk/bean/SessionContext.java index 029158f..d6d325f 100644 --- a/io.asgardio.java.oidc.sdk/src/main/java/io/asgardio/java/oidc/sdk/bean/AuthenticationInfo.java +++ b/io.asgardio.java.oidc.sdk/src/main/java/io/asgardio/java/oidc/sdk/bean/SessionContext.java @@ -25,9 +25,9 @@ import java.io.Serializable; /** - * A data model class to define the Authentication Info element. + * A data model class to define the Session Context element. */ -public class AuthenticationInfo implements Serializable { +public class SessionContext implements Serializable { private static final long serialVersionUID = 976008884476935474L; diff --git a/io.asgardio.java.oidc.sdk/src/main/java/io/asgardio/java/oidc/sdk/request/OIDCRequestBuilder.java b/io.asgardio.java.oidc.sdk/src/main/java/io/asgardio/java/oidc/sdk/request/OIDCRequestBuilder.java index 54d7453..f50d802 100644 --- a/io.asgardio.java.oidc.sdk/src/main/java/io/asgardio/java/oidc/sdk/request/OIDCRequestBuilder.java +++ b/io.asgardio.java.oidc.sdk/src/main/java/io/asgardio/java/oidc/sdk/request/OIDCRequestBuilder.java @@ -27,13 +27,14 @@ import com.nimbusds.openid.connect.sdk.LogoutRequest; import com.nimbusds.openid.connect.sdk.Nonce; import io.asgardio.java.oidc.sdk.OIDCManager; -import io.asgardio.java.oidc.sdk.bean.AuthenticationInfo; +import io.asgardio.java.oidc.sdk.bean.RequestContext; +import io.asgardio.java.oidc.sdk.bean.SessionContext; import io.asgardio.java.oidc.sdk.config.model.OIDCAgentConfig; -import org.apache.commons.lang.StringUtils; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import java.net.URI; +import java.util.UUID; /** * OIDCRequestBuilder is the class responsible for building requests @@ -59,45 +60,40 @@ public OIDCRequestBuilder(OIDCAgentConfig oidcAgentConfig) { } /** - * Returns {@link String} Authorization request. To build the authorization request, + * Returns {@link AuthenticationRequest} Authentication request. To build the authentication request, * {@link OIDCAgentConfig} should contain: *
    + *
  • The client ID *
  • The scope *
  • The callback URI *
  • The authorization endpoint URI *
* - * @param state State parameter. - * @return Authorization request. + * @return Authentication request. */ - public String buildAuthenticationRequest(String state, Nonce nonce) { + public io.asgardio.java.oidc.sdk.bean.AuthenticationRequest buildAuthenticationRequest() { ResponseType responseType = new ResponseType(ResponseType.Value.CODE); ClientID clientID = oidcAgentConfig.getConsumerKey(); Scope authScope = oidcAgentConfig.getScope(); URI callBackURI = oidcAgentConfig.getCallbackUrl(); URI authorizationEndpoint = oidcAgentConfig.getAuthorizeEndpoint(); - State stateParameter = null; - if (StringUtils.isNotBlank(state)) { - stateParameter = new State(state); - } + State state = generateStateParameter(); + Nonce nonce = new Nonce(); + RequestContext requestContext = new RequestContext(state, nonce); AuthenticationRequest authenticationRequest = new AuthenticationRequest.Builder(responseType, authScope, clientID, callBackURI) - .state(stateParameter) + .state(state) .endpointURI(authorizationEndpoint) .nonce(nonce) .build(); - return authenticationRequest.toURI().toString(); + io.asgardio.java.oidc.sdk.bean.AuthenticationRequest authRequest = + new io.asgardio.java.oidc.sdk.bean.AuthenticationRequest(authenticationRequest.toURI(), + requestContext); -// AuthorizationRequest authorizationRequest = new AuthorizationRequest.Builder(responseType, clientID) -// .scope(authScope) -// .state(stateParameter) -// .redirectionURI(callBackURI) -// .endpointURI(authorizationEndpoint) -// .build(); -// return authorizationRequest.toURI().toString(); + return authRequest; } /** @@ -108,20 +104,23 @@ public String buildAuthenticationRequest(String state, Nonce nonce) { *
  • The post logout redirection URI * * - * @param authenticationInfo {@link AuthenticationInfo} object with information of the current LoggedIn session. + * @param authenticationInfo {@link SessionContext} object with information of the current LoggedIn session. * It must include a valid ID token. - * @param state State parameter. * @return Logout request. */ - public String buildLogoutRequest(AuthenticationInfo authenticationInfo, String state) { + public String buildLogoutRequest(SessionContext authenticationInfo) { URI logoutEP = oidcAgentConfig.getLogoutEndpoint(); URI redirectionURI = oidcAgentConfig.getPostLogoutRedirectURI(); JWT jwtIdToken = authenticationInfo.getIdToken(); - State stateParam = null; - if (StringUtils.isNotBlank(state)) { - stateParam = new State(state); - } - return new LogoutRequest(logoutEP, jwtIdToken, redirectionURI, stateParam).toURI().toString(); + State state = generateStateParameter(); + + return new LogoutRequest(logoutEP, jwtIdToken, redirectionURI, state).toURI().toString(); + } + + private State generateStateParameter() { + + UUID uuid = UUID.randomUUID(); + return new State(uuid.toString()); } } diff --git a/io.asgardio.java.oidc.sdk/src/test/java/io/asgardio/java/oidc/sdk/OIDCManagerImplTest.java b/io.asgardio.java.oidc.sdk/src/test/java/io/asgardio/java/oidc/sdk/OIDCManagerImplTest.java index e2d77ad..48f0c9f 100644 --- a/io.asgardio.java.oidc.sdk/src/test/java/io/asgardio/java/oidc/sdk/OIDCManagerImplTest.java +++ b/io.asgardio.java.oidc.sdk/src/test/java/io/asgardio/java/oidc/sdk/OIDCManagerImplTest.java @@ -39,7 +39,7 @@ import com.nimbusds.oauth2.sdk.token.Tokens; import com.nimbusds.openid.connect.sdk.Nonce; import com.nimbusds.openid.connect.sdk.claims.IDTokenClaimsSet; -import io.asgardio.java.oidc.sdk.bean.AuthenticationInfo; +import io.asgardio.java.oidc.sdk.bean.SessionContext; import io.asgardio.java.oidc.sdk.config.model.OIDCAgentConfig; import io.asgardio.java.oidc.sdk.exception.SSOAgentException; import io.asgardio.java.oidc.sdk.request.OIDCRequestResolver; @@ -86,7 +86,7 @@ public class OIDCManagerImplTest extends PowerMockTestCase { OIDCRequestResolver requestResolver; @Mock - AuthenticationInfo authenticationInfo; + SessionContext authenticationInfo; OIDCAgentConfig oidcAgentConfig = new OIDCAgentConfig(); @@ -111,7 +111,7 @@ public void setUp() throws Exception { request = mock(HttpServletRequest.class); response = mock(HttpServletResponse.class); requestResolver = mock(OIDCRequestResolver.class); - authenticationInfo = mock(AuthenticationInfo.class); + authenticationInfo = mock(SessionContext.class); oidcAgentConfig.setConsumerKey(clientID); oidcAgentConfig.setConsumerSecret(clientSecret); @@ -187,8 +187,8 @@ public String toAuthorizationHeader() { when(request.getSession(false)).thenReturn(session); when(session.getAttribute(SSOAgentConstants.NONCE)).thenReturn(new Nonce()); - OIDCManager oidcManager = new OIDCManagerImpl(oidcAgentConfig); - AuthenticationInfo authenticationInfo = oidcManager.handleOIDCCallback(request, response); + OIDCManager oidcManager = new DefaultOIDCManager(oidcAgentConfig); + SessionContext authenticationInfo = oidcManager.handleOIDCCallback(request, response, null); assertEquals(authenticationInfo.getAccessToken(), accessToken); assertEquals(authenticationInfo.getRefreshToken(), refreshToken); @@ -203,7 +203,7 @@ public String toAuthorizationHeader() { public void testLogoutCallbackURI() throws SSOAgentException { oidcAgentConfig.setPostLogoutRedirectURI(null); - OIDCManager oidcManager = new OIDCManagerImpl(oidcAgentConfig); + OIDCManager oidcManager = new DefaultOIDCManager(oidcAgentConfig); oidcManager.logout(authenticationInfo, response, "state"); } @@ -212,7 +212,7 @@ public void testLogoutRedirectURI() throws URISyntaxException, SSOAgentException URI redirectionURI = new URI("http://test/sampleRedirectionURL"); oidcAgentConfig.setPostLogoutRedirectURI(redirectionURI); - OIDCManager oidcManager = new OIDCManagerImpl(oidcAgentConfig); + OIDCManager oidcManager = new DefaultOIDCManager(oidcAgentConfig); oidcManager.logout(authenticationInfo, response, "state"); } diff --git a/io.asgardio.java.oidc.sdk/src/test/java/io/asgardio/java/oidc/sdk/bean/AuthenticationInfoTest.java b/io.asgardio.java.oidc.sdk/src/test/java/io/asgardio/java/oidc/sdk/bean/AuthenticationInfoTest.java index 1f96268..7457ad0 100644 --- a/io.asgardio.java.oidc.sdk/src/test/java/io/asgardio/java/oidc/sdk/bean/AuthenticationInfoTest.java +++ b/io.asgardio.java.oidc.sdk/src/test/java/io/asgardio/java/oidc/sdk/bean/AuthenticationInfoTest.java @@ -36,7 +36,7 @@ */ public class AuthenticationInfoTest { - AuthenticationInfo authenticationInfo = new AuthenticationInfo(); + SessionContext authenticationInfo = new SessionContext(); @Test public void testGetUser() { diff --git a/io.asgardio.java.oidc.sdk/src/test/java/io/asgardio/java/oidc/sdk/request/OIDCRequestBuilderTest.java b/io.asgardio.java.oidc.sdk/src/test/java/io/asgardio/java/oidc/sdk/request/OIDCRequestBuilderTest.java index 29088cf..f7f5f4c 100644 --- a/io.asgardio.java.oidc.sdk/src/test/java/io/asgardio/java/oidc/sdk/request/OIDCRequestBuilderTest.java +++ b/io.asgardio.java.oidc.sdk/src/test/java/io/asgardio/java/oidc/sdk/request/OIDCRequestBuilderTest.java @@ -22,8 +22,9 @@ import com.nimbusds.jwt.JWTParser; import com.nimbusds.oauth2.sdk.Scope; import com.nimbusds.oauth2.sdk.id.ClientID; +import com.nimbusds.oauth2.sdk.id.State; import com.nimbusds.openid.connect.sdk.Nonce; -import io.asgardio.java.oidc.sdk.bean.AuthenticationInfo; +import io.asgardio.java.oidc.sdk.bean.SessionContext; import io.asgardio.java.oidc.sdk.config.model.OIDCAgentConfig; import org.mockito.Mock; import org.powermock.core.classloader.annotations.PrepareForTest; @@ -41,14 +42,14 @@ import static org.mockito.Mockito.when; import static org.testng.Assert.assertEquals; -@PrepareForTest({OIDCAgentConfig.class, AuthenticationInfo.class}) +@PrepareForTest({OIDCAgentConfig.class, SessionContext.class}) public class OIDCRequestBuilderTest extends PowerMockTestCase { @Mock OIDCAgentConfig oidcAgentConfig; @Mock - AuthenticationInfo authenticationInfo; + SessionContext authenticationInfo; @BeforeMethod public void setUp() throws URISyntaxException, ParseException { @@ -64,7 +65,7 @@ public void setUp() throws URISyntaxException, ParseException { "WF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c"); oidcAgentConfig = mock(OIDCAgentConfig.class); - authenticationInfo = mock(AuthenticationInfo.class); + authenticationInfo = mock(SessionContext.class); when(oidcAgentConfig.getConsumerKey()).thenReturn(clientID); when(oidcAgentConfig.getScope()).thenReturn(scope); @@ -89,8 +90,7 @@ public void testBuildAuthorizationRequest() { @Test public void testBuildLogoutRequest() { - String logoutRequest = new OIDCRequestBuilder(oidcAgentConfig).buildLogoutRequest(authenticationInfo, - "state"); + String logoutRequest = new OIDCRequestBuilder(oidcAgentConfig).buildLogoutRequest(authenticationInfo); assertEquals(logoutRequest, "http://test/sampleLogoutEP?state=state&post_logout_redirect_uri=http%3A%2F%2" + "Ftest%2FsampleRedirectionURL&id_token_hint=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3O" + "DkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c"); From eaec555f6dcfbe41d0b1bb76175cc7da938ba387 Mon Sep 17 00:00:00 2001 From: Chamath Samarawickrama Date: Tue, 27 Oct 2020 12:24:54 +0530 Subject: [PATCH 03/13] Implement Logout Request and integrate it to the DefaultOIDCManager. --- .../java/oidc/sdk/DefaultOIDCManager.java | 5 +- .../asgardio/java/oidc/sdk/OIDCManager.java | 8 +-- .../java/oidc/sdk/bean/LogoutContext.java | 30 +++++++++++ .../java/oidc/sdk/bean/LogoutRequest.java | 52 +++++++++++++++++++ .../oidc/sdk/request/OIDCRequestBuilder.java | 20 ++++--- ...lTest.java => DefaultOIDCManagerTest.java} | 0 6 files changed, 101 insertions(+), 14 deletions(-) create mode 100644 io.asgardio.java.oidc.sdk/src/main/java/io/asgardio/java/oidc/sdk/bean/LogoutContext.java create mode 100644 io.asgardio.java.oidc.sdk/src/main/java/io/asgardio/java/oidc/sdk/bean/LogoutRequest.java rename io.asgardio.java.oidc.sdk/src/test/java/io/asgardio/java/oidc/sdk/{OIDCManagerImplTest.java => DefaultOIDCManagerTest.java} (100%) diff --git a/io.asgardio.java.oidc.sdk/src/main/java/io/asgardio/java/oidc/sdk/DefaultOIDCManager.java b/io.asgardio.java.oidc.sdk/src/main/java/io/asgardio/java/oidc/sdk/DefaultOIDCManager.java index 1b8a08f..2c2b54c 100644 --- a/io.asgardio.java.oidc.sdk/src/main/java/io/asgardio/java/oidc/sdk/DefaultOIDCManager.java +++ b/io.asgardio.java.oidc.sdk/src/main/java/io/asgardio/java/oidc/sdk/DefaultOIDCManager.java @@ -43,6 +43,7 @@ import com.nimbusds.oauth2.sdk.token.RefreshToken; import com.nimbusds.openid.connect.sdk.Nonce; import com.nimbusds.openid.connect.sdk.claims.IDTokenClaimsSet; +import io.asgardio.java.oidc.sdk.bean.LogoutRequest; import io.asgardio.java.oidc.sdk.bean.RequestContext; import io.asgardio.java.oidc.sdk.bean.SessionContext; import io.asgardio.java.oidc.sdk.bean.AuthenticationRequest; @@ -141,9 +142,9 @@ public void logout(SessionContext sessionContext, HttpServletResponse response) oidcAgentConfig.setPostLogoutRedirectURI(callbackURI); } OIDCRequestBuilder requestBuilder = new OIDCRequestBuilder(oidcAgentConfig); - String logoutRequest = requestBuilder.buildLogoutRequest(sessionContext); + LogoutRequest logoutRequest = requestBuilder.buildLogoutRequest(sessionContext); try { - response.sendRedirect(logoutRequest); + response.sendRedirect(logoutRequest.getLogoutRequestURI().toString()); } catch (IOException e) { throw new SSOAgentException(SSOAgentConstants.ErrorMessages.SERVLET_CONNECTION.getMessage(), SSOAgentConstants.ErrorMessages.SERVLET_CONNECTION.getCode(), e); diff --git a/io.asgardio.java.oidc.sdk/src/main/java/io/asgardio/java/oidc/sdk/OIDCManager.java b/io.asgardio.java.oidc.sdk/src/main/java/io/asgardio/java/oidc/sdk/OIDCManager.java index 3624c71..2deba4c 100644 --- a/io.asgardio.java.oidc.sdk/src/main/java/io/asgardio/java/oidc/sdk/OIDCManager.java +++ b/io.asgardio.java.oidc.sdk/src/main/java/io/asgardio/java/oidc/sdk/OIDCManager.java @@ -49,15 +49,15 @@ RequestContext sendForLogin(HttpServletRequest request, HttpServletResponse resp * token request and parse the token response where the authenticated user info and tokens would be added to the * {@link SessionContext} object and returned. * - * @param request Incoming {@link HttpServletRequest}. - * @param response Outgoing {@link HttpServletResponse} + * @param request Incoming {@link HttpServletRequest}. + * @param response Outgoing {@link HttpServletResponse}. + * @param requestContext {@link RequestContext} object containing the authentication request related information. * @return {@link SessionContext} Object containing the authenticated {@link User}, AccessToken, RefreshToken * and IDToken. * @throws SSOAgentException Upon failed authentication. */ SessionContext handleOIDCCallback(HttpServletRequest request, HttpServletResponse response, - RequestContext authenticationContext) - throws SSOAgentException; + RequestContext requestContext) throws SSOAgentException; /** * Builds a logout request and redirects. diff --git a/io.asgardio.java.oidc.sdk/src/main/java/io/asgardio/java/oidc/sdk/bean/LogoutContext.java b/io.asgardio.java.oidc.sdk/src/main/java/io/asgardio/java/oidc/sdk/bean/LogoutContext.java new file mode 100644 index 0000000..58a8f80 --- /dev/null +++ b/io.asgardio.java.oidc.sdk/src/main/java/io/asgardio/java/oidc/sdk/bean/LogoutContext.java @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2020, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package io.asgardio.java.oidc.sdk.bean; + +import com.nimbusds.oauth2.sdk.id.State; + +import java.io.Serializable; + +public class LogoutContext implements Serializable { + + private static final long serialVersionUID = -3980859739213942559L; + + private State state; +} diff --git a/io.asgardio.java.oidc.sdk/src/main/java/io/asgardio/java/oidc/sdk/bean/LogoutRequest.java b/io.asgardio.java.oidc.sdk/src/main/java/io/asgardio/java/oidc/sdk/bean/LogoutRequest.java new file mode 100644 index 0000000..0858580 --- /dev/null +++ b/io.asgardio.java.oidc.sdk/src/main/java/io/asgardio/java/oidc/sdk/bean/LogoutRequest.java @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2020, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package io.asgardio.java.oidc.sdk.bean; + +import com.nimbusds.oauth2.sdk.id.State; + +import java.io.Serializable; +import java.net.URI; + +public class LogoutRequest implements Serializable { + + private static final long serialVersionUID = 6184960293632714833L; + + private URI logoutRequestURI; + private State state; + + public URI getLogoutRequestURI() { + + return logoutRequestURI; + } + + public void setLogoutRequestURI(URI logoutRequestURI) { + + this.logoutRequestURI = logoutRequestURI; + } + + public State getState() { + + return state; + } + + public void setState(State state) { + + this.state = state; + } +} diff --git a/io.asgardio.java.oidc.sdk/src/main/java/io/asgardio/java/oidc/sdk/request/OIDCRequestBuilder.java b/io.asgardio.java.oidc.sdk/src/main/java/io/asgardio/java/oidc/sdk/request/OIDCRequestBuilder.java index f50d802..af7a498 100644 --- a/io.asgardio.java.oidc.sdk/src/main/java/io/asgardio/java/oidc/sdk/request/OIDCRequestBuilder.java +++ b/io.asgardio.java.oidc.sdk/src/main/java/io/asgardio/java/oidc/sdk/request/OIDCRequestBuilder.java @@ -60,8 +60,8 @@ public OIDCRequestBuilder(OIDCAgentConfig oidcAgentConfig) { } /** - * Returns {@link AuthenticationRequest} Authentication request. To build the authentication request, - * {@link OIDCAgentConfig} should contain: + * Returns {@link io.asgardio.java.oidc.sdk.bean.AuthenticationRequest} Authentication request. + * To build the authentication request, {@link OIDCAgentConfig} should contain: *
      *
    • The client ID *
    • The scope @@ -97,25 +97,29 @@ public io.asgardio.java.oidc.sdk.bean.AuthenticationRequest buildAuthenticationR } /** - * Returns {@link String} Logout request. To build the logout request, + * Returns {@link io.asgardio.java.oidc.sdk.bean.LogoutRequest} Logout request. To build the logout request, * {@link OIDCAgentConfig} should contain: *
        *
      • The logout endpoint URI *
      • The post logout redirection URI *
      * - * @param authenticationInfo {@link SessionContext} object with information of the current LoggedIn session. - * It must include a valid ID token. + * @param sessionContext {@link SessionContext} object with information of the current LoggedIn session. + * It must include a valid ID token. * @return Logout request. */ - public String buildLogoutRequest(SessionContext authenticationInfo) { + public io.asgardio.java.oidc.sdk.bean.LogoutRequest buildLogoutRequest(SessionContext sessionContext) { + io.asgardio.java.oidc.sdk.bean.LogoutRequest logoutRequest = new io.asgardio.java.oidc.sdk.bean.LogoutRequest(); URI logoutEP = oidcAgentConfig.getLogoutEndpoint(); URI redirectionURI = oidcAgentConfig.getPostLogoutRedirectURI(); - JWT jwtIdToken = authenticationInfo.getIdToken(); + JWT jwtIdToken = sessionContext.getIdToken(); State state = generateStateParameter(); - return new LogoutRequest(logoutEP, jwtIdToken, redirectionURI, state).toURI().toString(); + URI logoutRequestURI = new LogoutRequest(logoutEP, jwtIdToken, redirectionURI, state).toURI(); + logoutRequest.setLogoutRequestURI(logoutRequestURI); + logoutRequest.setState(state); + return logoutRequest; } private State generateStateParameter() { diff --git a/io.asgardio.java.oidc.sdk/src/test/java/io/asgardio/java/oidc/sdk/OIDCManagerImplTest.java b/io.asgardio.java.oidc.sdk/src/test/java/io/asgardio/java/oidc/sdk/DefaultOIDCManagerTest.java similarity index 100% rename from io.asgardio.java.oidc.sdk/src/test/java/io/asgardio/java/oidc/sdk/OIDCManagerImplTest.java rename to io.asgardio.java.oidc.sdk/src/test/java/io/asgardio/java/oidc/sdk/DefaultOIDCManagerTest.java From 0e06a8b26fd4e45f6d7394a810ef66ffca883e97 Mon Sep 17 00:00:00 2001 From: Chamath Samarawickrama Date: Tue, 27 Oct 2020 12:25:08 +0530 Subject: [PATCH 04/13] Fix tests. --- .../java/oidc/sdk/DefaultOIDCManagerTest.java | 17 +++++---- .../sdk/request/OIDCRequestBuilderTest.java | 35 +++++++++++-------- .../src/test/resources/testng.xml | 2 +- 3 files changed, 32 insertions(+), 22 deletions(-) diff --git a/io.asgardio.java.oidc.sdk/src/test/java/io/asgardio/java/oidc/sdk/DefaultOIDCManagerTest.java b/io.asgardio.java.oidc.sdk/src/test/java/io/asgardio/java/oidc/sdk/DefaultOIDCManagerTest.java index 48f0c9f..93eccb4 100644 --- a/io.asgardio.java.oidc.sdk/src/test/java/io/asgardio/java/oidc/sdk/DefaultOIDCManagerTest.java +++ b/io.asgardio.java.oidc.sdk/src/test/java/io/asgardio/java/oidc/sdk/DefaultOIDCManagerTest.java @@ -32,6 +32,7 @@ import com.nimbusds.oauth2.sdk.http.ServletUtils; import com.nimbusds.oauth2.sdk.id.ClientID; import com.nimbusds.oauth2.sdk.id.Issuer; +import com.nimbusds.oauth2.sdk.id.State; import com.nimbusds.oauth2.sdk.id.Subject; import com.nimbusds.oauth2.sdk.token.AccessToken; import com.nimbusds.oauth2.sdk.token.AccessTokenType; @@ -39,6 +40,7 @@ import com.nimbusds.oauth2.sdk.token.Tokens; import com.nimbusds.openid.connect.sdk.Nonce; import com.nimbusds.openid.connect.sdk.claims.IDTokenClaimsSet; +import io.asgardio.java.oidc.sdk.bean.RequestContext; import io.asgardio.java.oidc.sdk.bean.SessionContext; import io.asgardio.java.oidc.sdk.config.model.OIDCAgentConfig; import io.asgardio.java.oidc.sdk.exception.SSOAgentException; @@ -74,7 +76,7 @@ @PrepareForTest({IDTokenValidator.class, IDTokenClaimsSet.class, com.nimbusds.openid.connect.sdk.validators.IDTokenValidator.class}) -public class OIDCManagerImplTest extends PowerMockTestCase { +public class DefaultOIDCManagerTest extends PowerMockTestCase { @Mock HttpServletRequest request; @@ -86,7 +88,7 @@ public class OIDCManagerImplTest extends PowerMockTestCase { OIDCRequestResolver requestResolver; @Mock - SessionContext authenticationInfo; + SessionContext sessionContext; OIDCAgentConfig oidcAgentConfig = new OIDCAgentConfig(); @@ -111,7 +113,7 @@ public void setUp() throws Exception { request = mock(HttpServletRequest.class); response = mock(HttpServletResponse.class); requestResolver = mock(OIDCRequestResolver.class); - authenticationInfo = mock(SessionContext.class); + sessionContext = mock(SessionContext.class); oidcAgentConfig.setConsumerKey(clientID); oidcAgentConfig.setConsumerSecret(clientSecret); @@ -121,7 +123,7 @@ public void setUp() throws Exception { oidcAgentConfig.setScope(scope); oidcAgentConfig.setIssuer(issuer); oidcAgentConfig.setJwksEndpoint(jwksURI); - when(authenticationInfo.getIdToken()).thenReturn(idToken); + when(sessionContext.getIdToken()).thenReturn(idToken); IDTokenClaimsSet claimsSet = mock(IDTokenClaimsSet.class); IDTokenValidator idTokenValidator = mock(IDTokenValidator.class); com.nimbusds.openid.connect.sdk.validators.IDTokenValidator validator = mock( @@ -186,9 +188,10 @@ public String toAuthorizationHeader() { HttpSession session = mock(HttpSession.class); when(request.getSession(false)).thenReturn(session); when(session.getAttribute(SSOAgentConstants.NONCE)).thenReturn(new Nonce()); + RequestContext requestContext = new RequestContext(new State("state"), new Nonce()); OIDCManager oidcManager = new DefaultOIDCManager(oidcAgentConfig); - SessionContext authenticationInfo = oidcManager.handleOIDCCallback(request, response, null); + SessionContext authenticationInfo = oidcManager.handleOIDCCallback(request, response, requestContext); assertEquals(authenticationInfo.getAccessToken(), accessToken); assertEquals(authenticationInfo.getRefreshToken(), refreshToken); @@ -204,7 +207,7 @@ public void testLogoutCallbackURI() throws SSOAgentException { oidcAgentConfig.setPostLogoutRedirectURI(null); OIDCManager oidcManager = new DefaultOIDCManager(oidcAgentConfig); - oidcManager.logout(authenticationInfo, response, "state"); + oidcManager.logout(sessionContext, response); } @Test @@ -213,7 +216,7 @@ public void testLogoutRedirectURI() throws URISyntaxException, SSOAgentException URI redirectionURI = new URI("http://test/sampleRedirectionURL"); oidcAgentConfig.setPostLogoutRedirectURI(redirectionURI); OIDCManager oidcManager = new DefaultOIDCManager(oidcAgentConfig); - oidcManager.logout(authenticationInfo, response, "state"); + oidcManager.logout(sessionContext, response); } @AfterMethod diff --git a/io.asgardio.java.oidc.sdk/src/test/java/io/asgardio/java/oidc/sdk/request/OIDCRequestBuilderTest.java b/io.asgardio.java.oidc.sdk/src/test/java/io/asgardio/java/oidc/sdk/request/OIDCRequestBuilderTest.java index f7f5f4c..f940a09 100644 --- a/io.asgardio.java.oidc.sdk/src/test/java/io/asgardio/java/oidc/sdk/request/OIDCRequestBuilderTest.java +++ b/io.asgardio.java.oidc.sdk/src/test/java/io/asgardio/java/oidc/sdk/request/OIDCRequestBuilderTest.java @@ -22,8 +22,9 @@ import com.nimbusds.jwt.JWTParser; import com.nimbusds.oauth2.sdk.Scope; import com.nimbusds.oauth2.sdk.id.ClientID; -import com.nimbusds.oauth2.sdk.id.State; -import com.nimbusds.openid.connect.sdk.Nonce; +import io.asgardio.java.oidc.sdk.bean.AuthenticationRequest; +import io.asgardio.java.oidc.sdk.bean.LogoutRequest; +import io.asgardio.java.oidc.sdk.bean.RequestContext; import io.asgardio.java.oidc.sdk.bean.SessionContext; import io.asgardio.java.oidc.sdk.config.model.OIDCAgentConfig; import org.mockito.Mock; @@ -49,7 +50,7 @@ public class OIDCRequestBuilderTest extends PowerMockTestCase { OIDCAgentConfig oidcAgentConfig; @Mock - SessionContext authenticationInfo; + SessionContext sessionContext; @BeforeMethod public void setUp() throws URISyntaxException, ParseException { @@ -65,7 +66,7 @@ public void setUp() throws URISyntaxException, ParseException { "WF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c"); oidcAgentConfig = mock(OIDCAgentConfig.class); - authenticationInfo = mock(SessionContext.class); + sessionContext = mock(SessionContext.class); when(oidcAgentConfig.getConsumerKey()).thenReturn(clientID); when(oidcAgentConfig.getScope()).thenReturn(scope); @@ -73,27 +74,33 @@ public void setUp() throws URISyntaxException, ParseException { when(oidcAgentConfig.getAuthorizeEndpoint()).thenReturn(authorizationEndpoint); when(oidcAgentConfig.getLogoutEndpoint()).thenReturn(logoutEP); when(oidcAgentConfig.getPostLogoutRedirectURI()).thenReturn(redirectionURI); - when(authenticationInfo.getIdToken()).thenReturn(idToken); + when(sessionContext.getIdToken()).thenReturn(idToken); } @Test public void testBuildAuthorizationRequest() { - Nonce nonce = new Nonce("sampleNonce"); - String authorizationRequest = - new OIDCRequestBuilder(oidcAgentConfig).buildAuthenticationRequest("state", nonce); - assertEquals(authorizationRequest, + AuthenticationRequest authenticationRequest = + new OIDCRequestBuilder(oidcAgentConfig).buildAuthenticationRequest(); + RequestContext requestContext = authenticationRequest.getRequestContext(); + String nonce = requestContext.getNonce().getValue(); + String state = requestContext.getState().getValue(); + + assertEquals(authenticationRequest.getAuthenticationRequestURI().toString(), "http://test/sampleAuthzEP?scope=sampleScope1+openid&response_type=code&redirect_uri=http" + - "%3A%2F%2Ftest%2FsampleCallbackURL&state=state&nonce=sampleNonce&client_id=sampleClientId"); + "%3A%2F%2Ftest%2FsampleCallbackURL&state=" + state + "&nonce=" + nonce + "&client_id" + + "=sampleClientId"); } @Test public void testBuildLogoutRequest() { - String logoutRequest = new OIDCRequestBuilder(oidcAgentConfig).buildLogoutRequest(authenticationInfo); - assertEquals(logoutRequest, "http://test/sampleLogoutEP?state=state&post_logout_redirect_uri=http%3A%2F%2" + - "Ftest%2FsampleRedirectionURL&id_token_hint=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3O" + - "DkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c"); + LogoutRequest logoutRequest = new OIDCRequestBuilder(oidcAgentConfig).buildLogoutRequest(sessionContext); + String state = logoutRequest.getState().getValue(); + assertEquals(logoutRequest.getLogoutRequestURI().toString(), "http://test/sampleLogoutEP?state=" + state + + "&post_logout_redirect_uri=http%3A%2F%2Ftest%2FsampleRedirectionURL&id_token_hint=eyJhbGciOiJIUzI1NiI" + + "sInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwR" + + "JSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c"); } @ObjectFactory diff --git a/io.asgardio.java.oidc.sdk/src/test/resources/testng.xml b/io.asgardio.java.oidc.sdk/src/test/resources/testng.xml index 575c106..fc82eaf 100644 --- a/io.asgardio.java.oidc.sdk/src/test/resources/testng.xml +++ b/io.asgardio.java.oidc.sdk/src/test/resources/testng.xml @@ -29,7 +29,7 @@ - + From d37f3fc3db6952e035691eb8c02855a564fea256 Mon Sep 17 00:00:00 2001 From: Chamath Samarawickrama Date: Tue, 27 Oct 2020 12:25:55 +0530 Subject: [PATCH 05/13] Remove LogoutContext class. --- .../java/oidc/sdk/bean/LogoutContext.java | 30 ------------------- 1 file changed, 30 deletions(-) delete mode 100644 io.asgardio.java.oidc.sdk/src/main/java/io/asgardio/java/oidc/sdk/bean/LogoutContext.java diff --git a/io.asgardio.java.oidc.sdk/src/main/java/io/asgardio/java/oidc/sdk/bean/LogoutContext.java b/io.asgardio.java.oidc.sdk/src/main/java/io/asgardio/java/oidc/sdk/bean/LogoutContext.java deleted file mode 100644 index 58a8f80..0000000 --- a/io.asgardio.java.oidc.sdk/src/main/java/io/asgardio/java/oidc/sdk/bean/LogoutContext.java +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright (c) 2020, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. - * - * WSO2 Inc. licenses this file to you under the Apache License, - * Version 2.0 (the "License"); you may not use this file except - * in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -package io.asgardio.java.oidc.sdk.bean; - -import com.nimbusds.oauth2.sdk.id.State; - -import java.io.Serializable; - -public class LogoutContext implements Serializable { - - private static final long serialVersionUID = -3980859739213942559L; - - private State state; -} From 5ca179e02efe574f15860a9fec5afc85d1d48962 Mon Sep 17 00:00:00 2001 From: Chamath Samarawickrama Date: Tue, 27 Oct 2020 13:14:48 +0530 Subject: [PATCH 06/13] Restructure packages. --- .../java/oidc/sdk/DefaultOIDCManager.java | 4 ++-- .../io/asgardio/java/oidc/sdk/OIDCManager.java | 2 +- .../java/oidc/sdk/bean/RequestContext.java | 10 ++++++++-- .../java/oidc/sdk/bean/SessionContext.java | 10 +++++++++- .../java/oidc/sdk/request/OIDCRequestBuilder.java | 15 ++++++++------- .../model}/AuthenticationRequest.java | 4 +++- .../{bean => request/model}/LogoutRequest.java | 5 ++++- .../oidc/sdk/request/OIDCRequestBuilderTest.java | 4 ++-- 8 files changed, 37 insertions(+), 17 deletions(-) rename io.asgardio.java.oidc.sdk/src/main/java/io/asgardio/java/oidc/sdk/{bean => request/model}/AuthenticationRequest.java (94%) rename io.asgardio.java.oidc.sdk/src/main/java/io/asgardio/java/oidc/sdk/{bean => request/model}/LogoutRequest.java (91%) diff --git a/io.asgardio.java.oidc.sdk/src/main/java/io/asgardio/java/oidc/sdk/DefaultOIDCManager.java b/io.asgardio.java.oidc.sdk/src/main/java/io/asgardio/java/oidc/sdk/DefaultOIDCManager.java index 2c2b54c..1c56b9b 100644 --- a/io.asgardio.java.oidc.sdk/src/main/java/io/asgardio/java/oidc/sdk/DefaultOIDCManager.java +++ b/io.asgardio.java.oidc.sdk/src/main/java/io/asgardio/java/oidc/sdk/DefaultOIDCManager.java @@ -43,10 +43,10 @@ import com.nimbusds.oauth2.sdk.token.RefreshToken; import com.nimbusds.openid.connect.sdk.Nonce; import com.nimbusds.openid.connect.sdk.claims.IDTokenClaimsSet; -import io.asgardio.java.oidc.sdk.bean.LogoutRequest; +import io.asgardio.java.oidc.sdk.request.model.LogoutRequest; import io.asgardio.java.oidc.sdk.bean.RequestContext; import io.asgardio.java.oidc.sdk.bean.SessionContext; -import io.asgardio.java.oidc.sdk.bean.AuthenticationRequest; +import io.asgardio.java.oidc.sdk.request.model.AuthenticationRequest; import io.asgardio.java.oidc.sdk.bean.User; import io.asgardio.java.oidc.sdk.config.model.OIDCAgentConfig; import io.asgardio.java.oidc.sdk.exception.SSOAgentClientException; diff --git a/io.asgardio.java.oidc.sdk/src/main/java/io/asgardio/java/oidc/sdk/OIDCManager.java b/io.asgardio.java.oidc.sdk/src/main/java/io/asgardio/java/oidc/sdk/OIDCManager.java index 2deba4c..236a86b 100644 --- a/io.asgardio.java.oidc.sdk/src/main/java/io/asgardio/java/oidc/sdk/OIDCManager.java +++ b/io.asgardio.java.oidc.sdk/src/main/java/io/asgardio/java/oidc/sdk/OIDCManager.java @@ -18,7 +18,7 @@ package io.asgardio.java.oidc.sdk; -import io.asgardio.java.oidc.sdk.bean.AuthenticationRequest; +import io.asgardio.java.oidc.sdk.request.model.AuthenticationRequest; import io.asgardio.java.oidc.sdk.bean.RequestContext; import io.asgardio.java.oidc.sdk.bean.SessionContext; import io.asgardio.java.oidc.sdk.bean.User; diff --git a/io.asgardio.java.oidc.sdk/src/main/java/io/asgardio/java/oidc/sdk/bean/RequestContext.java b/io.asgardio.java.oidc.sdk/src/main/java/io/asgardio/java/oidc/sdk/bean/RequestContext.java index a409ce6..a69655b 100644 --- a/io.asgardio.java.oidc.sdk/src/main/java/io/asgardio/java/oidc/sdk/bean/RequestContext.java +++ b/io.asgardio.java.oidc.sdk/src/main/java/io/asgardio/java/oidc/sdk/bean/RequestContext.java @@ -27,8 +27,14 @@ /** * A data model class to define the Request Context element. The Request Context object - * should be used to hold the attributes regarding the authentication flow. The Request - * Context and its attributes would be used from the initiation of the authentication + * should be used to hold the attributes regarding the authentication flow. These include the attributes: + *
        + *
      • The state parameter + *
      • The nonce value + *
      • Additional custom parameters + *
      + *

      + * The Request Context and its attributes would be used from the initiation of the authentication * request until the authentication completion of the user. */ public class RequestContext implements Serializable { diff --git a/io.asgardio.java.oidc.sdk/src/main/java/io/asgardio/java/oidc/sdk/bean/SessionContext.java b/io.asgardio.java.oidc.sdk/src/main/java/io/asgardio/java/oidc/sdk/bean/SessionContext.java index d6d325f..497e240 100644 --- a/io.asgardio.java.oidc.sdk/src/main/java/io/asgardio/java/oidc/sdk/bean/SessionContext.java +++ b/io.asgardio.java.oidc.sdk/src/main/java/io/asgardio/java/oidc/sdk/bean/SessionContext.java @@ -25,7 +25,15 @@ import java.io.Serializable; /** - * A data model class to define the Session Context element. + * A data model class to define the Session Context element. The Session Context object should be used to hold the + * attributes of the logged in user session. These include the attributes: + *

        + *
      • The Authenticated User + *
      • Access Token + *
      • Refresh Token + *
      • ID Token + *
      + *

      */ public class SessionContext implements Serializable { diff --git a/io.asgardio.java.oidc.sdk/src/main/java/io/asgardio/java/oidc/sdk/request/OIDCRequestBuilder.java b/io.asgardio.java.oidc.sdk/src/main/java/io/asgardio/java/oidc/sdk/request/OIDCRequestBuilder.java index af7a498..d7b09f3 100644 --- a/io.asgardio.java.oidc.sdk/src/main/java/io/asgardio/java/oidc/sdk/request/OIDCRequestBuilder.java +++ b/io.asgardio.java.oidc.sdk/src/main/java/io/asgardio/java/oidc/sdk/request/OIDCRequestBuilder.java @@ -60,7 +60,7 @@ public OIDCRequestBuilder(OIDCAgentConfig oidcAgentConfig) { } /** - * Returns {@link io.asgardio.java.oidc.sdk.bean.AuthenticationRequest} Authentication request. + * Returns {@link io.asgardio.java.oidc.sdk.request.model.AuthenticationRequest} Authentication request. * To build the authentication request, {@link OIDCAgentConfig} should contain: *

        *
      • The client ID @@ -71,7 +71,7 @@ public OIDCRequestBuilder(OIDCAgentConfig oidcAgentConfig) { * * @return Authentication request. */ - public io.asgardio.java.oidc.sdk.bean.AuthenticationRequest buildAuthenticationRequest() { + public io.asgardio.java.oidc.sdk.request.model.AuthenticationRequest buildAuthenticationRequest() { ResponseType responseType = new ResponseType(ResponseType.Value.CODE); ClientID clientID = oidcAgentConfig.getConsumerKey(); @@ -89,15 +89,15 @@ public io.asgardio.java.oidc.sdk.bean.AuthenticationRequest buildAuthenticationR .nonce(nonce) .build(); - io.asgardio.java.oidc.sdk.bean.AuthenticationRequest authRequest = - new io.asgardio.java.oidc.sdk.bean.AuthenticationRequest(authenticationRequest.toURI(), + io.asgardio.java.oidc.sdk.request.model.AuthenticationRequest authRequest = + new io.asgardio.java.oidc.sdk.request.model.AuthenticationRequest(authenticationRequest.toURI(), requestContext); return authRequest; } /** - * Returns {@link io.asgardio.java.oidc.sdk.bean.LogoutRequest} Logout request. To build the logout request, + * Returns {@link io.asgardio.java.oidc.sdk.request.model.LogoutRequest} Logout request. To build the logout request, * {@link OIDCAgentConfig} should contain: *
          *
        • The logout endpoint URI @@ -108,9 +108,10 @@ public io.asgardio.java.oidc.sdk.bean.AuthenticationRequest buildAuthenticationR * It must include a valid ID token. * @return Logout request. */ - public io.asgardio.java.oidc.sdk.bean.LogoutRequest buildLogoutRequest(SessionContext sessionContext) { + public io.asgardio.java.oidc.sdk.request.model.LogoutRequest buildLogoutRequest(SessionContext sessionContext) { - io.asgardio.java.oidc.sdk.bean.LogoutRequest logoutRequest = new io.asgardio.java.oidc.sdk.bean.LogoutRequest(); + io.asgardio.java.oidc.sdk.request.model.LogoutRequest + logoutRequest = new io.asgardio.java.oidc.sdk.request.model.LogoutRequest(); URI logoutEP = oidcAgentConfig.getLogoutEndpoint(); URI redirectionURI = oidcAgentConfig.getPostLogoutRedirectURI(); JWT jwtIdToken = sessionContext.getIdToken(); diff --git a/io.asgardio.java.oidc.sdk/src/main/java/io/asgardio/java/oidc/sdk/bean/AuthenticationRequest.java b/io.asgardio.java.oidc.sdk/src/main/java/io/asgardio/java/oidc/sdk/request/model/AuthenticationRequest.java similarity index 94% rename from io.asgardio.java.oidc.sdk/src/main/java/io/asgardio/java/oidc/sdk/bean/AuthenticationRequest.java rename to io.asgardio.java.oidc.sdk/src/main/java/io/asgardio/java/oidc/sdk/request/model/AuthenticationRequest.java index 4d0f327..7f4c200 100644 --- a/io.asgardio.java.oidc.sdk/src/main/java/io/asgardio/java/oidc/sdk/bean/AuthenticationRequest.java +++ b/io.asgardio.java.oidc.sdk/src/main/java/io/asgardio/java/oidc/sdk/request/model/AuthenticationRequest.java @@ -16,7 +16,9 @@ * under the License. */ -package io.asgardio.java.oidc.sdk.bean; +package io.asgardio.java.oidc.sdk.request.model; + +import io.asgardio.java.oidc.sdk.bean.RequestContext; import java.io.Serializable; import java.net.URI; diff --git a/io.asgardio.java.oidc.sdk/src/main/java/io/asgardio/java/oidc/sdk/bean/LogoutRequest.java b/io.asgardio.java.oidc.sdk/src/main/java/io/asgardio/java/oidc/sdk/request/model/LogoutRequest.java similarity index 91% rename from io.asgardio.java.oidc.sdk/src/main/java/io/asgardio/java/oidc/sdk/bean/LogoutRequest.java rename to io.asgardio.java.oidc.sdk/src/main/java/io/asgardio/java/oidc/sdk/request/model/LogoutRequest.java index 0858580..f74e763 100644 --- a/io.asgardio.java.oidc.sdk/src/main/java/io/asgardio/java/oidc/sdk/bean/LogoutRequest.java +++ b/io.asgardio.java.oidc.sdk/src/main/java/io/asgardio/java/oidc/sdk/request/model/LogoutRequest.java @@ -16,13 +16,16 @@ * under the License. */ -package io.asgardio.java.oidc.sdk.bean; +package io.asgardio.java.oidc.sdk.request.model; import com.nimbusds.oauth2.sdk.id.State; import java.io.Serializable; import java.net.URI; +/** + * A data model class to define the Logout Request element. + */ public class LogoutRequest implements Serializable { private static final long serialVersionUID = 6184960293632714833L; diff --git a/io.asgardio.java.oidc.sdk/src/test/java/io/asgardio/java/oidc/sdk/request/OIDCRequestBuilderTest.java b/io.asgardio.java.oidc.sdk/src/test/java/io/asgardio/java/oidc/sdk/request/OIDCRequestBuilderTest.java index f940a09..f1046bb 100644 --- a/io.asgardio.java.oidc.sdk/src/test/java/io/asgardio/java/oidc/sdk/request/OIDCRequestBuilderTest.java +++ b/io.asgardio.java.oidc.sdk/src/test/java/io/asgardio/java/oidc/sdk/request/OIDCRequestBuilderTest.java @@ -22,8 +22,8 @@ import com.nimbusds.jwt.JWTParser; import com.nimbusds.oauth2.sdk.Scope; import com.nimbusds.oauth2.sdk.id.ClientID; -import io.asgardio.java.oidc.sdk.bean.AuthenticationRequest; -import io.asgardio.java.oidc.sdk.bean.LogoutRequest; +import io.asgardio.java.oidc.sdk.request.model.AuthenticationRequest; +import io.asgardio.java.oidc.sdk.request.model.LogoutRequest; import io.asgardio.java.oidc.sdk.bean.RequestContext; import io.asgardio.java.oidc.sdk.bean.SessionContext; import io.asgardio.java.oidc.sdk.config.model.OIDCAgentConfig; From b6b4ead22428c159f343f092f20067ad878d51ab Mon Sep 17 00:00:00 2001 From: Chamath Samarawickrama Date: Wed, 28 Oct 2020 13:29:56 +0530 Subject: [PATCH 07/13] Add HTTP Session Based OIDC Processor. --- .../java/oidc/sdk/DefaultOIDCManager.java | 7 +- .../oidc/sdk/DefaultOIDCManagerFactory.java | 40 +++++ .../sdk/HTTPSessionBasedOIDCProcessor.java | 141 ++++++++++++++++++ .../asgardio/java/oidc/sdk/OIDCManager.java | 6 +- .../java/oidc/sdk/bean/RequestContext.java | 36 +++++ .../sdk/config/model/OIDCAgentConfig.java | 1 - .../oidc/sdk/request/OIDCRequestBuilder.java | 9 +- .../oidc/sdk/request/model/LogoutRequest.java | 18 ++- .../HTTPSessionBasedOIDCProcessorTest.java | 141 ++++++++++++++++++ .../sdk/request/OIDCRequestBuilderTest.java | 7 +- 10 files changed, 386 insertions(+), 20 deletions(-) create mode 100644 io.asgardio.java.oidc.sdk/src/main/java/io/asgardio/java/oidc/sdk/DefaultOIDCManagerFactory.java create mode 100644 io.asgardio.java.oidc.sdk/src/main/java/io/asgardio/java/oidc/sdk/HTTPSessionBasedOIDCProcessor.java create mode 100644 io.asgardio.java.oidc.sdk/src/test/java/io/asgardio/java/oidc/sdk/HTTPSessionBasedOIDCProcessorTest.java diff --git a/io.asgardio.java.oidc.sdk/src/main/java/io/asgardio/java/oidc/sdk/DefaultOIDCManager.java b/io.asgardio.java.oidc.sdk/src/main/java/io/asgardio/java/oidc/sdk/DefaultOIDCManager.java index 1c56b9b..c7ac7f3 100644 --- a/io.asgardio.java.oidc.sdk/src/main/java/io/asgardio/java/oidc/sdk/DefaultOIDCManager.java +++ b/io.asgardio.java.oidc.sdk/src/main/java/io/asgardio/java/oidc/sdk/DefaultOIDCManager.java @@ -43,10 +43,8 @@ import com.nimbusds.oauth2.sdk.token.RefreshToken; import com.nimbusds.openid.connect.sdk.Nonce; import com.nimbusds.openid.connect.sdk.claims.IDTokenClaimsSet; -import io.asgardio.java.oidc.sdk.request.model.LogoutRequest; import io.asgardio.java.oidc.sdk.bean.RequestContext; import io.asgardio.java.oidc.sdk.bean.SessionContext; -import io.asgardio.java.oidc.sdk.request.model.AuthenticationRequest; import io.asgardio.java.oidc.sdk.bean.User; import io.asgardio.java.oidc.sdk.config.model.OIDCAgentConfig; import io.asgardio.java.oidc.sdk.exception.SSOAgentClientException; @@ -54,6 +52,8 @@ import io.asgardio.java.oidc.sdk.exception.SSOAgentServerException; import io.asgardio.java.oidc.sdk.request.OIDCRequestBuilder; import io.asgardio.java.oidc.sdk.request.OIDCRequestResolver; +import io.asgardio.java.oidc.sdk.request.model.AuthenticationRequest; +import io.asgardio.java.oidc.sdk.request.model.LogoutRequest; import io.asgardio.java.oidc.sdk.validators.IDTokenValidator; import net.minidev.json.JSONObject; import org.apache.commons.lang.StringUtils; @@ -134,7 +134,7 @@ public SessionContext handleOIDCCallback(HttpServletRequest request, HttpServlet * {@inheritDoc} */ @Override - public void logout(SessionContext sessionContext, HttpServletResponse response) throws SSOAgentException { + public RequestContext logout(SessionContext sessionContext, HttpServletResponse response) throws SSOAgentException { if (oidcAgentConfig.getPostLogoutRedirectURI() == null) { logger.info("postLogoutRedirectURI is not configured. Using the callbackURL instead."); @@ -149,6 +149,7 @@ public void logout(SessionContext sessionContext, HttpServletResponse response) throw new SSOAgentException(SSOAgentConstants.ErrorMessages.SERVLET_CONNECTION.getMessage(), SSOAgentConstants.ErrorMessages.SERVLET_CONNECTION.getCode(), e); } + return logoutRequest.getRequestContext(); } private boolean handleAuthentication(final HttpServletRequest request, SessionContext authenticationInfo, diff --git a/io.asgardio.java.oidc.sdk/src/main/java/io/asgardio/java/oidc/sdk/DefaultOIDCManagerFactory.java b/io.asgardio.java.oidc.sdk/src/main/java/io/asgardio/java/oidc/sdk/DefaultOIDCManagerFactory.java new file mode 100644 index 0000000..d95b9c1 --- /dev/null +++ b/io.asgardio.java.oidc.sdk/src/main/java/io/asgardio/java/oidc/sdk/DefaultOIDCManagerFactory.java @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2020, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package io.asgardio.java.oidc.sdk; + +import io.asgardio.java.oidc.sdk.config.model.OIDCAgentConfig; +import io.asgardio.java.oidc.sdk.exception.SSOAgentClientException; + +/** + * A factory to create Default OIDC Manger objects based on a OIDCAgentConfig. + */ +public class DefaultOIDCManagerFactory { + + /** + * Creates a new {@link DefaultOIDCManager} object. + * + * @param oidcAgentConfig The {@link OIDCAgentConfig} object containing the client specific details. + * @return The DefaultOIDCManager instance. + * @throws SSOAgentClientException If the OIDCAgentConfig validation is unsuccessful. + */ + public static OIDCManager createOIDCManager(OIDCAgentConfig oidcAgentConfig) throws SSOAgentClientException { + + return new DefaultOIDCManager(oidcAgentConfig); + } +} diff --git a/io.asgardio.java.oidc.sdk/src/main/java/io/asgardio/java/oidc/sdk/HTTPSessionBasedOIDCProcessor.java b/io.asgardio.java.oidc.sdk/src/main/java/io/asgardio/java/oidc/sdk/HTTPSessionBasedOIDCProcessor.java new file mode 100644 index 0000000..c134891 --- /dev/null +++ b/io.asgardio.java.oidc.sdk/src/main/java/io/asgardio/java/oidc/sdk/HTTPSessionBasedOIDCProcessor.java @@ -0,0 +1,141 @@ +/* + * Copyright (c) 2020, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package io.asgardio.java.oidc.sdk; + +import io.asgardio.java.oidc.sdk.bean.RequestContext; +import io.asgardio.java.oidc.sdk.bean.SessionContext; +import io.asgardio.java.oidc.sdk.config.model.OIDCAgentConfig; +import io.asgardio.java.oidc.sdk.exception.SSOAgentClientException; +import io.asgardio.java.oidc.sdk.exception.SSOAgentException; +import io.asgardio.java.oidc.sdk.exception.SSOAgentServerException; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpSession; + +/** + * A wrapper class for the {@link DefaultOIDCManager} that provides + * the functionality defined by the {@link OIDCManager} with using + * HTTP sessions as the storage entity for the {@link RequestContext} + * and {@link SessionContext} information. + */ +public class HTTPSessionBasedOIDCProcessor { + + private static final Logger logger = LogManager.getLogger(HTTPSessionBasedOIDCProcessor.class); + + private final OIDCManager defaultOIDCManager; + + public HTTPSessionBasedOIDCProcessor(OIDCAgentConfig oidcAgentConfig) throws SSOAgentClientException { + + defaultOIDCManager = DefaultOIDCManagerFactory.createOIDCManager(oidcAgentConfig); + } + + /** + * Builds an authentication request and redirects. Information + * regarding the authentication session would be retrieved via + * {@link RequestContext} object and then, would be written to + * the http session. + * + * @param request Incoming {@link HttpServletRequest}. + * @param response Outgoing {@link HttpServletResponse}. + * @throws SSOAgentException + */ + public void sendForLogin(HttpServletRequest request, HttpServletResponse response) + throws SSOAgentException { + + HttpSession session = request.getSession(); + RequestContext requestContext = defaultOIDCManager.sendForLogin(request, response); + session.setAttribute(SSOAgentConstants.REQUEST_CONTEXT, requestContext); + } + + /** + * Processes the OIDC callback response and extract the authorization + * code, builds a token request, sends the token request and parse + * the token response where the authenticated user info and tokens + * would be added to the {@link SessionContext} object and written + * into the available http session. + * + * @param request Incoming {@link HttpServletRequest}. + * @param response Outgoing {@link HttpServletResponse}. + * @throws SSOAgentException Upon failed authentication. + */ + public void handleOIDCCallback(HttpServletRequest request, HttpServletResponse response) throws SSOAgentException { + + RequestContext requestContext = getRequestContext(request); + clearSession(request); + SessionContext sessionContext = defaultOIDCManager.handleOIDCCallback(request, response, requestContext); + + if (sessionContext != null) { + clearSession(request); + HttpSession session = request.getSession(); + session.setAttribute(SSOAgentConstants.SESSION_CONTEXT, sessionContext); + } else { + throw new SSOAgentServerException("Null session context."); + } + } + + /** + * Builds a logout request and redirects. + * + * @param request Incoming {@link HttpServletRequest}. + * @param response Outgoing {@link HttpServletResponse} + * @throws SSOAgentException + */ + public void logout(HttpServletRequest request, HttpServletResponse response) throws SSOAgentException { + + SessionContext sessionContext = getSessionContext(request); + clearSession(request); + RequestContext requestContext = defaultOIDCManager.logout(sessionContext, response); + HttpSession session = request.getSession(); + session.setAttribute(SSOAgentConstants.REQUEST_CONTEXT, requestContext); + } + + private void clearSession(HttpServletRequest request) { + + HttpSession session = request.getSession(false); + + if (session != null) { + session.invalidate(); + } + } + + private RequestContext getRequestContext(HttpServletRequest request) throws SSOAgentServerException { + + HttpSession session = request.getSession(false); + + if (session != null && session.getAttribute(SSOAgentConstants.REQUEST_CONTEXT) != null) { + return (RequestContext) request.getSession(false) + .getAttribute(SSOAgentConstants.REQUEST_CONTEXT); + } + throw new SSOAgentServerException("Request context null."); + } + + private SessionContext getSessionContext(HttpServletRequest request) throws SSOAgentServerException { + + HttpSession session = request.getSession(false); + + if (session != null && session.getAttribute(SSOAgentConstants.SESSION_CONTEXT) != null) { + return (SessionContext) request.getSession(false) + .getAttribute(SSOAgentConstants.SESSION_CONTEXT); + } + throw new SSOAgentServerException("Session context null."); + } +} diff --git a/io.asgardio.java.oidc.sdk/src/main/java/io/asgardio/java/oidc/sdk/OIDCManager.java b/io.asgardio.java.oidc.sdk/src/main/java/io/asgardio/java/oidc/sdk/OIDCManager.java index 236a86b..b6b9d97 100644 --- a/io.asgardio.java.oidc.sdk/src/main/java/io/asgardio/java/oidc/sdk/OIDCManager.java +++ b/io.asgardio.java.oidc.sdk/src/main/java/io/asgardio/java/oidc/sdk/OIDCManager.java @@ -18,11 +18,11 @@ package io.asgardio.java.oidc.sdk; -import io.asgardio.java.oidc.sdk.request.model.AuthenticationRequest; import io.asgardio.java.oidc.sdk.bean.RequestContext; import io.asgardio.java.oidc.sdk.bean.SessionContext; import io.asgardio.java.oidc.sdk.bean.User; import io.asgardio.java.oidc.sdk.exception.SSOAgentException; +import io.asgardio.java.oidc.sdk.request.model.AuthenticationRequest; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @@ -64,7 +64,9 @@ SessionContext handleOIDCCallback(HttpServletRequest request, HttpServletRespons * * @param sessionContext {@link SessionContext} of the logged in session. * @param response Outgoing {@link HttpServletResponse} + * @return {@link RequestContext} Object containing details regarding the state ID and other parameters of the + * {@link io.asgardio.java.oidc.sdk.request.model.LogoutRequest}. * @throws SSOAgentException */ - void logout(SessionContext sessionContext, HttpServletResponse response) throws SSOAgentException; + RequestContext logout(SessionContext sessionContext, HttpServletResponse response) throws SSOAgentException; } diff --git a/io.asgardio.java.oidc.sdk/src/main/java/io/asgardio/java/oidc/sdk/bean/RequestContext.java b/io.asgardio.java.oidc.sdk/src/main/java/io/asgardio/java/oidc/sdk/bean/RequestContext.java index a69655b..1b9656d 100644 --- a/io.asgardio.java.oidc.sdk/src/main/java/io/asgardio/java/oidc/sdk/bean/RequestContext.java +++ b/io.asgardio.java.oidc.sdk/src/main/java/io/asgardio/java/oidc/sdk/bean/RequestContext.java @@ -51,31 +51,67 @@ public RequestContext(State state, Nonce nonce) { this.nonce = nonce; } + public RequestContext() { + + } + + /** + * Returns the state. + * + * @return {@link State} object for the request. + */ public State getState() { return state; } + /** + * Sets the state. + * + * @param state The state object. + */ public void setState(State state) { this.state = state; } + /** + * Returns the nonce. + * + * @return {@link Nonce} object for the request. + */ public Nonce getNonce() { return nonce; } + /** + * Sets the nonce. + * + * @param nonce The nonce object. + */ public void setNonce(Nonce nonce) { this.nonce = nonce; } + /** + * Returns the object for the particular key. + * + * @param key The String value of the key. + * @return The additional parameter object in the request for the particular key. + */ public Object getParameter(String key) { return additionalParams.get(key); } + /** + * Sets additional parameter to the Request Context. + * + * @param key The key of the parameter. + * @param value The value of the parameter. + */ public void setParameter(String key, Object value) { additionalParams.put(key, value); diff --git a/io.asgardio.java.oidc.sdk/src/main/java/io/asgardio/java/oidc/sdk/config/model/OIDCAgentConfig.java b/io.asgardio.java.oidc.sdk/src/main/java/io/asgardio/java/oidc/sdk/config/model/OIDCAgentConfig.java index a8563bf..bd897d7 100644 --- a/io.asgardio.java.oidc.sdk/src/main/java/io/asgardio/java/oidc/sdk/config/model/OIDCAgentConfig.java +++ b/io.asgardio.java.oidc.sdk/src/main/java/io/asgardio/java/oidc/sdk/config/model/OIDCAgentConfig.java @@ -249,7 +249,6 @@ public void setIssuer(Issuer issuer) { this.issuer = issuer; } - public Set getTrustedAudience() { return trustedAudience; diff --git a/io.asgardio.java.oidc.sdk/src/main/java/io/asgardio/java/oidc/sdk/request/OIDCRequestBuilder.java b/io.asgardio.java.oidc.sdk/src/main/java/io/asgardio/java/oidc/sdk/request/OIDCRequestBuilder.java index d7b09f3..54d8ec4 100644 --- a/io.asgardio.java.oidc.sdk/src/main/java/io/asgardio/java/oidc/sdk/request/OIDCRequestBuilder.java +++ b/io.asgardio.java.oidc.sdk/src/main/java/io/asgardio/java/oidc/sdk/request/OIDCRequestBuilder.java @@ -110,17 +110,16 @@ public io.asgardio.java.oidc.sdk.request.model.AuthenticationRequest buildAuthen */ public io.asgardio.java.oidc.sdk.request.model.LogoutRequest buildLogoutRequest(SessionContext sessionContext) { - io.asgardio.java.oidc.sdk.request.model.LogoutRequest - logoutRequest = new io.asgardio.java.oidc.sdk.request.model.LogoutRequest(); URI logoutEP = oidcAgentConfig.getLogoutEndpoint(); URI redirectionURI = oidcAgentConfig.getPostLogoutRedirectURI(); JWT jwtIdToken = sessionContext.getIdToken(); State state = generateStateParameter(); + RequestContext requestContext = new RequestContext(); + requestContext.setState(state); URI logoutRequestURI = new LogoutRequest(logoutEP, jwtIdToken, redirectionURI, state).toURI(); - logoutRequest.setLogoutRequestURI(logoutRequestURI); - logoutRequest.setState(state); - return logoutRequest; + + return new io.asgardio.java.oidc.sdk.request.model.LogoutRequest(logoutRequestURI, requestContext); } private State generateStateParameter() { diff --git a/io.asgardio.java.oidc.sdk/src/main/java/io/asgardio/java/oidc/sdk/request/model/LogoutRequest.java b/io.asgardio.java.oidc.sdk/src/main/java/io/asgardio/java/oidc/sdk/request/model/LogoutRequest.java index f74e763..f03cbb7 100644 --- a/io.asgardio.java.oidc.sdk/src/main/java/io/asgardio/java/oidc/sdk/request/model/LogoutRequest.java +++ b/io.asgardio.java.oidc.sdk/src/main/java/io/asgardio/java/oidc/sdk/request/model/LogoutRequest.java @@ -18,7 +18,7 @@ package io.asgardio.java.oidc.sdk.request.model; -import com.nimbusds.oauth2.sdk.id.State; +import io.asgardio.java.oidc.sdk.bean.RequestContext; import java.io.Serializable; import java.net.URI; @@ -31,7 +31,13 @@ public class LogoutRequest implements Serializable { private static final long serialVersionUID = 6184960293632714833L; private URI logoutRequestURI; - private State state; + private RequestContext requestContext; + + public LogoutRequest(URI logoutRequestURI, RequestContext requestContext) { + + this.logoutRequestURI = logoutRequestURI; + this.requestContext = requestContext; + } public URI getLogoutRequestURI() { @@ -43,13 +49,13 @@ public void setLogoutRequestURI(URI logoutRequestURI) { this.logoutRequestURI = logoutRequestURI; } - public State getState() { + public RequestContext getRequestContext() { - return state; + return requestContext; } - public void setState(State state) { + public void setRequestContext(RequestContext requestContext) { - this.state = state; + this.requestContext = requestContext; } } diff --git a/io.asgardio.java.oidc.sdk/src/test/java/io/asgardio/java/oidc/sdk/HTTPSessionBasedOIDCProcessorTest.java b/io.asgardio.java.oidc.sdk/src/test/java/io/asgardio/java/oidc/sdk/HTTPSessionBasedOIDCProcessorTest.java new file mode 100644 index 0000000..c60f492 --- /dev/null +++ b/io.asgardio.java.oidc.sdk/src/test/java/io/asgardio/java/oidc/sdk/HTTPSessionBasedOIDCProcessorTest.java @@ -0,0 +1,141 @@ +/* + * Copyright (c) 2020, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package io.asgardio.java.oidc.sdk; + +import com.nimbusds.oauth2.sdk.id.State; +import com.nimbusds.openid.connect.sdk.Nonce; +import io.asgardio.java.oidc.sdk.bean.RequestContext; +import io.asgardio.java.oidc.sdk.bean.SessionContext; +import io.asgardio.java.oidc.sdk.config.model.OIDCAgentConfig; +import io.asgardio.java.oidc.sdk.exception.SSOAgentClientException; +import io.asgardio.java.oidc.sdk.exception.SSOAgentException; +import org.mockito.Mock; +import org.mockito.MockedStatic; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.testng.PowerMockTestCase; +import org.testng.IObjectFactory; +import org.testng.annotations.AfterMethod; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.ObjectFactory; +import org.testng.annotations.Test; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpSession; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.mockStatic; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +@PrepareForTest({DefaultOIDCManager.class, DefaultOIDCManagerFactory.class}) +public class HTTPSessionBasedOIDCProcessorTest extends PowerMockTestCase { + + @Mock + DefaultOIDCManager defaultOIDCManager; + + @Mock + HttpServletRequest request; + + @Mock + HttpServletResponse response; + + OIDCAgentConfig oidcAgentConfig = new OIDCAgentConfig(); + + private static MockedStatic mockedOIDCManagerFactory; + + @BeforeMethod + public void setUp() throws Exception { + + defaultOIDCManager = mock(DefaultOIDCManager.class); + request = mock(HttpServletRequest.class); + response = mock(HttpServletResponse.class); + } + + @AfterMethod + public void tearDown() { + + mockedOIDCManagerFactory.close(); + } + + @Test + public void testSendForLogin() throws Exception { + + Nonce nonce = new Nonce(); + State state = new State("SampleState"); + RequestContext requestContext = new RequestContext(state, nonce); + + HttpSession session = mock(HttpSession.class); + mockedOIDCManagerFactory = mockStatic(DefaultOIDCManagerFactory.class); + when(DefaultOIDCManagerFactory.createOIDCManager(oidcAgentConfig)).thenReturn(defaultOIDCManager); + when(request.getSession()).thenReturn(session); + when(defaultOIDCManager.sendForLogin(request, response)).thenReturn(requestContext); + + HTTPSessionBasedOIDCProcessor provider = new HTTPSessionBasedOIDCProcessor(oidcAgentConfig); + provider.sendForLogin(request, response); + + verify(session).setAttribute(SSOAgentConstants.REQUEST_CONTEXT, requestContext); + } + + @Test + public void testHandleOIDCCallback() throws SSOAgentException { + + SessionContext sessionContext = new SessionContext(); + RequestContext requestContext = new RequestContext(); + + HttpSession session = mock(HttpSession.class); + mockedOIDCManagerFactory = mockStatic(DefaultOIDCManagerFactory.class); + when(DefaultOIDCManagerFactory.createOIDCManager(oidcAgentConfig)).thenReturn(defaultOIDCManager); + when(request.getSession()).thenReturn(session); + when(request.getSession(false)).thenReturn(session); + when(session.getAttribute(SSOAgentConstants.REQUEST_CONTEXT)).thenReturn(requestContext); + when(defaultOIDCManager.handleOIDCCallback(request, response, requestContext)).thenReturn(sessionContext); + + HTTPSessionBasedOIDCProcessor provider = new HTTPSessionBasedOIDCProcessor(oidcAgentConfig); + provider.handleOIDCCallback(request, response); + + verify(session).setAttribute(SSOAgentConstants.SESSION_CONTEXT, sessionContext); + } + + @Test + public void testLogout() throws SSOAgentException { + + RequestContext requestContext = new RequestContext(); + SessionContext sessionContext = new SessionContext(); + + HttpSession session = mock(HttpSession.class); + mockedOIDCManagerFactory = mockStatic(DefaultOIDCManagerFactory.class); + when(DefaultOIDCManagerFactory.createOIDCManager(oidcAgentConfig)).thenReturn(defaultOIDCManager); + when(request.getSession()).thenReturn(session); + when(request.getSession(false)).thenReturn(session); + when(session.getAttribute(SSOAgentConstants.SESSION_CONTEXT)).thenReturn(sessionContext); + when(defaultOIDCManager.logout(sessionContext, response)).thenReturn(requestContext); + + HTTPSessionBasedOIDCProcessor provider = new HTTPSessionBasedOIDCProcessor(oidcAgentConfig); + provider.logout(request, response); + + verify(session).setAttribute(SSOAgentConstants.REQUEST_CONTEXT, requestContext); + } + + @ObjectFactory + public IObjectFactory getObjectFactory() { + + return new org.powermock.modules.testng.PowerMockObjectFactory(); + } +} \ No newline at end of file diff --git a/io.asgardio.java.oidc.sdk/src/test/java/io/asgardio/java/oidc/sdk/request/OIDCRequestBuilderTest.java b/io.asgardio.java.oidc.sdk/src/test/java/io/asgardio/java/oidc/sdk/request/OIDCRequestBuilderTest.java index f1046bb..809f486 100644 --- a/io.asgardio.java.oidc.sdk/src/test/java/io/asgardio/java/oidc/sdk/request/OIDCRequestBuilderTest.java +++ b/io.asgardio.java.oidc.sdk/src/test/java/io/asgardio/java/oidc/sdk/request/OIDCRequestBuilderTest.java @@ -22,11 +22,11 @@ import com.nimbusds.jwt.JWTParser; import com.nimbusds.oauth2.sdk.Scope; import com.nimbusds.oauth2.sdk.id.ClientID; -import io.asgardio.java.oidc.sdk.request.model.AuthenticationRequest; -import io.asgardio.java.oidc.sdk.request.model.LogoutRequest; import io.asgardio.java.oidc.sdk.bean.RequestContext; import io.asgardio.java.oidc.sdk.bean.SessionContext; import io.asgardio.java.oidc.sdk.config.model.OIDCAgentConfig; +import io.asgardio.java.oidc.sdk.request.model.AuthenticationRequest; +import io.asgardio.java.oidc.sdk.request.model.LogoutRequest; import org.mockito.Mock; import org.powermock.core.classloader.annotations.PrepareForTest; import org.powermock.modules.testng.PowerMockTestCase; @@ -96,7 +96,8 @@ public void testBuildAuthorizationRequest() { public void testBuildLogoutRequest() { LogoutRequest logoutRequest = new OIDCRequestBuilder(oidcAgentConfig).buildLogoutRequest(sessionContext); - String state = logoutRequest.getState().getValue(); + RequestContext requestContext = logoutRequest.getRequestContext(); + String state = requestContext.getState().getValue(); assertEquals(logoutRequest.getLogoutRequestURI().toString(), "http://test/sampleLogoutEP?state=" + state + "&post_logout_redirect_uri=http%3A%2F%2Ftest%2FsampleRedirectionURL&id_token_hint=eyJhbGciOiJIUzI1NiI" + "sInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwR" + From c8fdf89aa2596c5ac491c464e6a0bba0b6016af0 Mon Sep 17 00:00:00 2001 From: Chamath Samarawickrama Date: Wed, 28 Oct 2020 16:39:37 +0530 Subject: [PATCH 08/13] create new session prior to sending the response in logout implementation. --- .../asgardio/java/oidc/sdk/HTTPSessionBasedOIDCProcessor.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/io.asgardio.java.oidc.sdk/src/main/java/io/asgardio/java/oidc/sdk/HTTPSessionBasedOIDCProcessor.java b/io.asgardio.java.oidc.sdk/src/main/java/io/asgardio/java/oidc/sdk/HTTPSessionBasedOIDCProcessor.java index c134891..01eb5d3 100644 --- a/io.asgardio.java.oidc.sdk/src/main/java/io/asgardio/java/oidc/sdk/HTTPSessionBasedOIDCProcessor.java +++ b/io.asgardio.java.oidc.sdk/src/main/java/io/asgardio/java/oidc/sdk/HTTPSessionBasedOIDCProcessor.java @@ -103,8 +103,8 @@ public void logout(HttpServletRequest request, HttpServletResponse response) thr SessionContext sessionContext = getSessionContext(request); clearSession(request); - RequestContext requestContext = defaultOIDCManager.logout(sessionContext, response); HttpSession session = request.getSession(); + RequestContext requestContext = defaultOIDCManager.logout(sessionContext, response); session.setAttribute(SSOAgentConstants.REQUEST_CONTEXT, requestContext); } From 9a4449813c586055adb772a2dec2a15e79c2b210 Mon Sep 17 00:00:00 2001 From: Chamath Samarawickrama Date: Wed, 28 Oct 2020 16:39:52 +0530 Subject: [PATCH 09/13] Add support for errorPage property. --- .../java/oidc/sdk/SSOAgentConstants.java | 1 + .../config/FileBasedOIDCConfigProvider.java | 8 +++++++ .../sdk/config/model/OIDCAgentConfig.java | 21 +++++++++++++++++++ 3 files changed, 30 insertions(+) diff --git a/io.asgardio.java.oidc.sdk/src/main/java/io/asgardio/java/oidc/sdk/SSOAgentConstants.java b/io.asgardio.java.oidc.sdk/src/main/java/io/asgardio/java/oidc/sdk/SSOAgentConstants.java index afa637a..eb168de 100644 --- a/io.asgardio.java.oidc.sdk/src/main/java/io/asgardio/java/oidc/sdk/SSOAgentConstants.java +++ b/io.asgardio.java.oidc.sdk/src/main/java/io/asgardio/java/oidc/sdk/SSOAgentConstants.java @@ -48,6 +48,7 @@ public class SSOAgentConstants { public static final String CALL_BACK_URL = "callBackURL"; public static final String SKIP_URIS = "skipURIs"; public static final String INDEX_PAGE = "indexPage"; + public static final String ERROR_PAGE = "errorPage"; public static final String LOGOUT_URL = "logoutURL"; public static final String SCOPE = "scope"; public static final String OAUTH2_GRANT_TYPE = "grantType"; diff --git a/io.asgardio.java.oidc.sdk/src/main/java/io/asgardio/java/oidc/sdk/config/FileBasedOIDCConfigProvider.java b/io.asgardio.java.oidc.sdk/src/main/java/io/asgardio/java/oidc/sdk/config/FileBasedOIDCConfigProvider.java index c9e6a8b..049218c 100644 --- a/io.asgardio.java.oidc.sdk/src/main/java/io/asgardio/java/oidc/sdk/config/FileBasedOIDCConfigProvider.java +++ b/io.asgardio.java.oidc.sdk/src/main/java/io/asgardio/java/oidc/sdk/config/FileBasedOIDCConfigProvider.java @@ -68,6 +68,7 @@ private void initConfig(Properties properties) throws SSOAgentClientException { Secret consumerSecret = StringUtils.isNotBlank(properties.getProperty(SSOAgentConstants.CONSUMER_SECRET)) ? new Secret(properties.getProperty(SSOAgentConstants.CONSUMER_SECRET)) : null; String indexPage = properties.getProperty(SSOAgentConstants.INDEX_PAGE); + String errorPage = properties.getProperty(SSOAgentConstants.ERROR_PAGE); String logoutURL = properties.getProperty(SSOAgentConstants.LOGOUT_URL); JWSAlgorithm jwsAlgorithm = StringUtils.isNotBlank(properties.getProperty(SSOAgentConstants.ID_TOKEN_SIGN_ALG)) ? @@ -113,6 +114,12 @@ private void initConfig(Properties properties) throws SSOAgentClientException { String[] skipURIArray = skipURIsString.split(","); Collections.addAll(skipURIs, skipURIArray); } + if (StringUtils.isNotBlank(indexPage)) { + skipURIs.add(indexPage); + } + if (StringUtils.isNotBlank(errorPage)) { + skipURIs.add(errorPage); + } Set trustedAudience = new HashSet(); trustedAudience.add(consumerKey.getValue()); String trustedAudienceString = properties.getProperty(SSOAgentConstants.TRUSTED_AUDIENCE); @@ -123,6 +130,7 @@ private void initConfig(Properties properties) throws SSOAgentClientException { oidcAgentConfig.setConsumerKey(consumerKey); oidcAgentConfig.setConsumerSecret(consumerSecret); oidcAgentConfig.setIndexPage(indexPage); + oidcAgentConfig.setErrorPage(errorPage); oidcAgentConfig.setLogoutURL(logoutURL); oidcAgentConfig.setIssuer(issuer); oidcAgentConfig.setSkipURIs(skipURIs); diff --git a/io.asgardio.java.oidc.sdk/src/main/java/io/asgardio/java/oidc/sdk/config/model/OIDCAgentConfig.java b/io.asgardio.java.oidc.sdk/src/main/java/io/asgardio/java/oidc/sdk/config/model/OIDCAgentConfig.java index bd897d7..7f9f3ea 100644 --- a/io.asgardio.java.oidc.sdk/src/main/java/io/asgardio/java/oidc/sdk/config/model/OIDCAgentConfig.java +++ b/io.asgardio.java.oidc.sdk/src/main/java/io/asgardio/java/oidc/sdk/config/model/OIDCAgentConfig.java @@ -36,6 +36,7 @@ public class OIDCAgentConfig { private ClientID consumerKey; private Secret consumerSecret; private String indexPage; + private String errorPage; private String logoutURL; private URI callbackUrl; private Scope scope; @@ -109,6 +110,26 @@ public void setIndexPage(String indexPage) { this.indexPage = indexPage; } + /** + * Returns the error page of the OIDC agent. + * + * @return Error page of the OIDC agent. + */ + public String getErrorPage() { + + return errorPage; + } + + /** + * Sets the error page for the OIDC agent. + * + * @param errorPage The error page of the OIDC agent. + */ + public void setErrorPage(String errorPage) { + + this.errorPage = errorPage; + } + /** * Returns the logout URL of the OIDC agent. * From e4f87c78908c0c6e664e8d2aed3c4b2104e0ac22 Mon Sep 17 00:00:00 2001 From: Chamath Samarawickrama Date: Thu, 29 Oct 2020 11:15:35 +0530 Subject: [PATCH 10/13] Refactor SessionContext attributes. --- .../java/oidc/sdk/DefaultOIDCManager.java | 11 ++++---- .../java/oidc/sdk/bean/SessionContext.java | 28 ++++++++----------- .../oidc/sdk/request/OIDCRequestBuilder.java | 13 +++++++-- .../java/oidc/sdk/DefaultOIDCManagerTest.java | 12 ++++---- .../oidc/sdk/bean/AuthenticationInfoTest.java | 12 ++++---- .../sdk/request/OIDCRequestBuilderTest.java | 5 ++-- 6 files changed, 43 insertions(+), 38 deletions(-) diff --git a/io.asgardio.java.oidc.sdk/src/main/java/io/asgardio/java/oidc/sdk/DefaultOIDCManager.java b/io.asgardio.java.oidc.sdk/src/main/java/io/asgardio/java/oidc/sdk/DefaultOIDCManager.java index 86fa1f7..2743875 100644 --- a/io.asgardio.java.oidc.sdk/src/main/java/io/asgardio/java/oidc/sdk/DefaultOIDCManager.java +++ b/io.asgardio.java.oidc.sdk/src/main/java/io/asgardio/java/oidc/sdk/DefaultOIDCManager.java @@ -191,8 +191,7 @@ private boolean handleAuthentication(final HttpServletRequest request, SessionCo } } - private void handleSuccessTokenResponse(TokenResponse tokenResponse, SessionContext authenticationInfo, - Nonce nonce) + private void handleSuccessTokenResponse(TokenResponse tokenResponse, SessionContext sessionContext, Nonce nonce) throws SSOAgentServerException { AccessTokenResponse successResponse = tokenResponse.toSuccessResponse(); @@ -213,10 +212,10 @@ private void handleSuccessTokenResponse(TokenResponse tokenResponse, SessionCont IDTokenValidator idTokenValidator = new IDTokenValidator(oidcAgentConfig, idTokenJWT); IDTokenClaimsSet claimsSet = idTokenValidator.validate(nonce); User user = new User(claimsSet.getSubject().getValue(), getUserAttributes(idToken)); - authenticationInfo.setIdToken(idTokenJWT); - authenticationInfo.setUser(user); - authenticationInfo.setAccessToken(accessToken); - authenticationInfo.setRefreshToken(refreshToken); + sessionContext.setIdToken(idTokenJWT.getParsedString()); + sessionContext.setUser(user); + sessionContext.setAccessToken(accessToken.toJSONString()); + sessionContext.setRefreshToken(refreshToken.getValue()); } catch (ParseException e) { throw new SSOAgentServerException(SSOAgentConstants.ErrorMessages.ID_TOKEN_PARSE.getMessage(), SSOAgentConstants.ErrorMessages.ID_TOKEN_PARSE.getCode(), e); diff --git a/io.asgardio.java.oidc.sdk/src/main/java/io/asgardio/java/oidc/sdk/bean/SessionContext.java b/io.asgardio.java.oidc.sdk/src/main/java/io/asgardio/java/oidc/sdk/bean/SessionContext.java index 497e240..6612414 100644 --- a/io.asgardio.java.oidc.sdk/src/main/java/io/asgardio/java/oidc/sdk/bean/SessionContext.java +++ b/io.asgardio.java.oidc.sdk/src/main/java/io/asgardio/java/oidc/sdk/bean/SessionContext.java @@ -18,10 +18,6 @@ package io.asgardio.java.oidc.sdk.bean; -import com.nimbusds.jwt.JWT; -import com.nimbusds.oauth2.sdk.token.AccessToken; -import com.nimbusds.oauth2.sdk.token.RefreshToken; - import java.io.Serializable; /** @@ -40,9 +36,9 @@ public class SessionContext implements Serializable { private static final long serialVersionUID = 976008884476935474L; private User user; - private AccessToken accessToken; - private RefreshToken refreshToken; - private JWT idToken; + private String accessToken; + private String refreshToken; + private String idToken; /** * Returns the authenticated user. @@ -67,9 +63,9 @@ public void setUser(User user) { /** * Returns the access token. * - * @return The {@link AccessToken}. + * @return The access token string. */ - public AccessToken getAccessToken() { + public String getAccessToken() { return accessToken; } @@ -79,7 +75,7 @@ public AccessToken getAccessToken() { * * @param accessToken The access token. */ - public void setAccessToken(AccessToken accessToken) { + public void setAccessToken(String accessToken) { this.accessToken = accessToken; } @@ -87,9 +83,9 @@ public void setAccessToken(AccessToken accessToken) { /** * Returns the refresh token. * - * @return The {@link RefreshToken}. + * @return The refresh token string. */ - public RefreshToken getRefreshToken() { + public String getRefreshToken() { return refreshToken; } @@ -99,7 +95,7 @@ public RefreshToken getRefreshToken() { * * @param refreshToken The refresh token. */ - public void setRefreshToken(RefreshToken refreshToken) { + public void setRefreshToken(String refreshToken) { this.refreshToken = refreshToken; } @@ -107,9 +103,9 @@ public void setRefreshToken(RefreshToken refreshToken) { /** * Returns the id token. * - * @return The {@link JWT} Id token. + * @return The Id token string. */ - public JWT getIdToken() { + public String getIdToken() { return idToken; } @@ -119,7 +115,7 @@ public JWT getIdToken() { * * @param idToken The id token. */ - public void setIdToken(JWT idToken) { + public void setIdToken(String idToken) { this.idToken = idToken; } diff --git a/io.asgardio.java.oidc.sdk/src/main/java/io/asgardio/java/oidc/sdk/request/OIDCRequestBuilder.java b/io.asgardio.java.oidc.sdk/src/main/java/io/asgardio/java/oidc/sdk/request/OIDCRequestBuilder.java index 54d8ec4..e09087c 100644 --- a/io.asgardio.java.oidc.sdk/src/main/java/io/asgardio/java/oidc/sdk/request/OIDCRequestBuilder.java +++ b/io.asgardio.java.oidc.sdk/src/main/java/io/asgardio/java/oidc/sdk/request/OIDCRequestBuilder.java @@ -19,6 +19,7 @@ package io.asgardio.java.oidc.sdk.request; import com.nimbusds.jwt.JWT; +import com.nimbusds.jwt.JWTParser; import com.nimbusds.oauth2.sdk.ResponseType; import com.nimbusds.oauth2.sdk.Scope; import com.nimbusds.oauth2.sdk.id.ClientID; @@ -30,10 +31,12 @@ import io.asgardio.java.oidc.sdk.bean.RequestContext; import io.asgardio.java.oidc.sdk.bean.SessionContext; import io.asgardio.java.oidc.sdk.config.model.OIDCAgentConfig; +import io.asgardio.java.oidc.sdk.exception.SSOAgentServerException; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import java.net.URI; +import java.text.ParseException; import java.util.UUID; /** @@ -108,11 +111,17 @@ public io.asgardio.java.oidc.sdk.request.model.AuthenticationRequest buildAuthen * It must include a valid ID token. * @return Logout request. */ - public io.asgardio.java.oidc.sdk.request.model.LogoutRequest buildLogoutRequest(SessionContext sessionContext) { + public io.asgardio.java.oidc.sdk.request.model.LogoutRequest buildLogoutRequest(SessionContext sessionContext) + throws SSOAgentServerException { URI logoutEP = oidcAgentConfig.getLogoutEndpoint(); URI redirectionURI = oidcAgentConfig.getPostLogoutRedirectURI(); - JWT jwtIdToken = sessionContext.getIdToken(); + JWT jwtIdToken = null; + try { + jwtIdToken = JWTParser.parse(sessionContext.getIdToken()); + } catch (ParseException e) { + throw new SSOAgentServerException(e.getMessage(), e); + } State state = generateStateParameter(); RequestContext requestContext = new RequestContext(); diff --git a/io.asgardio.java.oidc.sdk/src/test/java/io/asgardio/java/oidc/sdk/DefaultOIDCManagerTest.java b/io.asgardio.java.oidc.sdk/src/test/java/io/asgardio/java/oidc/sdk/DefaultOIDCManagerTest.java index 93eccb4..8e79822 100644 --- a/io.asgardio.java.oidc.sdk/src/test/java/io/asgardio/java/oidc/sdk/DefaultOIDCManagerTest.java +++ b/io.asgardio.java.oidc.sdk/src/test/java/io/asgardio/java/oidc/sdk/DefaultOIDCManagerTest.java @@ -123,7 +123,7 @@ public void setUp() throws Exception { oidcAgentConfig.setScope(scope); oidcAgentConfig.setIssuer(issuer); oidcAgentConfig.setJwksEndpoint(jwksURI); - when(sessionContext.getIdToken()).thenReturn(idToken); + when(sessionContext.getIdToken()).thenReturn(idToken.getParsedString()); IDTokenClaimsSet claimsSet = mock(IDTokenClaimsSet.class); IDTokenValidator idTokenValidator = mock(IDTokenValidator.class); com.nimbusds.openid.connect.sdk.validators.IDTokenValidator validator = mock( @@ -191,12 +191,12 @@ public String toAuthorizationHeader() { RequestContext requestContext = new RequestContext(new State("state"), new Nonce()); OIDCManager oidcManager = new DefaultOIDCManager(oidcAgentConfig); - SessionContext authenticationInfo = oidcManager.handleOIDCCallback(request, response, requestContext); + SessionContext sessionContext = oidcManager.handleOIDCCallback(request, response, requestContext); - assertEquals(authenticationInfo.getAccessToken(), accessToken); - assertEquals(authenticationInfo.getRefreshToken(), refreshToken); - assertEquals(authenticationInfo.getIdToken().getParsedString(), parsedIdToken); - assertEquals(authenticationInfo.getUser().getSubject(), "alex@carbon.super"); + assertEquals(sessionContext.getAccessToken(), accessToken.toJSONString()); + assertEquals(sessionContext.getRefreshToken(), refreshToken.getValue()); + assertEquals(sessionContext.getIdToken(), parsedIdToken); + assertEquals(sessionContext.getUser().getSubject(), "alex@carbon.super"); mockedAuthorizationResponse.close(); mockedServletUtils.close(); mockedTokenResponse.close(); diff --git a/io.asgardio.java.oidc.sdk/src/test/java/io/asgardio/java/oidc/sdk/bean/AuthenticationInfoTest.java b/io.asgardio.java.oidc.sdk/src/test/java/io/asgardio/java/oidc/sdk/bean/AuthenticationInfoTest.java index 7457ad0..3e2aec2 100644 --- a/io.asgardio.java.oidc.sdk/src/test/java/io/asgardio/java/oidc/sdk/bean/AuthenticationInfoTest.java +++ b/io.asgardio.java.oidc.sdk/src/test/java/io/asgardio/java/oidc/sdk/bean/AuthenticationInfoTest.java @@ -57,16 +57,16 @@ public String toAuthorizationHeader() { return null; } }; - authenticationInfo.setAccessToken(accessToken); - assertEquals(authenticationInfo.getAccessToken(), accessToken); + authenticationInfo.setAccessToken(accessToken.toJSONString()); + assertEquals(authenticationInfo.getAccessToken(), accessToken.toJSONString()); } @Test public void testGetRefreshToken() { RefreshToken refreshToken = new RefreshToken(); - authenticationInfo.setRefreshToken(refreshToken); - assertEquals(authenticationInfo.getRefreshToken(), refreshToken); + authenticationInfo.setRefreshToken(refreshToken.getValue()); + assertEquals(authenticationInfo.getRefreshToken(), refreshToken.getValue()); } @Test @@ -74,8 +74,8 @@ public void testGetIdToken() { try { JWT idToken = JWTParser.parse("sample"); - authenticationInfo.setIdToken(idToken); - assertEquals(authenticationInfo.getIdToken(), idToken); + authenticationInfo.setIdToken(idToken.getParsedString()); + assertEquals(authenticationInfo.getIdToken(), idToken.getParsedString()); } catch (ParseException e) { //Test behaviour. Hence ignored. } diff --git a/io.asgardio.java.oidc.sdk/src/test/java/io/asgardio/java/oidc/sdk/request/OIDCRequestBuilderTest.java b/io.asgardio.java.oidc.sdk/src/test/java/io/asgardio/java/oidc/sdk/request/OIDCRequestBuilderTest.java index 809f486..acc9d2e 100644 --- a/io.asgardio.java.oidc.sdk/src/test/java/io/asgardio/java/oidc/sdk/request/OIDCRequestBuilderTest.java +++ b/io.asgardio.java.oidc.sdk/src/test/java/io/asgardio/java/oidc/sdk/request/OIDCRequestBuilderTest.java @@ -25,6 +25,7 @@ import io.asgardio.java.oidc.sdk.bean.RequestContext; import io.asgardio.java.oidc.sdk.bean.SessionContext; import io.asgardio.java.oidc.sdk.config.model.OIDCAgentConfig; +import io.asgardio.java.oidc.sdk.exception.SSOAgentServerException; import io.asgardio.java.oidc.sdk.request.model.AuthenticationRequest; import io.asgardio.java.oidc.sdk.request.model.LogoutRequest; import org.mockito.Mock; @@ -74,7 +75,7 @@ public void setUp() throws URISyntaxException, ParseException { when(oidcAgentConfig.getAuthorizeEndpoint()).thenReturn(authorizationEndpoint); when(oidcAgentConfig.getLogoutEndpoint()).thenReturn(logoutEP); when(oidcAgentConfig.getPostLogoutRedirectURI()).thenReturn(redirectionURI); - when(sessionContext.getIdToken()).thenReturn(idToken); + when(sessionContext.getIdToken()).thenReturn(idToken.getParsedString()); } @Test @@ -93,7 +94,7 @@ public void testBuildAuthorizationRequest() { } @Test - public void testBuildLogoutRequest() { + public void testBuildLogoutRequest() throws SSOAgentServerException { LogoutRequest logoutRequest = new OIDCRequestBuilder(oidcAgentConfig).buildLogoutRequest(sessionContext); RequestContext requestContext = logoutRequest.getRequestContext(); From 22a590a290cf5248981e6b85f9d3a7640b0a323f Mon Sep 17 00:00:00 2001 From: Chamath Samarawickrama Date: Thu, 29 Oct 2020 23:33:02 +0530 Subject: [PATCH 11/13] Add support for errorPage property. --- .../java/io/asgardio/java/oidc/sdk/SSOAgentConstants.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/io.asgardio.java.oidc.sdk/src/main/java/io/asgardio/java/oidc/sdk/SSOAgentConstants.java b/io.asgardio.java.oidc.sdk/src/main/java/io/asgardio/java/oidc/sdk/SSOAgentConstants.java index eb168de..5713d16 100644 --- a/io.asgardio.java.oidc.sdk/src/main/java/io/asgardio/java/oidc/sdk/SSOAgentConstants.java +++ b/io.asgardio.java.oidc.sdk/src/main/java/io/asgardio/java/oidc/sdk/SSOAgentConstants.java @@ -65,6 +65,7 @@ public class SSOAgentConstants { public static final String TRUSTED_AUDIENCE = "trustedAudience"; public static final String ID_TOKEN_SIGN_ALG = "signatureAlgorithm"; public static final String NONCE = "nonce"; + public static final String AGENT_EXCEPTION = "AgentException"; // Request headers. public static final String REFERER = "referer"; @@ -91,7 +92,8 @@ public enum ErrorMessages { AGENT_CONFIG_CLIENT_ID("18006", "Consumer Key/Client ID must not be null. This refers to the client identifier assigned to the " + "Relying Party during its registration with the OpenID Provider."), - AGENT_CONFIG_CALLBACK_URL("18007", + AGENT_CONFIG_CLIENT_SECRET("18007", "Consumer secret/Client secret must not be null."), + AGENT_CONFIG_CALLBACK_URL("18008", "Callback URL/Redirection URL must not be null. This refers to the Relying Party's redirection URIs " + "registered with the OpenID Provider."), SERVLET_CONNECTION("18008", "Error found with connection."); From 373947d4a4183468c8bce919702ef2d809606056 Mon Sep 17 00:00:00 2001 From: Chamath Samarawickrama Date: Sun, 1 Nov 2020 13:02:21 +0530 Subject: [PATCH 12/13] temp validation commit. --- .../io/asgardio/java/oidc/sdk/DefaultOIDCManager.java | 10 +++++++--- .../java/oidc/sdk/request/OIDCRequestBuilder.java | 8 +++++++- .../java/oidc/sdk/validators/IDTokenValidator.java | 2 +- 3 files changed, 15 insertions(+), 5 deletions(-) diff --git a/io.asgardio.java.oidc.sdk/src/main/java/io/asgardio/java/oidc/sdk/DefaultOIDCManager.java b/io.asgardio.java.oidc.sdk/src/main/java/io/asgardio/java/oidc/sdk/DefaultOIDCManager.java index 2743875..5348248 100644 --- a/io.asgardio.java.oidc.sdk/src/main/java/io/asgardio/java/oidc/sdk/DefaultOIDCManager.java +++ b/io.asgardio.java.oidc.sdk/src/main/java/io/asgardio/java/oidc/sdk/DefaultOIDCManager.java @@ -157,7 +157,7 @@ public RequestContext logout(SessionContext sessionContext, HttpServletResponse } private boolean handleAuthentication(final HttpServletRequest request, SessionContext authenticationInfo, - Nonce nonce) { + Nonce nonce) throws SSOAgentServerException { AuthorizationResponse authorizationResponse; AuthorizationCode authorizationCode; @@ -186,8 +186,7 @@ private boolean handleAuthentication(final HttpServletRequest request, SessionCo handleSuccessTokenResponse(tokenResponse, authenticationInfo, nonce); return true; } catch (com.nimbusds.oauth2.sdk.ParseException | SSOAgentServerException | IOException e) { - logger.error(e.getMessage(), e); - return false; + throw new SSOAgentServerException(e.getMessage(), e); } } @@ -308,6 +307,11 @@ private void validateForCode(OIDCAgentConfig oidcAgentConfig) throws SSOAgentCli } if (oidcAgentConfig.getConsumerKey() == null) { + throw new SSOAgentClientException(SSOAgentConstants.ErrorMessages.AGENT_CONFIG_CLIENT_SECRET.getMessage(), + SSOAgentConstants.ErrorMessages.AGENT_CONFIG_CLIENT_SECRET.getCode()); + } + + if (oidcAgentConfig.getConsumerSecret() == null) { throw new SSOAgentClientException(SSOAgentConstants.ErrorMessages.AGENT_CONFIG_CLIENT_ID.getMessage(), SSOAgentConstants.ErrorMessages.AGENT_CONFIG_CLIENT_ID.getCode()); } diff --git a/io.asgardio.java.oidc.sdk/src/main/java/io/asgardio/java/oidc/sdk/request/OIDCRequestBuilder.java b/io.asgardio.java.oidc.sdk/src/main/java/io/asgardio/java/oidc/sdk/request/OIDCRequestBuilder.java index e09087c..bc00890 100644 --- a/io.asgardio.java.oidc.sdk/src/main/java/io/asgardio/java/oidc/sdk/request/OIDCRequestBuilder.java +++ b/io.asgardio.java.oidc.sdk/src/main/java/io/asgardio/java/oidc/sdk/request/OIDCRequestBuilder.java @@ -126,7 +126,13 @@ public io.asgardio.java.oidc.sdk.request.model.LogoutRequest buildLogoutRequest( RequestContext requestContext = new RequestContext(); requestContext.setState(state); - URI logoutRequestURI = new LogoutRequest(logoutEP, jwtIdToken, redirectionURI, state).toURI(); + URI logoutRequestURI; + + try { + logoutRequestURI = new LogoutRequest(logoutEP, jwtIdToken, redirectionURI, state).toURI(); + } catch (Exception e) { + throw new SSOAgentServerException(e.getMessage(), e); + } return new io.asgardio.java.oidc.sdk.request.model.LogoutRequest(logoutRequestURI, requestContext); } diff --git a/io.asgardio.java.oidc.sdk/src/main/java/io/asgardio/java/oidc/sdk/validators/IDTokenValidator.java b/io.asgardio.java.oidc.sdk/src/main/java/io/asgardio/java/oidc/sdk/validators/IDTokenValidator.java index 2f030af..2fd7957 100644 --- a/io.asgardio.java.oidc.sdk/src/main/java/io/asgardio/java/oidc/sdk/validators/IDTokenValidator.java +++ b/io.asgardio.java.oidc.sdk/src/main/java/io/asgardio/java/oidc/sdk/validators/IDTokenValidator.java @@ -95,7 +95,7 @@ private com.nimbusds.openid.connect.sdk.validators.IDTokenValidator getIDTokenVa validator = new com.nimbusds.openid.connect.sdk.validators.IDTokenValidator(issuer, clientID, jwsAlgorithm, jwkSetURI.toURL()); - } catch (MalformedURLException e) { + } catch (Exception e) { throw new SSOAgentServerException(e.getMessage(), e.getCause()); } // Creates a new validator for HMAC protected ID tokens. From 8935b73e06c06ac1e1f216c89c0b19e175e1c196 Mon Sep 17 00:00:00 2001 From: Chamath Samarawickrama Date: Wed, 11 Nov 2020 14:52:19 +0530 Subject: [PATCH 13/13] Refactor code. --- .../java/oidc/sdk/HTTPSessionBasedOIDCProcessorTest.java | 2 +- .../asgardio/java/oidc/sdk/validators/IDTokenValidatorTest.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/io.asgardio.java.oidc.sdk/src/test/java/io/asgardio/java/oidc/sdk/HTTPSessionBasedOIDCProcessorTest.java b/io.asgardio.java.oidc.sdk/src/test/java/io/asgardio/java/oidc/sdk/HTTPSessionBasedOIDCProcessorTest.java index c60f492..d7afc42 100644 --- a/io.asgardio.java.oidc.sdk/src/test/java/io/asgardio/java/oidc/sdk/HTTPSessionBasedOIDCProcessorTest.java +++ b/io.asgardio.java.oidc.sdk/src/test/java/io/asgardio/java/oidc/sdk/HTTPSessionBasedOIDCProcessorTest.java @@ -138,4 +138,4 @@ public IObjectFactory getObjectFactory() { return new org.powermock.modules.testng.PowerMockObjectFactory(); } -} \ No newline at end of file +} diff --git a/io.asgardio.java.oidc.sdk/src/test/java/io/asgardio/java/oidc/sdk/validators/IDTokenValidatorTest.java b/io.asgardio.java.oidc.sdk/src/test/java/io/asgardio/java/oidc/sdk/validators/IDTokenValidatorTest.java index 3b54a9c..ee92c63 100644 --- a/io.asgardio.java.oidc.sdk/src/test/java/io/asgardio/java/oidc/sdk/validators/IDTokenValidatorTest.java +++ b/io.asgardio.java.oidc.sdk/src/test/java/io/asgardio/java/oidc/sdk/validators/IDTokenValidatorTest.java @@ -289,4 +289,4 @@ public void tearDown() { closeJadler(); } -} \ No newline at end of file +}