You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cxf.apache.org by bu...@apache.org on 2020/02/19 10:18:30 UTC

[cxf-fediz] branch master updated (e79d586 -> 0260eaa)

This is an automated email from the ASF dual-hosted git repository.

buhhunyx pushed a change to branch master
in repository https://gitbox.apache.org/repos/asf/cxf-fediz.git.


    from e79d586  fediz-systests-oidc: validate jwt using jwks
     new 7369053  fediz-systests-oidc: test public client
     new 0260eaa  fediz-systests-oidc: sync idp configuration

The 2 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 .../idp/src/main/resources/entities-realma.xml     |  10 +-
 .../cxf/fediz/systests/oidc/AbstractOIDCTest.java  | 838 +++++++++------------
 .../src/test/resources/realma/entities-realma.xml  |  35 +-
 3 files changed, 402 insertions(+), 481 deletions(-)


[cxf-fediz] 02/02: fediz-systests-oidc: sync idp configuration

Posted by bu...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

buhhunyx pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/cxf-fediz.git

commit 0260eaadc6b8d6a1986abdd6eca42e49b290060c
Author: Alexey Markevich <bu...@gmail.com>
AuthorDate: Wed Feb 19 13:17:52 2020 +0300

    fediz-systests-oidc: sync idp configuration
---
 .../idp/src/main/resources/entities-realma.xml     | 10 +++----
 .../cxf/fediz/systests/oidc/AbstractOIDCTest.java  |  4 +--
 .../src/test/resources/realma/entities-realma.xml  | 35 +++++++++++++++-------
 3 files changed, 32 insertions(+), 17 deletions(-)

diff --git a/services/idp/src/main/resources/entities-realma.xml b/services/idp/src/main/resources/entities-realma.xml
index 2a9c3d6..a2a2aea 100644
--- a/services/idp/src/main/resources/entities-realma.xml
+++ b/services/idp/src/main/resources/entities-realma.xml
@@ -66,7 +66,7 @@
         <property name="applications">
             <util:list>
                 <ref bean="srv-fedizhelloworld" />
-				<ref bean="srv-oidc" />
+                <ref bean="srv-oidc" />
             </util:list>
         </property>
         <property name="trustedIdps">
@@ -108,8 +108,8 @@
         <property name="passiveRequestorEndpointConstraint" value="https://localhost:?(\d)*/.*" />
         <property name="logoutEndpointConstraint" value="https://localhost:?(\d)*/.*" />
     </bean>
-	
-	<bean id="srv-oidc" class="org.apache.cxf.fediz.service.idp.service.jpa.ApplicationEntity">
+
+    <bean id="srv-oidc" class="org.apache.cxf.fediz.service.idp.service.jpa.ApplicationEntity">
         <property name="realm" value="urn:org:apache:cxf:fediz:oidc" />
         <property name="protocol" value="http://docs.oasis-open.org/wsfed/federation/200706" />
         <property name="serviceDisplayName" value="OIDC Provider" />
@@ -141,12 +141,12 @@
         <property name="claim" ref="claim_email" />
         <property name="optional" value="false" />
     </bean>
-    
     <bean class="org.apache.cxf.fediz.service.idp.service.jpa.ApplicationClaimEntity">
         <property name="application" ref="srv-oidc" />
         <property name="claim" ref="claim_role" />
         <property name="optional" value="false" />
     </bean>
+
     <bean class="org.apache.cxf.fediz.service.idp.service.jpa.ApplicationClaimEntity">
         <property name="application" ref="srv-oidc" />
         <property name="claim" ref="claim_givenname" />
@@ -162,7 +162,7 @@
         <property name="claim" ref="claim_email" />
         <property name="optional" value="false" />
     </bean>
-    
+
     <bean id="claim_role"
         class="org.apache.cxf.fediz.service.idp.service.jpa.ClaimEntity">
         <property name="claimType"
diff --git a/systests/oidc/src/test/java/org/apache/cxf/fediz/systests/oidc/AbstractOIDCTest.java b/systests/oidc/src/test/java/org/apache/cxf/fediz/systests/oidc/AbstractOIDCTest.java
index f419de7..21d53a8 100644
--- a/systests/oidc/src/test/java/org/apache/cxf/fediz/systests/oidc/AbstractOIDCTest.java
+++ b/systests/oidc/src/test/java/org/apache/cxf/fediz/systests/oidc/AbstractOIDCTest.java
@@ -96,9 +96,9 @@ abstract class AbstractOIDCTest {
     private static final Integer RP_HTTPS_PORT = Integer.getInteger("rp.https.port");
 
     private static final String CALLBACK_CONTEXT = "/callback";
-    private static final String REDIRECT_URL = "https://127.0.0.1:" + RP_HTTPS_PORT + CALLBACK_CONTEXT;
+    private static final String REDIRECT_URL = "https://localhost:" + RP_HTTPS_PORT + CALLBACK_CONTEXT;
     private static final String LOGOUT_CONTEXT = "/logout";
-    private static final String LOGOUT_URL = "https://127.0.0.1:" + RP_HTTPS_PORT + LOGOUT_CONTEXT;
+    private static final String LOGOUT_URL = "https://localhost:" + RP_HTTPS_PORT + LOGOUT_CONTEXT;
     private static final String LOGOUT_MSG = "logout";
 
     private static Tomcat idpServer;
diff --git a/systests/oidc/src/test/resources/realma/entities-realma.xml b/systests/oidc/src/test/resources/realma/entities-realma.xml
index 79a29f8..f0c94fa 100644
--- a/systests/oidc/src/test/resources/realma/entities-realma.xml
+++ b/systests/oidc/src/test/resources/realma/entities-realma.xml
@@ -40,10 +40,8 @@
         <property name="disableLogoutAddressValidation" value="true"/>
         <property name="supportedProtocols">
             <util:list>
-                <value>http://docs.oasis-open.org/wsfed/federation/200706
-                </value>
-                <value>http://docs.oasis-open.org/ws-sx/ws-trust/200512
-                </value>
+                <value>http://docs.oasis-open.org/wsfed/federation/200706</value>
+                <value>http://docs.oasis-open.org/ws-sx/ws-trust/200512</value>
             </util:list>
         </property>
         <property name="tokenTypesOffered">
@@ -54,7 +52,8 @@
         </property>
         <property name="authenticationURIs">
             <util:map>
-                <entry key="default" value="federation/up" />
+                <entry key="default"
+                       value="federation/up" />
                 <entry key="http://docs.oasis-open.org/wsfed/authorization/200706/authntypes/SslAndKey" 
                        value="federation/krb" />
                 <entry key="http://docs.oasis-open.org/wsfed/authorization/200706/authntypes/default"
@@ -110,7 +109,7 @@
         <property name="passiveRequestorEndpointConstraint" 
                   value="https://localhost:(\d)*/(\w)*helloworld(\w)*/secure/.*" />
     </bean>
-    
+
     <bean id="srv-oidc" class="org.apache.cxf.fediz.service.idp.service.jpa.ApplicationEntity">
         <property name="realm" value="urn:org:apache:cxf:fediz:oidc" />
         <property name="protocol" value="http://docs.oasis-open.org/wsfed/federation/200706" />
@@ -128,26 +127,42 @@
         <property name="optional" value="false" />
     </bean>
     <bean class="org.apache.cxf.fediz.service.idp.service.jpa.ApplicationClaimEntity">
+        <property name="application" ref="srv-fedizhelloworld" />
+        <property name="claim" ref="claim_givenname" />
+        <property name="optional" value="false" />
+    </bean>
+    <bean class="org.apache.cxf.fediz.service.idp.service.jpa.ApplicationClaimEntity">
+        <property name="application" ref="srv-fedizhelloworld" />
+        <property name="claim" ref="claim_surname" />
+        <property name="optional" value="false" />
+    </bean>
+    <bean class="org.apache.cxf.fediz.service.idp.service.jpa.ApplicationClaimEntity">
+        <property name="application" ref="srv-fedizhelloworld" />
+        <property name="claim" ref="claim_email" />
+        <property name="optional" value="false" />
+    </bean>
+    <bean class="org.apache.cxf.fediz.service.idp.service.jpa.ApplicationClaimEntity">
         <property name="application" ref="srv-oidc" />
         <property name="claim" ref="claim_role" />
         <property name="optional" value="false" />
     </bean>
+
     <bean class="org.apache.cxf.fediz.service.idp.service.jpa.ApplicationClaimEntity">
-        <property name="application" ref="srv-fedizhelloworld" />
+        <property name="application" ref="srv-oidc" />
         <property name="claim" ref="claim_givenname" />
         <property name="optional" value="false" />
     </bean>
     <bean class="org.apache.cxf.fediz.service.idp.service.jpa.ApplicationClaimEntity">
-        <property name="application" ref="srv-fedizhelloworld" />
+        <property name="application" ref="srv-oidc" />
         <property name="claim" ref="claim_surname" />
         <property name="optional" value="false" />
     </bean>
     <bean class="org.apache.cxf.fediz.service.idp.service.jpa.ApplicationClaimEntity">
-        <property name="application" ref="srv-fedizhelloworld" />
+        <property name="application" ref="srv-oidc" />
         <property name="claim" ref="claim_email" />
         <property name="optional" value="false" />
     </bean>
-    
+
     <bean id="claim_role"
         class="org.apache.cxf.fediz.service.idp.service.jpa.ClaimEntity">
         <property name="claimType"


[cxf-fediz] 01/02: fediz-systests-oidc: test public client

Posted by bu...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

buhhunyx pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/cxf-fediz.git

commit 736905361b96737d3d38aa7992eb6a8bfebc5cd8
Author: Alexey Markevich <bu...@gmail.com>
AuthorDate: Wed Feb 19 13:02:49 2020 +0300

    fediz-systests-oidc: test public client
---
 .../cxf/fediz/systests/oidc/AbstractOIDCTest.java  | 838 +++++++++------------
 1 file changed, 372 insertions(+), 466 deletions(-)

diff --git a/systests/oidc/src/test/java/org/apache/cxf/fediz/systests/oidc/AbstractOIDCTest.java b/systests/oidc/src/test/java/org/apache/cxf/fediz/systests/oidc/AbstractOIDCTest.java
index 2ea021d..f419de7 100644
--- a/systests/oidc/src/test/java/org/apache/cxf/fediz/systests/oidc/AbstractOIDCTest.java
+++ b/systests/oidc/src/test/java/org/apache/cxf/fediz/systests/oidc/AbstractOIDCTest.java
@@ -32,6 +32,7 @@ import java.util.Arrays;
 import java.util.Date;
 import java.util.List;
 import java.util.Locale;
+import java.util.Map;
 import java.util.TimeZone;
 
 import javax.net.ssl.HttpsURLConnection;
@@ -57,6 +58,7 @@ import com.gargoylesoftware.htmlunit.html.HtmlPage;
 import com.gargoylesoftware.htmlunit.html.HtmlSelect;
 import com.gargoylesoftware.htmlunit.html.HtmlSubmitInput;
 import com.gargoylesoftware.htmlunit.html.HtmlTable;
+import com.gargoylesoftware.htmlunit.html.HtmlTableRow;
 import com.gargoylesoftware.htmlunit.html.HtmlTextInput;
 import com.gargoylesoftware.htmlunit.util.NameValuePair;
 
@@ -66,6 +68,7 @@ import org.apache.catalina.LifecycleState;
 import org.apache.catalina.connector.Connector;
 import org.apache.catalina.startup.Tomcat;
 import org.apache.cxf.fediz.tomcat.FederationAuthenticator;
+import org.apache.cxf.jaxrs.json.basic.JsonMapObjectReaderWriter;
 import org.apache.cxf.rs.security.jose.common.JoseConstants;
 import org.apache.cxf.rs.security.jose.jwa.SignatureAlgorithm;
 import org.apache.cxf.rs.security.jose.jwk.JsonWebKeys;
@@ -90,30 +93,27 @@ import static org.junit.Assert.fail;
 abstract class AbstractOIDCTest {
 
     private static final Integer IDP_HTTPS_PORT = Integer.getInteger("idp.https.port");
-    private static final String RP_HTTPS_PORT = System.getProperty("rp.https.port");
+    private static final Integer RP_HTTPS_PORT = Integer.getInteger("rp.https.port");
 
-    private static final String CALLBACK_CONTEXT_1 = "/callback1";
-    private static final String REDIRECT_URL_1 = "https://127.0.0.1:" + RP_HTTPS_PORT + CALLBACK_CONTEXT_1;
-    private static final String LOGOUT_CONTEXT_1 = "/logout1";
-    private static final String LOGOUT_URL_1 = "https://127.0.0.1:" + RP_HTTPS_PORT + LOGOUT_CONTEXT_1;
+    private static final String CALLBACK_CONTEXT = "/callback";
+    private static final String REDIRECT_URL = "https://127.0.0.1:" + RP_HTTPS_PORT + CALLBACK_CONTEXT;
+    private static final String LOGOUT_CONTEXT = "/logout";
+    private static final String LOGOUT_URL = "https://127.0.0.1:" + RP_HTTPS_PORT + LOGOUT_CONTEXT;
     private static final String LOGOUT_MSG = "logout";
-    private static final String CALLBACK_CONTEXT_2 = "/callback2";
-    private static final String REDIRECT_URL_2 = "https://127.0.0.1:" + RP_HTTPS_PORT + CALLBACK_CONTEXT_2;
 
     private static Tomcat idpServer;
     private static Tomcat rpServer;
 
-    private static String storedClientId;
-    private static String storedClient2Id;
-    private static String storedClientPassword;
-    private static String storedClient2Password;
+    private static String confidentialClientId;
+    private static String confidentialClientSecret;
+    private static String publicClientId;
 
     protected static void startServer(String servletContextName, String fedizConfigPath) throws Exception {
         assertNotNull("Property 'idp.https.port' null", IDP_HTTPS_PORT);
         assertNotNull("Property 'rp.https.port' null", RP_HTTPS_PORT);
 
         idpServer = startServer(IDP_HTTPS_PORT, null, null);
-        rpServer = startServer(Integer.parseInt(RP_HTTPS_PORT), servletContextName, fedizConfigPath);
+        rpServer = startServer(RP_HTTPS_PORT, servletContextName, fedizConfigPath);
 
         TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
         try (InputStream is = Loader.getResource("/server.jks").openStream()) {
@@ -125,7 +125,7 @@ abstract class AbstractOIDCTest {
         sc.init(null, tmf.getTrustManagers(), new java.security.SecureRandom()); 
         HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
 
-        loginToClientsPage(RP_HTTPS_PORT, servletContextName);
+        loginToClientsPage(servletContextName);
     }
 
     private static Tomcat startServer(int port, String servletContextName, String fedizConfigPath)
@@ -187,11 +187,10 @@ abstract class AbstractOIDCTest {
             ctx = server.addContext("", null);
             final String callbackName = "callback";
             Tomcat.addServlet(ctx, callbackName, new CallbackServlet());
-            ctx.addServletMappingDecoded(CALLBACK_CONTEXT_1, callbackName);
-            ctx.addServletMappingDecoded(CALLBACK_CONTEXT_2, callbackName);
+            ctx.addServletMappingDecoded(CALLBACK_CONTEXT, callbackName);
             final String logoutName = "logout";
             Tomcat.addServlet(ctx, logoutName, new LogoutServlet());
-            ctx.addServletMappingDecoded(LOGOUT_CONTEXT_1, logoutName);
+            ctx.addServletMappingDecoded(LOGOUT_CONTEXT, logoutName);
         }
 
         server.getService().addConnector(httpsConnector);
@@ -203,7 +202,7 @@ abstract class AbstractOIDCTest {
 
     protected static void shutdownServer(String servletContextName) throws Exception {
         try {
-            loginToClientsPageAndDeleteClient(RP_HTTPS_PORT, servletContextName);
+            loginToClientsPageAndDeleteClient(servletContextName);
         } finally {
             shutdownServer(idpServer);
             shutdownServer(rpServer);
@@ -220,74 +219,76 @@ abstract class AbstractOIDCTest {
         }
     }
 
-    private String getRpHttpsPort() {
-        return RP_HTTPS_PORT;
-    }
-
     protected abstract String getServletContextName();
 
     // Runs as BeforeClass: Login to the OIDC Clients page + create two new clients
-    private static void loginToClientsPage(String rpPort, String servletContext) throws Exception {
-        URL url = oidcEndpoint(servletContext, "/console/clients");
-        String user = "alice";
-        String password = "ecila";
-
+    private static void loginToClientsPage(String servletContext) throws IOException {
         // Login to the client page successfully
-        WebClient webClient = setupWebClientIDP(user, password);
-        HtmlPage loginPage = login(url, webClient);
-        final String bodyTextContent = loginPage.getBody().getTextContent();
-        assertTrue(bodyTextContent.contains("Registered Clients"));
+        try (WebClient webClient = setupWebClientIDP("alice", "ecila")) {
+            final URL clientsUrl = oidcEndpoint(servletContext, "/console/clients");
+            HtmlPage registeredClientsPage = login(clientsUrl, webClient);
+            String registeredClientsPageBody = registeredClientsPage.getBody().getTextContent();
+            assertTrue(registeredClientsPageBody.contains("Registered Clients"));
 
-        // Now try to register a new client
-        HtmlPage registeredClientPage =
-            registerNewClient(webClient, url, "new-client", REDIRECT_URL_1,
-                              "https://cxf.apache.org", LOGOUT_URL_1);
-        String registeredClientPageBody = registeredClientPage.getBody().getTextContent();
-        assertTrue(registeredClientPageBody.contains("Registered Clients"));
-        assertTrue(registeredClientPageBody.contains("new-client"));
-        assertTrue(registeredClientPageBody.contains(REDIRECT_URL_1));
-
-        HtmlTable table = registeredClientPage.getHtmlElementById("registered_clients");
-        storedClientId = table.getCellAt(1, 1).asText().trim();
-        assertNotNull(storedClientId);
-
-        // Get the password
-        registeredClientPage = webClient.getPage(url + "/" + storedClientId);
-        table = registeredClientPage.getHtmlElementById("client");
-        storedClientPassword = table.getCellAt(1, 2).asText().trim();
-
-        // Try to register another new client
-        registeredClientPage =
-            registerNewClient(webClient, url, "new-client2", REDIRECT_URL_2,
-                              "https://ws.apache.org", "https://localhost:12346");
-        registeredClientPageBody = registeredClientPage.getBody().getTextContent();
-        assertTrue(registeredClientPageBody.contains("Registered Clients"));
-        assertTrue(registeredClientPageBody.contains("new-client"));
-        assertTrue(registeredClientPageBody.contains(REDIRECT_URL_1));
-        assertTrue(registeredClientPageBody.contains("new-client2"));
-        assertTrue(registeredClientPageBody.contains(REDIRECT_URL_2));
-
-        table = registeredClientPage.getHtmlElementById("registered_clients");
-        storedClient2Id = table.getCellAt(2, 1).asText().trim();
-        if (storedClient2Id.equals(storedClientId)) {
-            storedClient2Id = table.getCellAt(1, 1).asText().trim();
+            // Now try to register a new client
+            registeredClientsPage =
+                registerNewClient(webClient, clientsUrl, "confidential-client", REDIRECT_URL,
+                                  "https://cxf.apache.org", LOGOUT_URL);
+            registeredClientsPageBody = registeredClientsPage.getBody().getTextContent();
+            assertTrue(registeredClientsPageBody.contains("confidential-client"));
+            assertTrue(registeredClientsPageBody.contains(REDIRECT_URL));
+
+            confidentialClientId = getClientIdByName("confidential-client", registeredClientsPage);
+
+            // Get the password
+            confidentialClientSecret = getClientSecret(webClient, clientsUrl, confidentialClientId);
+
+            // Register public client
+            registeredClientsPage =
+                registerNewClient(webClient, clientsUrl, "public-client", REDIRECT_URL,
+                                  "https://ws.apache.org", LOGOUT_URL, false);
+            registeredClientsPageBody = registeredClientsPage.getBody().getTextContent();
+            assertTrue(registeredClientsPageBody.contains("Registered Clients"));
+            assertTrue(registeredClientsPageBody.contains("confidential-client"));
+            assertTrue(registeredClientsPageBody.contains(REDIRECT_URL));
+            assertTrue(registeredClientsPageBody.contains("public-client"));
+
+            publicClientId = getClientIdByName("public-client", registeredClientsPage);
         }
-        assertNotNull(storedClient2Id);
+    }
 
-        // Get the password
-        registeredClientPage = webClient.getPage(url + "/" + storedClient2Id);
-        table = registeredClientPage.getHtmlElementById("client");
-        storedClient2Password = table.getCellAt(1, 2).asText().trim();
-        assertNotNull(storedClient2Password);
+    private static String getClientIdByName(String clientName, HtmlPage registeredClientsPage) {
+        final HtmlTable table = registeredClientsPage.getHtmlElementById("registered_clients");
+        for (final HtmlTableRow row : table.getRows()) {
+            if (clientName.equals(row.getCell(0).asText())) {
+                final String clientId = row.getCell(1).asText();
+                assertNotNull(clientId);
+                return clientId;
+            }
+        }
+        throw new IllegalArgumentException("Client '" + clientName + "' not found");
+    }
 
-        webClient.close();
+    private static String getClientSecret(WebClient webClient, URL clientsUrl, String clientId) throws IOException {
+        final HtmlPage registeredClientPage = webClient.getPage(clientsUrl + "/" + clientId);
+        final HtmlTable table = registeredClientPage.getHtmlElementById("client");
+        assertEquals(clientId, table.getCellAt(1, 0).asText());
+        return table.getCellAt(1, 2).asText();
     }
 
-    private static HtmlPage registerNewClient(WebClient webClient, URL url,
+    private static HtmlPage registerNewClient(WebClient webClient, URL clientsUrl,
+        String clientName, String redirectURI,
+        String clientAudience,
+        String logoutURI) throws IOException {
+        return registerNewClient(webClient, clientsUrl, clientName, redirectURI, clientAudience, logoutURI, true);
+    }
+
+    private static HtmlPage registerNewClient(WebClient webClient, URL clientsUrl,
                                             String clientName, String redirectURI,
                                             String clientAudience,
-                                            String logoutURI) throws Exception {
-        HtmlPage registerPage = webClient.getPage(url + "/register");
+                                            String logoutURI,
+                                            boolean confidential) throws IOException {
+        HtmlPage registerPage = webClient.getPage(clientsUrl + "/register");
 
         final HtmlForm form = registerPage.getForms().get(0);
 
@@ -295,7 +296,7 @@ abstract class AbstractOIDCTest {
         final HtmlTextInput clientNameInput = form.getInputByName("client_name");
         clientNameInput.setValueAttribute(clientName);
         final HtmlSelect clientTypeSelect = form.getSelectByName("client_type");
-        clientTypeSelect.setSelectedAttribute("confidential", true);
+        clientTypeSelect.setSelectedAttribute(confidential ? "confidential" : "public", true);
         final HtmlTextInput redirectURIInput = form.getInputByName("client_redirectURI");
         redirectURIInput.setValueAttribute(redirectURI);
         final HtmlTextInput clientAudienceURIInput = form.getInputByName("client_audience");
@@ -308,44 +309,37 @@ abstract class AbstractOIDCTest {
     }
 
     // Runs as AfterClass: Login to the OIDC Clients page + delete the created clients!
-    private static void loginToClientsPageAndDeleteClient(String rpPort, String servletContext)
-            throws Exception {
-        URL url = oidcEndpoint(servletContext, "/console/clients");
-        String user = "alice";
-        String password = "ecila";
-
+    private static void loginToClientsPageAndDeleteClient(String servletContext) throws IOException {
         // Login to the client page successfully
-        WebClient webClient = setupWebClientIDP(user, password);
-        HtmlPage loginPage = login(url, webClient);
-        final String bodyTextContent = loginPage.getBody().getTextContent();
-        assertTrue(bodyTextContent.contains("Registered Clients"));
-
-        // Get the client identifier
-        HtmlTable table = loginPage.getHtmlElementById("registered_clients");
-        String clientId = table.getCellAt(1, 1).asText().trim();
-        assertNotNull(clientId);
-        String clientId2 = table.getCellAt(2, 1).asText().trim();
-        assertNotNull(clientId2);
-
-        // Now go to the specific client page
-        HtmlPage registeredClientsPage = deleteClient(webClient, url, clientId);
-
-        // Check we have one more registered clients
-        table = registeredClientsPage.getHtmlElementById("registered_clients");
-        assertEquals(2, table.getRowCount());
-
-        // Now delete the other client
-        registeredClientsPage = deleteClient(webClient, url, clientId2);
-
-        // Check we have no more registered clients
-        table = registeredClientsPage.getHtmlElementById("registered_clients");
-        assertEquals(1, table.getRowCount());
-
-        webClient.close();
+        try (WebClient webClient = setupWebClientIDP("alice", "ecila")) {
+            final URL clientsUrl = oidcEndpoint(servletContext, "/console/clients");
+            HtmlPage registeredClientsPage = login(clientsUrl, webClient);
+
+            // Get the client identifier
+            HtmlTable table = registeredClientsPage.getHtmlElementById("registered_clients");
+            String clientId = table.getCellAt(1, 1).asText();
+            assertNotNull(clientId);
+            String clientId2 = table.getCellAt(2, 1).asText();
+            assertNotNull(clientId2);
+
+            // Now go to the specific client page
+            registeredClientsPage = deleteClient(webClient, clientsUrl, clientId);
+
+            // Check we have one more registered clients
+            table = registeredClientsPage.getHtmlElementById("registered_clients");
+            assertEquals(2, table.getRowCount());
+
+            // Now delete the other client
+            registeredClientsPage = deleteClient(webClient, clientsUrl, clientId2);
+
+            // Check we have no more registered clients
+            table = registeredClientsPage.getHtmlElementById("registered_clients");
+            assertEquals(1, table.getRowCount());
+        }
     }
 
-    private static HtmlPage deleteClient(WebClient webClient, URL url, String clientId) throws Exception {
-        HtmlPage clientPage = webClient.getPage(url + "/" + clientId);
+    private static HtmlPage deleteClient(WebClient webClient, URL clientsUrl, String clientId) throws IOException {
+        HtmlPage clientPage = webClient.getPage(clientsUrl + "/" + clientId);
 
         final HtmlForm deleteForm = clientPage.getFormByName("deleteForm");
         assertNotNull(deleteForm);
@@ -359,11 +353,9 @@ abstract class AbstractOIDCTest {
     @org.junit.Test
     public void testCreatedClients() throws Exception {
         final URL url = oidcEndpoint("/console/clients");
-        String user = "alice";
-        String password = "ecila";
 
         // Login to the client page successfully
-        WebClient webClient = setupWebClientIDP(user, password);
+        WebClient webClient = setupWebClientIDP("alice", "ecila");
         HtmlPage loginPage = login(url, webClient);
         final String bodyTextContent = loginPage.getBody().getTextContent();
         assertTrue(bodyTextContent.contains("Registered Clients"));
@@ -375,24 +367,18 @@ abstract class AbstractOIDCTest {
         assertEquals(table.getRows().size(), 3);
 
         // Now check the first client
-        String clientId = table.getCellAt(1, 1).asText().trim();
+        String clientId = table.getCellAt(1, 1).asText();
         assertNotNull(clientId);
 
         // Check the Date
-        String date = table.getCellAt(1, 2).asText().trim();
+        String date = table.getCellAt(1, 2).asText().trim(); // <br/>
         SimpleDateFormat dateFormat = new SimpleDateFormat("dd MMM yyyy", Locale.US);
         dateFormat.setTimeZone(TimeZone.getTimeZone("GMT"));
         assertEquals(dateFormat.format(new Date()), date);
 
         // Check the redirect URI
-        String redirectURI = table.getCellAt(1, 3).asText().trim();
-        assertTrue(REDIRECT_URL_1.equals(redirectURI)
-                          || REDIRECT_URL_2.equals(redirectURI));
-
-        // Now check the specific client page
-        HtmlPage clientPage = webClient.getPage(url + "/" + clientId);
-        HtmlTable clientTable = clientPage.getHtmlElementById("client");
-        assertEquals(clientId, clientTable.getCellAt(1, 0).asText().trim());
+        String redirectURI = table.getCellAt(1, 3).asText().trim(); // <br/>
+        assertTrue(REDIRECT_URL.equals(redirectURI));
 
         webClient.close();
     }
@@ -401,88 +387,67 @@ abstract class AbstractOIDCTest {
     @org.junit.Test
     public void testRegisteredClientsAsBob() throws Exception {
         final URL url = oidcEndpoint("/console/clients");
-        String user = "bob";
-        String password = "bob";
 
         // Login to the client page successfully
-        WebClient webClient = setupWebClientIDP(user, password);
-        HtmlPage loginPage = login(url, webClient);
-        final String bodyTextContent = loginPage.getBody().getTextContent();
-        assertTrue(bodyTextContent.contains("Registered Clients"));
-
-        // Get the new client identifier
-        HtmlTable table = loginPage.getHtmlElementById("registered_clients");
+        try (WebClient webClient = setupWebClientIDP("bob", "bob")) {
+            HtmlPage loginPage = login(url, webClient);
+            final String bodyTextContent = loginPage.getBody().getTextContent();
+            assertTrue(bodyTextContent.contains("Registered Clients"));
 
-        // 2 clients
-        assertEquals(table.getRows().size(), 1);
+            // Get the new client identifier
+            HtmlTable table = loginPage.getHtmlElementById("registered_clients");
 
-        webClient.close();
+            // no clients
+            assertEquals(table.getRows().size(), 1);
+        }
     }
 
     @org.junit.Test
-    public void testOIDCLoginForClient1() throws Exception {
-
-        final URL url = oidcEndpointBuilder("/idp/authorize")
-            .queryParam("client_id", storedClientId)
+    public void testOIDCLoginForConfidentialClient() throws IOException {
+        final URL authorizationUrl = oidcEndpointBuilder("/idp/authorize")
+            .queryParam("client_id", confidentialClientId)
             .queryParam("response_type", "code")
             .queryParam("scope", "openid")
             .build().toURL();
-        String user = "alice";
-        String password = "ecila";
-
-        // Login to the OIDC token endpoint + get the authorization code
-        final String authorizationCode;
-        try (WebClient webClient = setupWebClientIDP(user, password)) {
-            authorizationCode = loginAndGetAuthorizationCode(url, webClient);
-        }
-
-        // Now use the code to get an IdToken
-        final String rawToken = getRawToken(authorizationCode, storedClientId, storedClientPassword);
-
-        // Check the IdToken
-        validateIdToken(getIdToken(rawToken), storedClientId);
+        testOIDCLogin(authorizationUrl, confidentialClientId, confidentialClientSecret);
     }
 
     @org.junit.Test
-    public void testOIDCLoginForClient2() throws Exception {
-        final URL url = oidcEndpointBuilder("/idp/authorize")
-            .queryParam("client_id", storedClient2Id)
+    public void testOIDCLoginForPublicClient() throws IOException {
+        final URL authorizationUrl = oidcEndpointBuilder("/idp/authorize")
+            .queryParam("client_id", publicClientId)
             .queryParam("response_type", "code")
             .queryParam("scope", "openid")
+            .queryParam("redirect_uri", REDIRECT_URL)
             .build().toURL();
-        String user = "alice";
-        String password = "ecila";
+        testOIDCLogin(authorizationUrl, publicClientId, null);
+    }
 
+    private void testOIDCLogin(final URL authorizationUrl, String clientId, String clientSecret) throws IOException {
         // Login to the OIDC token endpoint + get the authorization code
-        final String authorizationCode;
-        try (WebClient webClient = setupWebClientIDP(user, password)) {
-            authorizationCode = loginAndGetAuthorizationCode(url, webClient);
-        }
+        final String authorizationCode = loginAndGetAuthorizationCode(authorizationUrl, "alice", "ecila");
 
         // Now use the code to get an IdToken
-        final String rawToken = getRawToken(authorizationCode, storedClient2Id, storedClient2Password);
+        final Map<String, Object> json = getTokenJson(authorizationCode, clientId, clientSecret);
 
         // Check the IdToken
-        validateIdToken(getIdToken(rawToken), storedClient2Id);
+        validateIdToken(getIdToken(json), clientId);
     }
 
     @org.junit.Test
     public void testUsingCodeForOtherClient() throws Exception {
         // Get the code for the first client
-        final URL url = oidcEndpointBuilder("/idp/authorize")
-            .queryParam("client_id", storedClientId)
+        final URL authorizationUrl = oidcEndpointBuilder("/idp/authorize")
+            .queryParam("client_id", confidentialClientId)
             .queryParam("response_type", "code")
             .queryParam("scope", "openid")
             .build().toURL();
-        String user = "alice";
-        String password = "ecila";
 
         // Login to the OIDC token endpoint + get the authorization code
-        try (WebClient webClient = setupWebClientIDP(user, password)) {
-            String authorizationCode = loginAndGetAuthorizationCode(url, webClient);
-
+        final String authorizationCode = loginAndGetAuthorizationCode(authorizationUrl, "alice", "ecila");
+        try {
             // Now try and get a token for the second client
-            getRawToken(authorizationCode, storedClient2Id, storedClient2Password);
+            getTokenJson(authorizationCode, publicClientId, null);
             fail();
         } catch (FailingHttpStatusCodeException ex) {
             assertEquals(Status.BAD_REQUEST.getStatusCode(), ex.getStatusCode());
@@ -491,52 +456,42 @@ abstract class AbstractOIDCTest {
 
     @org.junit.Test
     public void testBadClientId() throws Exception {
-        final URL url = oidcEndpointBuilder("/idp/authorize")
-            .queryParam("client_id", storedClientId.substring(1))
+        final URL authorizationUrl = oidcEndpointBuilder("/idp/authorize")
+            .queryParam("client_id", confidentialClientId.substring(1))
             .queryParam("response_type", "code")
             .queryParam("scope", "openid")
             .build().toURL();
-        String user = "alice";
-        String password = "ecila";
 
         // Login to the OIDC token endpoint + get the authorization code
-        try (WebClient webClient = setupWebClientIDP(user, password)) {
-            final String response = loginAndGetAuthorizationCode(url, webClient);
-            assertTrue(response.contains("invalid_request"));
-        }
+        final String response = loginAndGetAuthorizationCode(authorizationUrl, "alice", "ecila");
+        assertTrue(response.contains("invalid_request"));
     }
 
     @org.junit.Test
     public void testEmptyClientId() throws Exception {
-        final URL url = oidcEndpointBuilder("/idp/authorize")
+        final URL authorizationUrl = oidcEndpointBuilder("/idp/authorize")
             .queryParam("client_id", "")
             .queryParam("response_type", "code")
             .queryParam("scope", "openid")
             .build().toURL();
-        String user = "alice";
-        String password = "ecila";
 
         // Login to the OIDC token endpoint + get the authorization code
-        try (WebClient webClient = setupWebClientIDP(user, password)) {
-            final String response = loginAndGetAuthorizationCode(url, webClient);
-            assertTrue(response.contains("invalid_request"));
-        }
+        final String response = loginAndGetAuthorizationCode(authorizationUrl, "alice", "ecila");
+        assertTrue(response.contains("invalid_request"));
     }
 
     @org.junit.Test
     public void testIncorrectRedirectURI() throws Exception {
-        final URL url = oidcEndpointBuilder("/idp/authorize")
-            .queryParam("client_id", storedClientId)
+        final URL authorizationUrl = oidcEndpointBuilder("/idp/authorize")
+            .queryParam("client_id", confidentialClientId)
             .queryParam("response_type", "code")
             .queryParam("scope", "openid")
             .queryParam("redirect_uri", "https://127.0.0.5")
             .build().toURL();
-        String user = "alice";
-        String password = "ecila";
 
         // Login to the OIDC token endpoint + get the authorization code
-        try (WebClient webClient = setupWebClientIDP(user, password)) {
-            loginAndGetAuthorizationCode(url, webClient);
+        try {
+            loginAndGetAuthorizationCode(authorizationUrl, "alice", "ecila");
         } catch (FailingHttpStatusCodeException e) {
             assertEquals(Status.BAD_REQUEST.getStatusCode(), e.getStatusCode());
         }
@@ -544,18 +499,13 @@ abstract class AbstractOIDCTest {
 
     @org.junit.Test
     public void testCreateClientWithInvalidRegistrationURI() throws Exception {
-        final URL url = oidcEndpoint("/console/clients");
-        String user = "alice";
-        String password = "ecila";
-
         // Login to the client page successfully
-        try (WebClient webClient = setupWebClientIDP(user, password)) {
-            HtmlPage loginPage = login(url, webClient);
-            final String bodyTextContent = loginPage.getBody().getTextContent();
-            assertTrue(bodyTextContent.contains("Registered Clients"));
+        try (WebClient webClient = setupWebClientIDP("alice", "ecila")) {
+            final URL clientsUrl = oidcEndpoint("/console/clients");
+            login(clientsUrl, webClient);
 
             // Now try to register a new client
-            HtmlPage errorPage = registerNewClient(webClient, url, "asfxyz", "https://127.0.0.1//",
+            HtmlPage errorPage = registerNewClient(webClient, clientsUrl, "asfxyz", "https://127.0.0.1//",
                           "https://cxf.apache.org", "https://localhost:12345");
             assertTrue(errorPage.asText().contains("Invalid Client Registration"));
         }
@@ -563,18 +513,13 @@ abstract class AbstractOIDCTest {
 
     @org.junit.Test
     public void testCreateClientWithRegistrationURIFragment() throws Exception {
-        final URL url = oidcEndpoint("/console/clients");
-        String user = "alice";
-        String password = "ecila";
-
         // Login to the client page successfully
-        try (WebClient webClient = setupWebClientIDP(user, password)) {
-            HtmlPage loginPage = login(url, webClient);
-            final String bodyTextContent = loginPage.getBody().getTextContent();
-            assertTrue(bodyTextContent.contains("Registered Clients"));
+        try (WebClient webClient = setupWebClientIDP("alice", "ecila")) {
+            final URL clientsUrl = oidcEndpoint("/console/clients");
+            login(clientsUrl, webClient);
 
             // Now try to register a new client
-            HtmlPage errorPage = registerNewClient(webClient, url, "asfxyz", "https://127.0.0.1#fragment",
+            HtmlPage errorPage = registerNewClient(webClient, clientsUrl, "asfxyz", "https://127.0.0.1#fragment",
                           "https://cxf.apache.org", "https://localhost:12345");
             assertTrue(errorPage.asText().contains("Invalid Client Registration"));
         }
@@ -582,18 +527,13 @@ abstract class AbstractOIDCTest {
 
     @org.junit.Test
     public void testCreateClientWithInvalidAudienceURI() throws Exception {
-        final URL url = oidcEndpoint("/console/clients");
-        String user = "alice";
-        String password = "ecila";
-
         // Login to the client page successfully
-        try (WebClient webClient = setupWebClientIDP(user, password)) {
-            HtmlPage loginPage = login(url, webClient);
-            final String bodyTextContent = loginPage.getBody().getTextContent();
-            assertTrue(bodyTextContent.contains("Registered Clients"));
+        try (WebClient webClient = setupWebClientIDP("alice", "ecila")) {
+            final URL clientsUrl = oidcEndpoint("/console/clients");
+            login(clientsUrl, webClient);
 
             // Now try to register a new client
-            HtmlPage errorPage = registerNewClient(webClient, url, "asfxyz", "https://127.0.0.1/",
+            HtmlPage errorPage = registerNewClient(webClient, clientsUrl, "asfxyz", "https://127.0.0.1/",
                           "https://cxf.apache.org//", "https://localhost:12345");
             assertTrue(errorPage.asText().contains("Invalid Client Registration"));
         }
@@ -601,18 +541,13 @@ abstract class AbstractOIDCTest {
 
     @org.junit.Test
     public void testCreateClientWithInvalidLogoutURI() throws Exception {
-        final URL url = oidcEndpoint("/console/clients");
-        String user = "alice";
-        String password = "ecila";
-
         // Login to the client page successfully
-        try (WebClient webClient = setupWebClientIDP(user, password)) {
-            HtmlPage loginPage = login(url, webClient);
-            final String bodyTextContent = loginPage.getBody().getTextContent();
-            assertTrue(bodyTextContent.contains("Registered Clients"));
+        try (WebClient webClient = setupWebClientIDP("alice", "ecila")) {
+            final URL clientsUrl = oidcEndpoint("/console/clients");
+            login(clientsUrl, webClient);
 
             // Now try to register a new client
-            HtmlPage errorPage = registerNewClient(webClient, url, "asfxyz", "https://127.0.0.1/",
+            HtmlPage errorPage = registerNewClient(webClient, clientsUrl, "asfxyz", "https://127.0.0.1/",
                           "https://cxf.apache.org/", "https://localhost:12345//");
             assertTrue(errorPage.asText().contains("Invalid Client Registration"));
         }
@@ -620,18 +555,13 @@ abstract class AbstractOIDCTest {
 
     @org.junit.Test
     public void testCreateClientWithAudienceURIFragment() throws Exception {
-        final URL url = oidcEndpoint("/console/clients");
-        String user = "alice";
-        String password = "ecila";
-
         // Login to the client page successfully
-        try (WebClient webClient = setupWebClientIDP(user, password)) {
-            HtmlPage loginPage = login(url, webClient);
-            final String bodyTextContent = loginPage.getBody().getTextContent();
-            assertTrue(bodyTextContent.contains("Registered Clients"));
+        try (WebClient webClient = setupWebClientIDP("alice", "ecila")) {
+            final URL clientsUrl = oidcEndpoint("/console/clients");
+            login(clientsUrl, webClient);
 
             // Now try to register a new client
-            HtmlPage errorPage = registerNewClient(webClient, url, "asfxyz", "https://127.0.0.1",
+            HtmlPage errorPage = registerNewClient(webClient, clientsUrl, "asfxyz", "https://127.0.0.1",
                           "https://cxf.apache.org#fragment", "https://localhost:12345");
             assertTrue(errorPage.asText().contains("Invalid Client Registration"));
         }
@@ -647,9 +577,7 @@ abstract class AbstractOIDCTest {
             new NameValuePair("client_secret", "ecila"),
             new NameValuePair("grant_type", "client_credentials")));
 
-        try (WebClient webClient = new WebClient()) {
-            webClient.getOptions().setUseInsecureSSL(true);
-            webClient.getOptions().setJavaScriptEnabled(false);
+        try (WebClient webClient = setupWebClient()) {
             final String response = webClient.getPage(request).getWebResponse().getContentAsString();
             assertTrue(response.contains("access_token"));
         }
@@ -657,333 +585,316 @@ abstract class AbstractOIDCTest {
 
     @org.junit.Test
     public void testCreateClientWithSupportedTLD() throws Exception {
-        final URL url = oidcEndpoint("/console/clients");
-        String user = "alice";
-        String password = "ecila";
-
         // Login to the client page successfully
-        WebClient webClient = setupWebClientIDP(user, password);
-        HtmlPage loginPage = login(url, webClient);
-        final String bodyTextContent = loginPage.getBody().getTextContent();
-        assertTrue(bodyTextContent.contains("Registered Clients"));
-
-        // Register a client with a supported TLD
-        HtmlPage registeredClientPage = registerNewClient(webClient, url, "tld1", "https://www.apache.corp",
-            "https://cxf.apache.org", "https://localhost:12345");
-        String registeredClientPageBody = registeredClientPage.getBody().getTextContent();
-        assertTrue(registeredClientPageBody.contains("Registered Clients"));
-        assertTrue(registeredClientPageBody.contains("tld1"));
-        assertTrue(registeredClientPageBody.contains("https://www.apache.corp"));
-
-        HtmlTable table = registeredClientPage.getHtmlElementById("registered_clients");
-        String clientId = table.getCellAt(3, 1).asText().trim();
-
-        // Register a client with an unsupported TLD
-        HtmlPage errorPage = registerNewClient(webClient, url, "tld2", "https://www.apache.corp2",
-                                               "https://cxf.apache.org", "https://localhost:12345");
-        assertTrue(errorPage.asText().contains("Invalid Client Registration"));
-
-        // Delete the first client above
-        deleteClient(webClient, url, clientId);
+        try (WebClient webClient = setupWebClientIDP("alice", "ecila")) {
+            final URL clientsUrl = oidcEndpoint("/console/clients");
+            login(clientsUrl, webClient);
+
+            // Register a client with a supported TLD
+            HtmlPage registeredClientsPage = registerNewClient(webClient, clientsUrl, "tld1", "https://www.apache.corp",
+                "https://cxf.apache.org", "https://localhost:12345");
+            String registeredClientPageBody = registeredClientsPage.getBody().getTextContent();
+            assertTrue(registeredClientPageBody.contains("tld1"));
+            assertTrue(registeredClientPageBody.contains("https://www.apache.corp"));
+
+            final String clientId = getClientIdByName("tld1", registeredClientsPage);
+
+            // Register a client with an unsupported TLD
+            HtmlPage errorPage = registerNewClient(webClient, clientsUrl, "tld2", "https://www.apache.corp2",
+                                                   "https://cxf.apache.org", "https://localhost:12345");
+            assertTrue(errorPage.asText().contains("Invalid Client Registration"));
 
-        webClient.close();
+            // Delete the first client above
+            deleteClient(webClient, clientsUrl, clientId);
+        }
     }
 
     @org.junit.Test
     public void testLogout() throws Exception {
         // 1. Log in
-        final URL url = oidcEndpointBuilder("/idp/authorize")
-            .queryParam("client_id", storedClientId)
+        final URL authorizationUrl = oidcEndpointBuilder("/idp/authorize")
+            .queryParam("client_id", confidentialClientId)
             .queryParam("response_type", "code")
             .queryParam("scope", "openid")
             .build().toURL();
-        String user = "alice";
-        String password = "ecila";
 
         // Login to the OIDC token endpoint + get the authorization code
-        WebClient webClient = setupWebClientIDP(user, password);
-        final String authorizationCode1 = loginAndGetAuthorizationCode(url, webClient);
+        try (WebClient webClient = setupWebClientIDP("alice", "ecila")) {
+            final String authorizationCode = login(authorizationUrl, webClient).getWebResponse().getContentAsString();
 
-        // 2. Get another authorization code without username/password. This should work as we are logged on
-        webClient.getCredentialsProvider().clear();
-
-        final String authorizationCode2 = webClient.getPage(url).getWebResponse().getContentAsString();
-        assertNotNull(authorizationCode2);
-        assertNotEquals(authorizationCode1, authorizationCode2);
+            // 2. Get another authorization code without username/password. This should work as we are logged on
+            final String authorizationCode2 = webClient.getPage(authorizationUrl).getWebResponse().getContentAsString();
+            assertNotNull(authorizationCode2);
+            assertNotEquals(authorizationCode, authorizationCode2);
 
-        // 3. Log out
-        URL logoutUrl =
-            UriBuilder.fromUri("https://localhost:" + getRpHttpsPort() + "/" + getServletContextName() + "/idp/logout")
-                .queryParam("client_id", storedClientId)
+            // 3. Log out
+            final URL logoutUrl = oidcEndpointBuilder("/idp/logout")
+                .queryParam("client_id", confidentialClientId)
                 .build().toURL();
 
-        final String logoutContent = webClient.getPage(logoutUrl).getWebResponse().getContentAsString();
-        assertEquals(LOGOUT_MSG, logoutContent);
+            final String logoutContent = webClient.getPage(logoutUrl).getWebResponse().getContentAsString();
+            assertEquals(LOGOUT_MSG, logoutContent);
 
-        // 4. Get another authorization code without username/password. This should fail as we have logged out
-        try {
-            loginAndGetAuthorizationCode(url, webClient);
-            fail("Failure expected after logout");
-        } catch (FailingHttpStatusCodeException ex) {
-            assertEquals(Status.UNAUTHORIZED.getStatusCode(), ex.getStatusCode());
+            // 4. Get another authorization code without username/password. This should fail as we have logged out
+            try {
+                webClient.getPage(authorizationUrl);
+                fail("Failure expected after logout");
+            } catch (FailingHttpStatusCodeException ex) {
+                assertEquals(Status.UNAUTHORIZED.getStatusCode(), ex.getStatusCode());
+            }
         }
+    }
 
-        webClient.close();
+    @org.junit.Test
+    public void testLogoutForConfidentialClientViaTokenHint() throws IOException {
+        final URL authorizationUrl = oidcEndpointBuilder("/idp/authorize")
+            .queryParam("client_id", confidentialClientId)
+            .queryParam("response_type", "code")
+            .queryParam("scope", "openid")
+            .build().toURL();
+        testLogoutViaTokenHint(authorizationUrl, confidentialClientId, confidentialClientSecret);
     }
 
     @org.junit.Test
-    public void testLogoutViaTokenHint() throws Exception {
-        // 1. Log in
-        final URL url = oidcEndpointBuilder("/idp/authorize")
-            .queryParam("client_id", storedClientId)
+    public void testLogoutForPublicClientViaTokenHint() throws IOException {
+        final URL authorizationUrl = oidcEndpointBuilder("/idp/authorize")
+            .queryParam("client_id", publicClientId)
             .queryParam("response_type", "code")
             .queryParam("scope", "openid")
+            .queryParam("redirect_uri", REDIRECT_URL)
             .build().toURL();
-        String user = "alice";
-        String password = "ecila";
+        testLogoutViaTokenHint(authorizationUrl, publicClientId, null);
+    }
 
-        // Login to the OIDC token endpoint + get the authorization code
-        WebClient webClient = setupWebClientIDP(user, password);
-        final String authorizationCode = loginAndGetAuthorizationCode(url, webClient);
-        webClient.getCredentialsProvider().clear();
+    private void testLogoutViaTokenHint(final URL authorizationUrl, String clientId, String clientSecret)
+        throws IOException {
+        // 1. Login to the OIDC authorization endpoint + get the authorization code
+        try (WebClient webClient = setupWebClientIDP("alice", "ecila")) {
+            final String authorizationCode = login(authorizationUrl, webClient).getWebResponse().getContentAsString();
 
-        // Now use the code to get an IdToken
-        final String rawToken = getRawToken(authorizationCode, storedClientId, storedClientPassword);
+            // Now use the code to get an IdToken
+            final Map<String, Object> json = getTokenJson(authorizationCode, clientId, clientSecret);
 
-        // Check the IdToken
-        String idToken = getIdToken(rawToken);
-        validateIdToken(idToken, storedClientId);
+            // Check the IdToken
+            final String idToken = getIdToken(json);
+            validateIdToken(idToken, clientId);
 
-        // 2. Log out using the token hint
-        URL logoutUrl =
-            UriBuilder.fromUri("https://localhost:" + getRpHttpsPort() + "/" + getServletContextName() + "/idp/logout")
+            // 2. Log out using the token hint
+            final URL logoutUrl = oidcEndpointBuilder("/idp/logout")
                 .queryParam("id_token_hint", idToken)
-                //.queryParam("post_logout_redirect_uri", LOGOUT_URL_1)
+                //.queryParam("post_logout_redirect_uri", LOGOUT_URL) // optional
                 .build().toURL();
 
-        final String logoutContent = webClient.getPage(logoutUrl).getWebResponse().getContentAsString();
-        assertEquals(LOGOUT_MSG, logoutContent);
+            final String logoutContent = webClient.getPage(logoutUrl).getWebResponse().getContentAsString();
+            assertEquals(LOGOUT_MSG, logoutContent);
 
-        // 3. Get another authorization code without username/password. This should fail as we have logged out
-        try {
-            loginAndGetAuthorizationCode(url, webClient);
-            fail("Failure expected after logout");
-        } catch (FailingHttpStatusCodeException ex) {
-            assertEquals(Status.UNAUTHORIZED.getStatusCode(), ex.getStatusCode());
+            // 3. Get another authorization code without username/password. This should fail as we have logged out
+            try {
+                webClient.getPage(authorizationUrl);
+                fail("Failure expected after logout");
+            } catch (FailingHttpStatusCodeException ex) {
+                assertEquals(Status.UNAUTHORIZED.getStatusCode(), ex.getStatusCode());
+            }
         }
-
-        webClient.close();
     }
 
     @org.junit.Test
     public void testLogoutWrongPostLogoutRedirectUri() throws Exception {
         // 1. Log in
-        final URL url = oidcEndpointBuilder("/idp/authorize")
-            .queryParam("client_id", storedClientId)
+        final URL authorizationUrl = oidcEndpointBuilder("/idp/authorize")
+            .queryParam("client_id", confidentialClientId)
             .queryParam("response_type", "code")
             .queryParam("scope", "openid")
             .build().toURL();
-        String user = "alice";
-        String password = "ecila";
 
         // Login to the OIDC token endpoint + get the authorization code
-        WebClient webClient = setupWebClientIDP(user, password);
-        String authorizationCode = loginAndGetAuthorizationCode(url, webClient);
+        try (WebClient webClient = setupWebClientIDP("alice", "ecila")) {
+            final String authorizationCode = login(authorizationUrl, webClient).getWebResponse().getContentAsString();
 
-        // Now use the code to get an IdToken
-        final String rawToken = getRawToken(authorizationCode, storedClientId, storedClientPassword);
+            // Now use the code to get an IdToken
+            final Map<String, Object> json =
+                getTokenJson(authorizationCode, confidentialClientId, confidentialClientSecret);
 
-        // Check the IdToken
-        String idToken = getIdToken(rawToken);
-        validateIdToken(idToken, storedClientId);
+            // Check the IdToken 
+            final String idToken = getIdToken(json);
+            validateIdToken(idToken, confidentialClientId);
 
-        // 2. Log out using the token hint
-        URL logoutUrl =
-            UriBuilder.fromUri("https://localhost:" + getRpHttpsPort() + "/" + getServletContextName() + "/idp/logout")
+            // 2. Log out using the token hint
+            final URL logoutUrl = oidcEndpointBuilder("/idp/logout")
                 .queryParam("id_token_hint", idToken)
-                .queryParam("post_logout_redirect_uri", "https://localhost:12345")
+                .queryParam("post_logout_redirect_uri", LOGOUT_URL + '/')
                 .build().toURL();
 
-        try {
-            webClient.getPage(logoutUrl);
-            fail("Failure expected using invalid post_logout_redirect_uri");
-        } catch (FailingHttpStatusCodeException e) {
-            assertEquals(Status.BAD_REQUEST.getStatusCode(), e.getStatusCode());
-        } 
+            try {
+                webClient.getPage(logoutUrl);
+                fail("Failure expected using invalid post_logout_redirect_uri");
+            } catch (FailingHttpStatusCodeException e) {
+                assertEquals(Status.BAD_REQUEST.getStatusCode(), e.getStatusCode());
+            }
+        }
     }
 
     // Test that the form has the correct CSRF token in it when creating a client
     @org.junit.Test
     public void testCSRFClientRegistration() throws Exception {
-        final URL url = oidcEndpoint("/console/clients");
-        String user = "alice";
-        String password = "ecila";
-
         // Login to the client page successfully
-        WebClient webClient = setupWebClientIDP(user, password);
-        HtmlPage loginPage = login(url, webClient);
-        final String bodyTextContent = loginPage.getBody().getTextContent();
-        assertTrue(bodyTextContent.contains("Registered Clients"));
+        try (WebClient webClient = setupWebClientIDP("alice", "ecila")) {
+            final URL clientsUrl = oidcEndpoint("/console/clients");
+            login(clientsUrl, webClient);
 
-        // Register a new client
+            // Register a new client
 
-        WebRequest request = new WebRequest(url, HttpMethod.POST);
-        request.setRequestParameters(Arrays.asList(
-            new NameValuePair("client_name", "bad_client"),
-            new NameValuePair("client_type", "confidential"),
-            new NameValuePair("client_redirectURI", "https://127.0.0.1"),
-            new NameValuePair("client_audience", ""),
-            new NameValuePair("client_logoutURI", ""),
-            new NameValuePair("client_homeRealm", ""),
-            new NameValuePair("client_csrfToken", "12345")));
-
-        HtmlPage registeredClientPage = webClient.getPage(request);
-        assertTrue(registeredClientPage.asXml().contains("Invalid CSRF Token"));
-
-        webClient.close();
+            WebRequest request = new WebRequest(clientsUrl, HttpMethod.POST);
+            request.setRequestParameters(Arrays.asList(
+                new NameValuePair("client_name", "bad_client"),
+                new NameValuePair("client_type", "confidential"),
+                new NameValuePair("client_redirectURI", "https://127.0.0.1"),
+                new NameValuePair("client_audience", ""),
+                new NameValuePair("client_logoutURI", ""),
+                new NameValuePair("client_homeRealm", ""),
+                new NameValuePair("client_csrfToken", "12345")));
+
+            HtmlPage registeredClientPage = webClient.getPage(request);
+            assertTrue(registeredClientPage.asXml().contains("Invalid CSRF Token"));
+        }
     }
 
     @org.junit.Test
-    public void testOIDCLoginForClient1WithRoles() throws Exception {
-        final URL url = oidcEndpointBuilder("/idp/authorize")
-            .queryParam("client_id", storedClientId)
+    public void testOIDCLoginForConfidentialClientWithRoles() throws Exception {
+        final URL authorizationUrl = oidcEndpointBuilder("/idp/authorize")
+            .queryParam("client_id", confidentialClientId)
             .queryParam("response_type", "code")
             .queryParam("scope", "openid")
             .queryParam("claims", "roles")
             .build().toURL();
-        String user = "alice";
-        String password = "ecila";
 
         // Login to the OIDC token endpoint + get the authorization code
-        final String authorizationCode;
-        try (WebClient webClient = setupWebClientIDP(user, password)) {
-            authorizationCode = loginAndGetAuthorizationCode(url, webClient);
-        }
+        final String authorizationCode = loginAndGetAuthorizationCode(authorizationUrl, "alice", "ecila");
 
         // Now use the code to get an IdToken
-        final String rawToken = getRawToken(authorizationCode, storedClientId, storedClientPassword);
+        final Map<String, Object> json =
+            getTokenJson(authorizationCode, confidentialClientId, confidentialClientSecret);
 
         // Check the IdToken
-        validateIdToken(getIdToken(rawToken), storedClientId, "User");
+        validateIdToken(getIdToken(json), confidentialClientId, "User");
     }
 
     @org.junit.Test
-    public void testOIDCLoginForClient1WithRolesScope() throws Exception {
-        final URL url = oidcEndpointBuilder("/idp/authorize")
-            .queryParam("client_id", storedClientId)
+    public void testOIDCLoginForConfidentialClientWithRolesScope() throws Exception {
+        final URL authorizationUrl = oidcEndpointBuilder("/idp/authorize")
+            .queryParam("client_id", confidentialClientId)
             .queryParam("response_type", "code")
             .queryParam("scope", "openid roles")
             .build().toURL();
-        String user = "alice";
-        String password = "ecila";
 
         // Login to the OIDC token endpoint + get the authorization code
-        final String authorizationCode;
-        try (WebClient webClient = setupWebClientIDP(user, password)) {
-            authorizationCode = loginAndGetAuthorizationCode(url, webClient);
-        }
+        final String authorizationCode = loginAndGetAuthorizationCode(authorizationUrl, "alice", "ecila");
 
         // Now use the code to get an IdToken
-        final String rawToken = getRawToken(authorizationCode, storedClientId, storedClientPassword);
+        final Map<String, Object> json =
+            getTokenJson(authorizationCode, confidentialClientId, confidentialClientSecret);
 
         // Check the IdToken
-        validateIdToken(getIdToken(rawToken), storedClientId, "User");
+        validateIdToken(getIdToken(json), confidentialClientId, "User");
     }
 
     @org.junit.Test
     public void testAccessTokenRevocation() throws Exception {
-        final URL url = oidcEndpointBuilder("/idp/authorize")
-            .queryParam("client_id", storedClientId)
+        final URL authorizationUrl = oidcEndpointBuilder("/idp/authorize")
+            .queryParam("client_id", confidentialClientId)
             .queryParam("response_type", "code")
             .queryParam("scope", "openid")
             .build().toURL();
-        String user = "alice";
-        String password = "ecila";
 
         // Login to the OIDC token endpoint + get the authorization code
-        final String authorizationCode;
-        try (WebClient webClient = setupWebClientIDP(user, password)) {
-            authorizationCode = loginAndGetAuthorizationCode(url, webClient);
-        }
+        final String authorizationCode = loginAndGetAuthorizationCode(authorizationUrl, "alice", "ecila");
 
         // Now use the code to get an IdToken
-        final String rawToken = getRawToken(authorizationCode, storedClientId, storedClientPassword);
+        final Map<String, Object> json =
+            getTokenJson(authorizationCode, confidentialClientId, confidentialClientSecret);
 
         // Check the IdToken
-        validateIdToken(getIdToken(rawToken), storedClientId);
+        validateIdToken(getIdToken(json), confidentialClientId);
 
         // Get the access token
-        String accessToken = parseToken(rawToken, "access_token");
-        assertNotNull(accessToken);
+        String accessToken = json.get("access_token").toString();
 
         // Introspect the token and check it's valid
         WebRequest introspectionRequest = new WebRequest(oidcEndpoint("/oauth2/introspect"), HttpMethod.POST);
         introspectionRequest.setRequestParameters(Arrays.asList(
             new NameValuePair("token", accessToken)));
 
-        WebClient webClient2 = setupWebClientRP(storedClientId, storedClientPassword);
-        String introspectionResponse = webClient2.getPage(introspectionRequest).getWebResponse().getContentAsString();
-
-        assertTrue(introspectionResponse.contains("\"active\":true"));
+        try (WebClient webClient = setupWebClientRP(confidentialClientId, confidentialClientSecret)) {
+            String introspectionResponse =
+                webClient.getPage(introspectionRequest).getWebResponse().getContentAsString();
 
-        // Now revoke the token
-        WebRequest revocationRequest = new WebRequest(oidcEndpoint("/oauth2/revoke"), HttpMethod.POST);
-        revocationRequest.setRequestParameters(Arrays.asList(
-            new NameValuePair("token", accessToken)));
+            assertTrue(introspectionResponse.contains("\"active\":true"));
 
-        webClient2.getPage(revocationRequest);
+            // Now revoke the token
+            WebRequest revocationRequest = new WebRequest(oidcEndpoint("/oauth2/revoke"), HttpMethod.POST);
+            revocationRequest.setRequestParameters(Arrays.asList(
+                new NameValuePair("token", accessToken)));
 
-        // Now introspect the token again and check it's not valid
-        introspectionResponse = webClient2.getPage(introspectionRequest).getWebResponse().getContentAsString();
+            webClient.getPage(revocationRequest);
 
-        assertTrue(introspectionResponse.contains("\"active\":false"));
+            // Now introspect the token again and check it's not valid
+            introspectionResponse = webClient.getPage(introspectionRequest).getWebResponse().getContentAsString();
 
-        webClient2.close();
+            assertTrue(introspectionResponse.contains("\"active\":false"));
+        }
     }
 
     @org.junit.Test
     public void testAccessTokenRevocationWrongClient() throws Exception {
-        final URL url = oidcEndpointBuilder("/idp/authorize")
-            .queryParam("client_id", storedClientId)
+        final URL authorizationUrl = oidcEndpointBuilder("/idp/authorize")
+            .queryParam("client_id", confidentialClientId)
             .queryParam("response_type", "code")
             .queryParam("scope", "openid")
             .build().toURL();
-        String user = "alice";
-        String password = "ecila";
 
         // Login to the OIDC token endpoint + get the authorization code
-        final String authorizationCode;
-        try (WebClient webClient = setupWebClientIDP(user, password)) {
-            authorizationCode = loginAndGetAuthorizationCode(url, webClient);
-        }
+        final String authorizationCode = loginAndGetAuthorizationCode(authorizationUrl, "alice", "ecila");
 
         // Now use the code to get an IdToken
-        final String rawToken = getRawToken(authorizationCode, storedClientId, storedClientPassword);
+        final Map<String, Object> json =
+            getTokenJson(authorizationCode, confidentialClientId, confidentialClientSecret);
 
         // Check the IdToken
-        validateIdToken(getIdToken(rawToken), storedClientId);
+        validateIdToken(getIdToken(json), confidentialClientId);
 
         // Get the access token
-        String accessToken = parseToken(rawToken, "access_token");
-        assertNotNull(accessToken);
+        String accessToken = json.get("access_token").toString();
 
         // Introspect the token and check it's valid
         WebRequest introspectionRequest = new WebRequest(oidcEndpoint("/oauth2/introspect"), HttpMethod.POST);
         introspectionRequest.setRequestParameters(Arrays.asList(
             new NameValuePair("token", accessToken)));
 
-        WebClient webClient2 = setupWebClientRP(storedClientId, storedClientPassword);
+        WebClient webClient2 = setupWebClientRP(confidentialClientId, confidentialClientSecret);
         String introspectionResponse = webClient2.getPage(introspectionRequest).getWebResponse().getContentAsString();
 
         assertTrue(introspectionResponse.contains("\"active\":true"));
 
-        // Now try to revoke the token as the other client
-        try (WebClient webClient3 = setupWebClientRP(storedClient2Id, storedClient2Password)) {
-            WebRequest revocationRequest = new WebRequest(oidcEndpoint("/oauth2/revoke"), HttpMethod.POST);
-            revocationRequest.setRequestParameters(Arrays.asList(
-                new NameValuePair("token", accessToken)));
+        try (WebClient webClient = setupWebClientIDP("alice", "ecila")) {
+            final URL clientsUrl = oidcEndpoint("/console/clients");
+            login(clientsUrl, webClient);
+
+            HtmlPage registeredClientsPage = registerNewClient(webClient, clientsUrl, "client3",
+                "https://localhost:12345", "https://cxf.apache.org", "https://localhost:12345");
 
-            webClient3.getPage(revocationRequest);
+            final String clientId = getClientIdByName("client3", registeredClientsPage);
+            final String clientSecret = getClientSecret(webClient, clientsUrl, clientId);
+
+            // Now try to revoke the token as the other client
+            try (WebClient webClient3 = setupWebClientRP(clientId, clientSecret)) {
+                WebRequest revocationRequest = new WebRequest(oidcEndpoint("/oauth2/revoke"), HttpMethod.POST);
+                revocationRequest.setRequestParameters(Arrays.asList(
+                    new NameValuePair("token", accessToken)));
+
+                webClient3.getPage(revocationRequest);
+            } finally {
+                deleteClient(webClient, clientsUrl, clientId);
+            }
         }
 
         // Now introspect the token again and check it's still valid
@@ -997,7 +908,7 @@ abstract class AbstractOIDCTest {
     @org.junit.Test
     public void testJWKKeyService2() throws Exception {
         final String response;
-        try (WebClient webClient = setupWebClientRP("", "")) {
+        try (WebClient webClient = setupWebClient()) {
             response = webClient.getPage(oidcEndpoint("/jwk2/keys")).getWebResponse().getContentAsString();
         }
         assertTrue(response.contains("2011-04-29"));
@@ -1029,27 +940,31 @@ abstract class AbstractOIDCTest {
     }
 
     private static WebClient setupWebClientRP(String user, String password) {
-        return setupWebClient(Integer.parseInt(RP_HTTPS_PORT), user, password);
+        return setupWebClient(RP_HTTPS_PORT, user, password);
+    }
+
+    private static WebClient setupWebClient() {
+        return setupWebClient(-1, null, null);
     }
 
     private static WebClient setupWebClient(int port, String user, String password) {
         final WebClient webClient = new WebClient();
         webClient.getOptions().setUseInsecureSSL(true);
-        webClient.getCredentialsProvider().setCredentials(
-            new AuthScope("localhost", port),
-            new UsernamePasswordCredentials(user, password));
-
+        if (-1 != port && null != password) {
+            webClient.getCredentialsProvider().setCredentials(
+                new AuthScope("localhost", port),
+                new UsernamePasswordCredentials(user, password));
+        }
         webClient.getOptions().setJavaScriptEnabled(false);
-
         return webClient;
     }
 
     private static <P extends Page> P login(URL url, WebClient webClient) throws IOException {
-        webClient.getOptions().setJavaScriptEnabled(false);
         final HtmlPage idpPage = webClient.getPage(url);
-        webClient.getOptions().setJavaScriptEnabled(true);
         assertEquals("IDP SignIn Response Form", idpPage.getTitleText());
 
+        webClient.getCredentialsProvider().clear();
+
         // Test the SAML Version here
         DomNodeList<DomElement> results = idpPage.getElementsByTagName("input");
 
@@ -1069,40 +984,32 @@ abstract class AbstractOIDCTest {
         return button.click();
     }
 
-    private static String loginAndGetAuthorizationCode(
-        URL url, WebClient webClient
-    ) throws Exception {
-        final String authorizationCode = login(url, webClient).getWebResponse().getContentAsString();
-        assertNotNull(authorizationCode);
-        return authorizationCode;
+    private static String loginAndGetAuthorizationCode(URL authorizationUrl, String user, String password)
+        throws IOException {
+        try (WebClient webClient = setupWebClientIDP(user, password)) {
+            final String authorizationCode = login(authorizationUrl, webClient).getWebResponse().getContentAsString();
+            assertNotNull(authorizationCode);
+            return authorizationCode;
+        }
     }
 
-    private String getRawToken(String authorizationCode, String user, String password) throws IOException {
-        try (WebClient webClient = setupWebClientRP(user, password)) {
+    private Map<String, Object> getTokenJson(String authorizationCode, String clientId, String clientSecret)
+        throws IOException {
+        try (WebClient webClient = setupWebClientRP(clientId, clientSecret)) {
             WebRequest request = new WebRequest(oidcEndpoint("/oauth2/token"), HttpMethod.POST);
 
             request.setRequestParameters(Arrays.asList(
-                new NameValuePair("client_id", storedClientId),
+                new NameValuePair("client_id", clientId),
                 new NameValuePair("grant_type", "authorization_code"),
                 new NameValuePair("code", authorizationCode)));
-    
-            return webClient.getPage(request).getWebResponse().getContentAsString();
-        }
-    }
 
-    private static String getIdToken(String jsonToken) {
-        return parseToken(jsonToken, "id_token");
+            return new JsonMapObjectReaderWriter().fromJson(
+                webClient.getPage(request).getWebResponse().getContentAsString());
+        }
     }
 
-    private static String parseToken(String jsonToken, String tag) {
-        String foundString =
-            jsonToken.substring(jsonToken.indexOf(tag)
-                                   + (tag + "\":\"").length());
-        int quoteIndex = foundString.indexOf('"');
-        if (quoteIndex < 1) {
-            quoteIndex = foundString.length();
-        }
-        return foundString.substring(0, quoteIndex);
+    private static String getIdToken(Map<String, Object> json) {
+        return json.get("id_token").toString();
     }
 
     private void validateIdToken(String idToken, String audience) throws IOException {
@@ -1110,7 +1017,6 @@ abstract class AbstractOIDCTest {
     }
 
     private void validateIdToken(String idToken, String audience, String role) throws IOException {
-        assertNotNull(idToken);
         JwsJwtCompactConsumer jwtConsumer = new JwsJwtCompactConsumer(idToken);
         JwtToken jwt = jwtConsumer.getJwtToken();