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 79%
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 dd78612..5348248 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
@@ -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,7 +41,10 @@
import com.nimbusds.oauth2.sdk.id.ClientID;
import com.nimbusds.oauth2.sdk.token.AccessToken;
import com.nimbusds.oauth2.sdk.token.RefreshToken;
-import io.asgardio.java.oidc.sdk.bean.AuthenticationInfo;
+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.bean.User;
import io.asgardio.java.oidc.sdk.config.model.OIDCAgentConfig;
import io.asgardio.java.oidc.sdk.exception.SSOAgentClientException;
@@ -48,6 +52,9 @@
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;
import org.apache.logging.log4j.Level;
@@ -66,13 +73,13 @@
/**
* 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;
@@ -82,36 +89,38 @@ 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);
- String authorizationRequest = requestBuilder.buildAuthorizationRequest(state);
+ 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;
}
}
@@ -127,8 +136,7 @@ public AuthenticationInfo handleOIDCCallback(HttpServletRequest request, HttpSer
* {@inheritDoc}
*/
@Override
- public void logout(AuthenticationInfo authenticationInfo, HttpServletResponse response, String state)
- 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.");
@@ -137,17 +145,19 @@ public void logout(AuthenticationInfo authenticationInfo, HttpServletResponse re
}
OIDCRequestBuilder requestBuilder = new OIDCRequestBuilder(oidcAgentConfig);
- String logoutRequest = requestBuilder.buildLogoutRequest(authenticationInfo, state);
+ 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);
}
+ return logoutRequest.getRequestContext();
}
- private boolean handleAuthentication(final HttpServletRequest request, AuthenticationInfo authenticationInfo) {
+ private boolean handleAuthentication(final HttpServletRequest request, SessionContext authenticationInfo,
+ Nonce nonce) throws SSOAgentServerException {
AuthorizationResponse authorizationResponse;
AuthorizationCode authorizationCode;
@@ -173,15 +183,14 @@ private boolean handleAuthentication(final HttpServletRequest request, Authentic
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);
- return false;
+ throw new SSOAgentServerException(e.getMessage(), e);
}
}
- private void handleSuccessTokenResponse(TokenResponse tokenResponse, AuthenticationInfo authenticationInfo)
+ private void handleSuccessTokenResponse(TokenResponse tokenResponse, SessionContext sessionContext, Nonce nonce)
throws SSOAgentServerException {
AccessTokenResponse successResponse = tokenResponse.toSuccessResponse();
@@ -198,13 +207,14 @@ private void handleSuccessTokenResponse(TokenResponse tokenResponse, Authenticat
}
try {
- JWTClaimsSet claimsSet = SignedJWT.parse(idToken).getJWTClaimsSet();
- User user = new User(claimsSet.getSubject(), getUserAttributes(idToken));
-
- authenticationInfo.setIdToken(JWTParser.parse(idToken));
- authenticationInfo.setUser(user);
- authenticationInfo.setAccessToken(accessToken);
- authenticationInfo.setRefreshToken(refreshToken);
+ 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));
+ 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);
@@ -297,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/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..01eb5d3
--- /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);
+ HttpSession session = request.getSession();
+ RequestContext requestContext = defaultOIDCManager.logout(sessionContext, response);
+ 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 dfe9a2a..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,9 +18,11 @@
package io.asgardio.java.oidc.sdk;
-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.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;
@@ -35,33 +37,36 @@ 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
+ * @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.
*/
- AuthenticationInfo handleOIDCCallback(HttpServletRequest request, HttpServletResponse response)
- throws SSOAgentException;
+ SessionContext handleOIDCCallback(HttpServletRequest request, HttpServletResponse response,
+ RequestContext requestContext) 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}
+ * @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(AuthenticationInfo authenticationInfo, HttpServletResponse response, String state)
- 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/SSOAgentConstants.java b/io.asgardio.java.oidc.sdk/src/main/java/io/asgardio/java/oidc/sdk/SSOAgentConstants.java
index 22bb0f9..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
@@ -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";
@@ -46,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";
@@ -57,8 +60,12 @@ 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";
+ 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";
@@ -85,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.");
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..1b9656d
--- /dev/null
+++ b/io.asgardio.java.oidc.sdk/src/main/java/io/asgardio/java/oidc/sdk/bean/RequestContext.java
@@ -0,0 +1,119 @@
+/*
+ * 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. 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 {
+
+ 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 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/bean/AuthenticationInfo.java b/io.asgardio.java.oidc.sdk/src/main/java/io/asgardio/java/oidc/sdk/bean/SessionContext.java
similarity index 69%
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..6612414 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
@@ -18,23 +18,27 @@
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;
/**
- * A data model class to define the Authentication Info 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 AuthenticationInfo implements Serializable {
+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.
@@ -59,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;
}
@@ -71,7 +75,7 @@ public AccessToken getAccessToken() {
*
* @param accessToken The access token.
*/
- public void setAccessToken(AccessToken accessToken) {
+ public void setAccessToken(String accessToken) {
this.accessToken = accessToken;
}
@@ -79,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;
}
@@ -91,7 +95,7 @@ public RefreshToken getRefreshToken() {
*
* @param refreshToken The refresh token.
*/
- public void setRefreshToken(RefreshToken refreshToken) {
+ public void setRefreshToken(String refreshToken) {
this.refreshToken = refreshToken;
}
@@ -99,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;
}
@@ -111,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/config/FileBasedOIDCConfigProvider.java b/io.asgardio.java.oidc.sdk/src/main/java/io/asgardio/java/oidc/sdk/config/FileBasedOIDCConfigProvider.java
index c69f417..fc70d11 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;
@@ -66,9 +68,11 @@ 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);
- Issuer issuer = StringUtils.isNotBlank(properties.getProperty(SSOAgentConstants.OIDC_ISSUER)) ?
- new Issuer(properties.getProperty(SSOAgentConstants.OIDC_ISSUER)) : null;
+ 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)) ?
@@ -98,6 +102,8 @@ private void initConfig(Properties properties) throws SSOAgentClientException {
throw new SSOAgentClientException("URL not formatted properly.", e);
}
+ Issuer issuer = StringUtils.isNotBlank(properties.getProperty(SSOAgentConstants.OIDC_ISSUER)) ?
+ new Issuer(properties.getProperty(SSOAgentConstants.OIDC_ISSUER)) : null;
String scopeString = properties.getProperty(SSOAgentConstants.SCOPE);
if (StringUtils.isNotBlank(scopeString)) {
String[] scopeArray = scopeString.split(",");
@@ -109,17 +115,31 @@ private void initConfig(Properties properties) throws SSOAgentClientException {
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);
+ }
+ 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);
+ if (StringUtils.isNotBlank(trustedAudienceString)) {
+ String[] trustedAudienceArray = trustedAudienceString.split(",");
+ Collections.addAll(trustedAudience, trustedAudienceArray);
}
oidcAgentConfig.setConsumerKey(consumerKey);
oidcAgentConfig.setConsumerSecret(consumerSecret);
oidcAgentConfig.setIndexPage(indexPage);
+ oidcAgentConfig.setErrorPage(errorPage);
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..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
@@ -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;
@@ -35,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;
@@ -42,8 +44,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();
/**
@@ -106,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.
*
@@ -246,6 +270,16 @@ 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 +320,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 faf83a7..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
@@ -19,20 +19,25 @@
package io.asgardio.java.oidc.sdk.request;
import com.nimbusds.jwt.JWT;
-import com.nimbusds.oauth2.sdk.AuthorizationRequest;
+import com.nimbusds.jwt.JWTParser;
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.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 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;
/**
* OIDCRequestBuilder is the class responsible for building requests
@@ -58,62 +63,83 @@ public OIDCRequestBuilder(OIDCAgentConfig oidcAgentConfig) {
}
/**
- * Returns {@link String} Authorization request. To build the authorization request,
- * {@link OIDCAgentConfig} should contain:
+ * Returns {@link io.asgardio.java.oidc.sdk.request.model.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 buildAuthorizationRequest(String state) {
+ public io.asgardio.java.oidc.sdk.request.model.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);
- AuthorizationRequest authorizationRequest = new AuthorizationRequest.Builder(responseType, clientID)
- .scope(authScope)
- .state(stateParameter)
- .redirectionURI(callBackURI)
+ AuthenticationRequest authenticationRequest = new AuthenticationRequest.Builder(responseType, authScope,
+ clientID, callBackURI)
+ .state(state)
.endpointURI(authorizationEndpoint)
+ .nonce(nonce)
.build();
- return authorizationRequest.toURI().toString();
+
+ 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 String} 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
*
- The post logout redirection URI
*
*
- * @param authenticationInfo {@link AuthenticationInfo} object with information of the current LoggedIn session.
- * It must include a valid ID token.
- * @param state State parameter.
+ * @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(AuthenticationInfo authenticationInfo, String state) {
+ public io.asgardio.java.oidc.sdk.request.model.LogoutRequest buildLogoutRequest(SessionContext sessionContext)
+ throws SSOAgentServerException {
URI logoutEP = oidcAgentConfig.getLogoutEndpoint();
URI redirectionURI = oidcAgentConfig.getPostLogoutRedirectURI();
- JWT jwtIdToken = authenticationInfo.getIdToken();
- State stateParam = null;
+ 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();
- if (StringUtils.isNotBlank(state)) {
- stateParam = new State(state);
+ requestContext.setState(state);
+ URI logoutRequestURI;
+
+ try {
+ logoutRequestURI = new LogoutRequest(logoutEP, jwtIdToken, redirectionURI, state).toURI();
+ } catch (Exception e) {
+ throw new SSOAgentServerException(e.getMessage(), e);
}
- return new LogoutRequest(logoutEP, jwtIdToken, redirectionURI, stateParam).toURI().toString();
+
+ return new io.asgardio.java.oidc.sdk.request.model.LogoutRequest(logoutRequestURI, requestContext);
+ }
+
+ private State generateStateParameter() {
+
+ UUID uuid = UUID.randomUUID();
+ return new State(uuid.toString());
}
}
diff --git a/io.asgardio.java.oidc.sdk/src/main/java/io/asgardio/java/oidc/sdk/request/model/AuthenticationRequest.java b/io.asgardio.java.oidc.sdk/src/main/java/io/asgardio/java/oidc/sdk/request/model/AuthenticationRequest.java
new file mode 100644
index 0000000..7f4c200
--- /dev/null
+++ b/io.asgardio.java.oidc.sdk/src/main/java/io/asgardio/java/oidc/sdk/request/model/AuthenticationRequest.java
@@ -0,0 +1,61 @@
+/*
+ * 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.request.model;
+
+import io.asgardio.java.oidc.sdk.bean.RequestContext;
+
+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/request/model/LogoutRequest.java b/io.asgardio.java.oidc.sdk/src/main/java/io/asgardio/java/oidc/sdk/request/model/LogoutRequest.java
new file mode 100644
index 0000000..f03cbb7
--- /dev/null
+++ b/io.asgardio.java.oidc.sdk/src/main/java/io/asgardio/java/oidc/sdk/request/model/LogoutRequest.java
@@ -0,0 +1,61 @@
+/*
+ * 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.request.model;
+
+import io.asgardio.java.oidc.sdk.bean.RequestContext;
+
+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;
+
+ private URI logoutRequestURI;
+ private RequestContext requestContext;
+
+ public LogoutRequest(URI logoutRequestURI, RequestContext requestContext) {
+
+ this.logoutRequestURI = logoutRequestURI;
+ this.requestContext = requestContext;
+ }
+
+ public URI getLogoutRequestURI() {
+
+ return logoutRequestURI;
+ }
+
+ public void setLogoutRequestURI(URI logoutRequestURI) {
+
+ this.logoutRequestURI = logoutRequestURI;
+ }
+
+ 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/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..2fd7957
--- /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 (Exception 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/DefaultOIDCManagerTest.java
similarity index 67%
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
index f55c9d6..8e79822 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/DefaultOIDCManagerTest.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,34 @@
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.State;
+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 io.asgardio.java.oidc.sdk.bean.AuthenticationInfo;
+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;
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 +66,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 +74,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 DefaultOIDCManagerTest extends PowerMockTestCase {
@Mock
HttpServletRequest request;
@@ -74,20 +88,22 @@ public class OIDCManagerImplTest {
OIDCRequestResolver requestResolver;
@Mock
- AuthenticationInfo authenticationInfo;
+ SessionContext sessionContext;
OIDCAgentConfig oidcAgentConfig = new OIDCAgentConfig();
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
@@ -97,7 +113,7 @@ public void setUp() throws URISyntaxException, java.text.ParseException {
request = mock(HttpServletRequest.class);
response = mock(HttpServletResponse.class);
requestResolver = mock(OIDCRequestResolver.class);
- authenticationInfo = mock(AuthenticationInfo.class);
+ sessionContext = mock(SessionContext.class);
oidcAgentConfig.setConsumerKey(clientID);
oidcAgentConfig.setConsumerSecret(clientSecret);
@@ -105,11 +121,23 @@ public void setUp() throws URISyntaxException, java.text.ParseException {
oidcAgentConfig.setTokenEndpoint(tokenEPURI);
oidcAgentConfig.setLogoutEndpoint(logoutEP);
oidcAgentConfig.setScope(scope);
- when(authenticationInfo.getIdToken()).thenReturn(idToken);
+ oidcAgentConfig.setIssuer(issuer);
+ oidcAgentConfig.setJwksEndpoint(jwksURI);
+ 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(
+ 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 +160,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,15 +185,18 @@ public String toAuthorizationHeader() {
when(tokenResponse.toSuccessResponse()).thenReturn(accessTokenResponse);
when(accessTokenResponse.getTokens()).thenReturn(tokens);
when(accessTokenResponse.getCustomParameters()).thenReturn(customParameters);
-
- OIDCManager oidcManager = new OIDCManagerImpl(oidcAgentConfig);
- AuthenticationInfo authenticationInfo = oidcManager.handleOIDCCallback(request, response);
-
- assertEquals(authenticationInfo.getAccessToken(), accessToken);
- assertEquals(authenticationInfo.getRefreshToken(), refreshToken);
- assertEquals(authenticationInfo.getIdToken().getParsedString(), parsedIdToken);
- assertEquals(authenticationInfo.getUser().getSubject(), "alex@carbon.super");
-
+ 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 sessionContext = oidcManager.handleOIDCCallback(request, response, requestContext);
+
+ 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();
@@ -174,8 +206,8 @@ public String toAuthorizationHeader() {
public void testLogoutCallbackURI() throws SSOAgentException {
oidcAgentConfig.setPostLogoutRedirectURI(null);
- OIDCManager oidcManager = new OIDCManagerImpl(oidcAgentConfig);
- oidcManager.logout(authenticationInfo, response, "state");
+ OIDCManager oidcManager = new DefaultOIDCManager(oidcAgentConfig);
+ oidcManager.logout(sessionContext, response);
}
@Test
@@ -183,8 +215,8 @@ public void testLogoutRedirectURI() throws URISyntaxException, SSOAgentException
URI redirectionURI = new URI("http://test/sampleRedirectionURL");
oidcAgentConfig.setPostLogoutRedirectURI(redirectionURI);
- OIDCManager oidcManager = new OIDCManagerImpl(oidcAgentConfig);
- oidcManager.logout(authenticationInfo, response, "state");
+ OIDCManager oidcManager = new DefaultOIDCManager(oidcAgentConfig);
+ oidcManager.logout(sessionContext, response);
}
@AfterMethod
@@ -192,4 +224,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/HTTPSessionBasedOIDCProcessorTest.java b/io.asgardio.java.oidc.sdk/src/test/java/io/asgardio/java/oidc/sdk/HTTPSessionBasedOIDCProcessorTest.java
new file mode 100644
index 0000000..d7afc42
--- /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();
+ }
+}
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..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
@@ -36,7 +36,7 @@
*/
public class AuthenticationInfoTest {
- AuthenticationInfo authenticationInfo = new AuthenticationInfo();
+ SessionContext authenticationInfo = new SessionContext();
@Test
public void testGetUser() {
@@ -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 ab27b49..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
@@ -22,10 +22,18 @@
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.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 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;
+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,19 +44,20 @@
import static org.mockito.Mockito.when;
import static org.testng.Assert.assertEquals;
-public class OIDCRequestBuilderTest {
+@PrepareForTest({OIDCAgentConfig.class, SessionContext.class})
+public class OIDCRequestBuilderTest extends PowerMockTestCase {
@Mock
OIDCAgentConfig oidcAgentConfig;
@Mock
- AuthenticationInfo authenticationInfo;
+ SessionContext sessionContext;
@BeforeMethod
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");
@@ -58,7 +67,7 @@ public void setUp() throws URISyntaxException, ParseException {
"WF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c");
oidcAgentConfig = mock(OIDCAgentConfig.class);
- authenticationInfo = mock(AuthenticationInfo.class);
+ sessionContext = mock(SessionContext.class);
when(oidcAgentConfig.getConsumerKey()).thenReturn(clientID);
when(oidcAgentConfig.getScope()).thenReturn(scope);
@@ -66,24 +75,39 @@ 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.getParsedString());
}
@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");
+ 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=" + nonce + "&client_id" +
+ "=sampleClientId");
}
@Test
- public void testBuildLogoutRequest() {
+ public void testBuildLogoutRequest() throws SSOAgentServerException {
+
+ LogoutRequest logoutRequest = new OIDCRequestBuilder(oidcAgentConfig).buildLogoutRequest(sessionContext);
+ 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" +
+ "JSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c");
+ }
+
+ @ObjectFactory
+ public IObjectFactory getObjectFactory() {
- String logoutRequest = new OIDCRequestBuilder(oidcAgentConfig).buildLogoutRequest(authenticationInfo,
- "state");
- 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");
+ 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..ee92c63
--- /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();
+ }
+}
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..fc82eaf 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