From 016e793c435eedc4e841d0710f24d8f6e77dffa7 Mon Sep 17 00:00:00 2001
From: Prarthona Paul <prpaul@redhat.com>
Date: Thu, 24 Aug 2023 09:12:53 -0400
Subject: [PATCH] [ELY-2584] Add the ability to specify that the OIDC
 Authentication Request should include request and request_uri parameters

---
 http/oidc/pom.xml                             |  16 ++
 .../security/http/oidc/ElytronMessages.java   |  42 ++-
 .../http/oidc/JWKEncPublicKeyLocator.java     | 113 ++++++++
 .../oidc/JWTClientCredentialsProvider.java    |  46 +---
 .../security/http/oidc/JWTSigningUtils.java   |  78 ++++++
 .../org/wildfly/security/http/oidc/Oidc.java  |  24 ++
 .../http/oidc/OidcClientConfiguration.java    | 135 +++++++++-
 .../oidc/OidcClientConfigurationBuilder.java  |  41 ++-
 .../security/http/oidc/OidcClientContext.java | 101 ++++++++
 .../http/oidc/OidcJsonConfiguration.java      | 100 ++++++-
 .../http/oidc/OidcProviderMetadata.java       |  33 +++
 .../http/oidc/OidcRequestAuthenticator.java   | 177 ++++++++++++-
 .../security/http/oidc/ServerRequest.java     |  35 ++-
 .../http/oidc/KeycloakConfiguration.java      |  85 ++++--
 .../security/http/oidc/OidcBaseTest.java      |   9 +-
 .../wildfly/security/http/oidc/OidcTest.java  | 245 +++++++++++++++++-
 pom.xml                                       |   7 +
 17 files changed, 1199 insertions(+), 88 deletions(-)
 create mode 100644 http/oidc/src/main/java/org/wildfly/security/http/oidc/JWKEncPublicKeyLocator.java
 create mode 100644 http/oidc/src/main/java/org/wildfly/security/http/oidc/JWTSigningUtils.java

diff --git a/http/oidc/pom.xml b/http/oidc/pom.xml
index 64a7f7285d2..5f3a6504fda 100644
--- a/http/oidc/pom.xml
+++ b/http/oidc/pom.xml
@@ -128,6 +128,11 @@
             <artifactId>keycloak-admin-client</artifactId>
             <scope>test</scope>
         </dependency>
+        <dependency>
+            <groupId>org.keycloak</groupId>
+            <artifactId>keycloak-services</artifactId>
+            <scope>test</scope>
+        </dependency>
         <dependency>
             <groupId>org.jboss.logmanager</groupId>
             <artifactId>jboss-logmanager</artifactId>
@@ -173,6 +178,17 @@
             <artifactId>jmockit</artifactId>
             <scope>test</scope>
         </dependency>
+        <dependency>
+            <groupId>org.wildfly.security</groupId>
+            <artifactId>wildfly-elytron-credential-source-impl</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.wildfly.security</groupId>
+            <artifactId>wildfly-elytron-tests-common</artifactId>
+            <type>test-jar</type>
+            <scope>test</scope>
+        </dependency>
 
     </dependencies>
 
diff --git a/http/oidc/src/main/java/org/wildfly/security/http/oidc/ElytronMessages.java b/http/oidc/src/main/java/org/wildfly/security/http/oidc/ElytronMessages.java
index ac5e2861fc3..e836cc3b468 100644
--- a/http/oidc/src/main/java/org/wildfly/security/http/oidc/ElytronMessages.java
+++ b/http/oidc/src/main/java/org/wildfly/security/http/oidc/ElytronMessages.java
@@ -18,10 +18,10 @@
 
 package org.wildfly.security.http.oidc;
 
+import static org.jboss.logging.annotations.Message.NONE;
 import static org.jboss.logging.Logger.Level.DEBUG;
 import static org.jboss.logging.Logger.Level.ERROR;
 import static org.jboss.logging.Logger.Level.WARN;
-import static org.jboss.logging.annotations.Message.NONE;
 
 import java.io.IOException;
 
@@ -238,5 +238,45 @@ interface ElytronMessages extends BasicLogger {
     @Message(id = 23057, value = "principal-attribute '%s' claim does not exist, falling back to 'sub'")
     void principalAttributeClaimDoesNotExist(String principalAttributeClaim);
 
+    @Message(id = 23058, value = "Invalid keystore configuration for signing Request Objects.")
+    IOException invalidKeyStoreConfiguration();
+
+    @Message(id = 23059, value = "The signature algorithm specified is not supported by the OpenID Provider.")
+    IOException invalidRequestObjectSignatureAlgorithm();
+
+    @Message(id = 23060, value = "The encryption algorithm specified is not supported by the OpenID Provider.")
+    IOException invalidRequestObjectEncryptionAlgorithm();
+
+    @Message(id = 23061, value = "The content encryption algorithm (enc value) specified is not supported by the OpenID Provider.")
+    IOException invalidRequestObjectEncryptionEncValue();
+
+    @LogMessage(level = WARN)
+    @Message(id = 23062, value = "The OpenID provider does not support request parameters. Sending the request using OAuth2 format.")
+    void requestParameterNotSupported();
+
+    @Message(id = 23063, value = "Both request object encryption algorithm and request object content encryption algorithm must be configured to encrypt the request object.")
+    IllegalArgumentException invalidRequestObjectEncryptionAlgorithmConfiguration();
+
+    @Message(id = 23064, value = "Failed to create the authentication request using the request parameter.")
+    RuntimeException unableToCreateRequestWithRequestParameter(@Cause Exception cause);
+
+    @Message(id = 23065, value = "Failed to create the authentication request using the request_uri parameter.")
+    RuntimeException unableToCreateRequestUriWithRequestParameter(@Cause Exception cause);
+
+    @Message (id = 23066, value = "Failed to send a request to the OpenID provider's Pushed Authorization Request endpoint.")
+    RuntimeException failedToSendPushedAuthorizationRequest(@Cause Exception cause);
+
+    @Message(id = 23067, value = "Cannot retrieve the request_uri as the pushed authorization request endpoint is not available for the OpenID provider.")
+    RuntimeException pushedAuthorizationRequestEndpointNotAvailable();
+
+    @LogMessage(level = WARN)
+    @Message(id = 23068, value = "The request object will be unsigned. This should not be used in a production environment. To sign the request object, for use in a production environment, please specify the request object signing algorithm.")
+    void unsignedRequestObjectIsUsed();
+
+    @Message(id = 23069, value = "The client secret has not been configured. Unable to sign the request object using the client secret.")
+    RuntimeException clientSecretNotConfigured();
+
+    @Message(id = 23070, value = "Authentication request format must be one of the following: oauth2, request, request_uri.")
+    RuntimeException invalidAuthenticationRequestFormat();
 }
 
diff --git a/http/oidc/src/main/java/org/wildfly/security/http/oidc/JWKEncPublicKeyLocator.java b/http/oidc/src/main/java/org/wildfly/security/http/oidc/JWKEncPublicKeyLocator.java
new file mode 100644
index 00000000000..819e5950671
--- /dev/null
+++ b/http/oidc/src/main/java/org/wildfly/security/http/oidc/JWKEncPublicKeyLocator.java
@@ -0,0 +1,113 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2024 Red Hat, Inc., and individual contributors
+ * as indicated by the @author tags.
+ *
+ * Licensed 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 org.wildfly.security.http.oidc;
+
+import static org.apache.http.HttpHeaders.ACCEPT;
+import static org.wildfly.security.http.oidc.ElytronMessages.log;
+import static org.wildfly.security.http.oidc.Oidc.JSON_CONTENT_TYPE;
+
+import java.security.PublicKey;
+import java.util.ArrayList;
+import java.util.Map;
+import java.util.List;
+
+import org.apache.http.client.methods.HttpGet;
+import org.wildfly.security.jose.jwk.JWK;
+import org.wildfly.security.jose.jwk.JsonWebKeySet;
+import org.wildfly.security.jose.jwk.JsonWebKeySetUtil;
+
+/**
+ * A public key locator that dynamically obtains the public key used for encryption
+ * from an OpenID provider by sending a request to the provider's {@code jwks_uri}
+ * when needed.
+ *
+ * @author <a href="mailto:prpaul@redhat.com">Prarthona Paul</a>
+ * */
+class JWKEncPublicKeyLocator implements PublicKeyLocator {
+    private List<PublicKey> currentKeys = new ArrayList<>();
+
+    private volatile int lastRequestTime = 0;
+
+    @Override
+    public PublicKey getPublicKey(String kid, OidcClientConfiguration config) {
+        int minTimeBetweenRequests = config.getMinTimeBetweenJwksRequests();
+        int publicKeyCacheTtl = config.getPublicKeyCacheTtl();
+        int currentTime = getCurrentTime();
+
+        PublicKey publicKey = lookupCachedKey(publicKeyCacheTtl, currentTime);
+        if (publicKey != null) {
+            return publicKey;
+        }
+
+        synchronized (this) {
+            currentTime = getCurrentTime();
+            if (currentTime > lastRequestTime + minTimeBetweenRequests) {
+                sendRequest(config);
+                lastRequestTime = currentTime;
+            } else {
+                log.debug("Won't send request to jwks url. Last request time was " + lastRequestTime);
+            }
+            return lookupCachedKey(publicKeyCacheTtl, currentTime);
+        }
+
+    }
+
+    @Override
+    public void reset(OidcClientConfiguration config) {
+        synchronized (this) {
+            sendRequest(config);
+            lastRequestTime = getCurrentTime();
+        }
+    }
+
+    private PublicKey lookupCachedKey(int publicKeyCacheTtl, int currentTime) {
+        if (lastRequestTime + publicKeyCacheTtl > currentTime) {
+            return currentKeys.get(0); // returns the first cached public key
+        } else {
+            return null;
+        }
+    }
+
+    private static int getCurrentTime() {
+        return (int) (System.currentTimeMillis() / 1000);
+    }
+
+    private void sendRequest(OidcClientConfiguration config) {
+        if (log.isTraceEnabled()) {
+            log.trace("Going to send request to retrieve new set of public keys to encrypt a JWT request for client " + config.getResourceName());
+        }
+
+        HttpGet request = new HttpGet(config.getJwksUrl());
+        request.addHeader(ACCEPT, JSON_CONTENT_TYPE);
+        try {
+            JsonWebKeySet jwks = Oidc.sendJsonHttpRequest(config, request, JsonWebKeySet.class);
+            Map<String, PublicKey> publicKeys = JsonWebKeySetUtil.getKeysForUse(jwks, JWK.Use.ENC);
+
+            if (log.isDebugEnabled()) {
+                log.debug("Public keys successfully retrieved for client " +  config.getResourceName() + ". New kids: " + publicKeys.keySet());
+            }
+
+            // update current keys
+            currentKeys.clear();
+            currentKeys.addAll(publicKeys.values());
+        } catch (OidcException e) {
+            log.error("Error when sending request to retrieve public keys", e);
+        }
+    }
+}
diff --git a/http/oidc/src/main/java/org/wildfly/security/http/oidc/JWTClientCredentialsProvider.java b/http/oidc/src/main/java/org/wildfly/security/http/oidc/JWTClientCredentialsProvider.java
index 4da8d3a5384..13df213373b 100644
--- a/http/oidc/src/main/java/org/wildfly/security/http/oidc/JWTClientCredentialsProvider.java
+++ b/http/oidc/src/main/java/org/wildfly/security/http/oidc/JWTClientCredentialsProvider.java
@@ -19,18 +19,13 @@
 package org.wildfly.security.http.oidc;
 
 import static org.wildfly.security.http.oidc.ElytronMessages.log;
+import static org.wildfly.security.http.oidc.JWTSigningUtils.loadKeyPairFromKeyStore;
 import static org.wildfly.security.http.oidc.Oidc.CLIENT_ASSERTION;
 import static org.wildfly.security.http.oidc.Oidc.CLIENT_ASSERTION_TYPE;
 import static org.wildfly.security.http.oidc.Oidc.CLIENT_ASSERTION_TYPE_JWT;
-import static org.wildfly.security.http.oidc.Oidc.PROTOCOL_CLASSPATH;
 import static org.wildfly.security.http.oidc.Oidc.asInt;
 
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-import java.io.InputStream;
 import java.security.KeyPair;
-import java.security.KeyStore;
-import java.security.PrivateKey;
 import java.security.PublicKey;
 import java.security.interfaces.RSAPublicKey;
 import java.util.Map;
@@ -155,43 +150,4 @@ protected JwtClaims createRequestToken(String clientId, String tokenUrl) {
         jwtClaims.setExpirationTime(exp);
         return jwtClaims;
     }
-
-    private static KeyPair loadKeyPairFromKeyStore(String keyStoreFile, String storePassword, String keyPassword, String keyAlias, String keyStoreType) {
-        InputStream stream = findFile(keyStoreFile);
-        try {
-            KeyStore keyStore = KeyStore.getInstance(keyStoreType);
-            keyStore.load(stream, storePassword.toCharArray());
-            PrivateKey privateKey = (PrivateKey) keyStore.getKey(keyAlias, keyPassword.toCharArray());
-            if (privateKey == null) {
-                log.unableToLoadKeyWithAlias(keyAlias);
-            }
-            PublicKey publicKey = keyStore.getCertificate(keyAlias).getPublicKey();
-            return new KeyPair(publicKey, privateKey);
-        } catch (Exception e) {
-            throw log.unableToLoadPrivateKey(e);
-        }
-    }
-
-    private static InputStream findFile(String keystoreFile) {
-        if (keystoreFile.startsWith(PROTOCOL_CLASSPATH)) {
-            String classPathLocation = keystoreFile.replace(PROTOCOL_CLASSPATH, "");
-            // try current class classloader first
-            InputStream is = JWTClientCredentialsProvider.class.getClassLoader().getResourceAsStream(classPathLocation);
-            if (is == null) {
-                is = Thread.currentThread().getContextClassLoader().getResourceAsStream(classPathLocation);
-            }
-            if (is != null) {
-                return is;
-            } else {
-                throw log.unableToFindKeystoreFile(keystoreFile);
-            }
-        } else {
-            try {
-                // fallback to file
-                return new FileInputStream(keystoreFile);
-            } catch (FileNotFoundException e) {
-                throw new RuntimeException(e);
-            }
-        }
-    }
 }
diff --git a/http/oidc/src/main/java/org/wildfly/security/http/oidc/JWTSigningUtils.java b/http/oidc/src/main/java/org/wildfly/security/http/oidc/JWTSigningUtils.java
new file mode 100644
index 00000000000..6aa6d6c135f
--- /dev/null
+++ b/http/oidc/src/main/java/org/wildfly/security/http/oidc/JWTSigningUtils.java
@@ -0,0 +1,78 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2024 Red Hat, Inc., and individual contributors
+ * as indicated by the @author tags.
+ *
+ * Licensed 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 org.wildfly.security.http.oidc;
+
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.InputStream;
+import java.security.KeyPair;
+import java.security.KeyStore;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+
+import static org.wildfly.security.http.oidc.ElytronMessages.log;
+import static org.wildfly.security.http.oidc.Oidc.PROTOCOL_CLASSPATH;
+
+/**
+ * A utility class to obtain the KeyPair from a keystore file.
+ *
+ * @author <a href="mailto:prpaul@redhat.com">Prarthona Paul</a>
+ */
+
+class JWTSigningUtils {
+
+    public static KeyPair loadKeyPairFromKeyStore(String keyStoreFile, String storePassword, String keyPassword, String keyAlias, String keyStoreType) {
+        InputStream stream = findFile(keyStoreFile);
+        try {
+            KeyStore keyStore = KeyStore.getInstance(keyStoreType);
+            keyStore.load(stream, storePassword.toCharArray());
+            PrivateKey privateKey = (PrivateKey) keyStore.getKey(keyAlias, keyPassword.toCharArray());
+            if (privateKey == null) {
+                throw log.unableToLoadKeyWithAlias(keyAlias);
+            }
+            PublicKey publicKey = keyStore.getCertificate(keyAlias).getPublicKey();
+            return new KeyPair(publicKey, privateKey);
+        } catch (Exception e) {
+            throw log.unableToLoadPrivateKey(e);
+        }
+    }
+
+    public static InputStream findFile(String keystoreFile) {
+        if (keystoreFile.startsWith(PROTOCOL_CLASSPATH)) {
+            String classPathLocation = keystoreFile.replace(PROTOCOL_CLASSPATH, "");
+            // try current class classloader first
+            InputStream is = Thread.currentThread().getContextClassLoader().getResourceAsStream(classPathLocation);
+            if (is == null) {
+                is = JWTSigningUtils.class.getClassLoader().getResourceAsStream(classPathLocation);
+            }
+            if (is != null) {
+                return is;
+            } else {
+                throw log.unableToFindKeystoreFile(keystoreFile);
+            }
+        } else {
+            try {
+                // fallback to file
+                return new FileInputStream(keystoreFile);
+            } catch (FileNotFoundException e) {
+                throw new RuntimeException(e);
+            }
+        }
+    }
+}
diff --git a/http/oidc/src/main/java/org/wildfly/security/http/oidc/Oidc.java b/http/oidc/src/main/java/org/wildfly/security/http/oidc/Oidc.java
index f42313b7f58..575809f2f4b 100644
--- a/http/oidc/src/main/java/org/wildfly/security/http/oidc/Oidc.java
+++ b/http/oidc/src/main/java/org/wildfly/security/http/oidc/Oidc.java
@@ -45,6 +45,7 @@
 public class Oidc {
 
     public static final String ACCEPT = "Accept";
+    public static final String AUTHENTICATION_REQUEST_FORMAT = "authentication-request-format";
     public static final String OIDC_NAME = "OIDC";
     public static final String JSON_CONTENT_TYPE = "application/json";
     public static final String HTML_CONTENT_TYPE = "text/html";
@@ -74,6 +75,8 @@ public class Oidc {
     public static final String PARTIAL = "partial/";
     public static final String PASSWORD = "password";
     public static final String PROMPT = "prompt";
+    public static final String REQUEST = "request";
+    public static final String REQUEST_URI = "request_uri";
     public static final String SCOPE = "scope";
     public static final String UI_LOCALES = "ui_locales";
     public static final String USERNAME = "username";
@@ -201,6 +204,27 @@ public enum TokenStore {
         COOKIE
     }
 
+    public enum AuthenticationRequestFormat {
+        OAUTH2("oauth2"),
+        REQUEST("request"),
+        REQUEST_URI("request_uri");
+
+        private final String value;
+
+        AuthenticationRequestFormat(String value) {
+            this.value = value;
+        }
+
+        /**
+         * Get the string value for this authentication format.
+         *
+         * @return the string value for this authentication format
+         */
+        public String getValue() {
+            return value;
+        }
+    }
+
     public enum ClientCredentialsProviderType {
         SECRET("secret"),
         JWT("jwt"),
diff --git a/http/oidc/src/main/java/org/wildfly/security/http/oidc/OidcClientConfiguration.java b/http/oidc/src/main/java/org/wildfly/security/http/oidc/OidcClientConfiguration.java
index 3e18fb4eb6d..ca56da28633 100644
--- a/http/oidc/src/main/java/org/wildfly/security/http/oidc/OidcClientConfiguration.java
+++ b/http/oidc/src/main/java/org/wildfly/security/http/oidc/OidcClientConfiguration.java
@@ -30,9 +30,11 @@
 import static org.wildfly.security.http.oidc.Oidc.SLASH;
 import static org.wildfly.security.http.oidc.Oidc.SSLRequired;
 import static org.wildfly.security.http.oidc.Oidc.TokenStore;
+import static org.wildfly.security.jose.util.JsonSerialization.readValue;
 
 import java.net.URI;
 import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
 import java.util.concurrent.Callable;
 
@@ -41,7 +43,6 @@
 import org.apache.http.client.HttpClient;
 import org.apache.http.client.methods.HttpGet;
 import org.apache.http.util.EntityUtils;
-import org.wildfly.security.jose.util.JsonSerialization;
 
 /**
  * The OpenID Connect (OIDC) configuration for a client application. This class is based on
@@ -81,6 +82,11 @@ public enum RelativeUrlsUsed {
     protected String jwksUrl;
     protected String issuerUrl;
     protected String principalAttribute = "sub";
+    protected List<String> requestObjectSigningAlgValuesSupported;
+    protected List<String> requestObjectEncryptionEncValuesSupported;
+    protected List<String> requestObjectEncryptionAlgValuesSupported;
+    protected boolean requestParameterSupported;
+    protected boolean requestUriParameterSupported;
 
     protected String resource;
     protected String clientId;
@@ -126,6 +132,17 @@ public enum RelativeUrlsUsed {
     protected boolean verifyTokenAudience = false;
 
     protected String tokenSignatureAlgorithm = DEFAULT_TOKEN_SIGNATURE_ALGORITHM;
+    protected String authenticationRequestFormat;
+    protected String requestObjectSigningAlgorithm;
+    protected String requestObjectEncryptionAlgValue;
+    protected String requestObjectEncryptionEncValue;
+    protected String pushedAuthorizationRequestEndpoint;
+    protected String requestObjectSigningKeyStoreFile;
+    protected String requestObjectSigningKeyStorePassword;
+    protected String requestObjectSigningKeyPassword;
+    protected String requestObjectSigningKeyAlias;
+    protected String requestObjectSigningKeyStoreType;
+    protected JWKEncPublicKeyLocator encryptionPublicKeyLocator;
 
     public OidcClientConfiguration() {
     }
@@ -223,6 +240,13 @@ protected void resolveUrls() {
                     tokenUrl = config.getTokenEndpoint();
                     logoutUrl = config.getLogoutEndpoint();
                     jwksUrl = config.getJwksUri();
+                    requestParameterSupported = config.getRequestParameterSupported();
+                    requestObjectSigningAlgValuesSupported = config.getRequestObjectSigningAlgValuesSupported();
+                    requestObjectEncryptionEncValuesSupported = config.getRequestObjectEncryptionEncValuesSupported();
+                    requestObjectEncryptionAlgValuesSupported = config.getRequestObjectEncryptionAlgValuesSupported();
+                    requestUriParameterSupported = config.getRequestUriParameterSupported();
+                    pushedAuthorizationRequestEndpoint = config.getPushedAuthorizationRequestEndpoint();
+
                     if (authServerBaseUrl != null) {
                         // keycloak-specific properties
                         accountUrl = getUrl(issuerUrl, ACCOUNT_PATH);
@@ -246,7 +270,7 @@ protected OidcProviderMetadata getOidcProviderMetadata(String discoveryUrl) thro
                 EntityUtils.consumeQuietly(response.getEntity());
                 throw new Exception(response.getStatusLine().getReasonPhrase());
             }
-            return JsonSerialization.readValue(response.getEntity().getContent(), OidcProviderMetadata.class);
+            return readValue(response.getEntity().getContent(), OidcProviderMetadata.class);
         } finally {
             request.releaseConnection();
         }
@@ -329,6 +353,26 @@ public String getIssuerUrl() {
         return issuerUrl;
     }
 
+    public List<String> getRequestObjectSigningAlgValuesSupported() {
+        return requestObjectSigningAlgValuesSupported;
+    }
+
+    public List<String> getRequestObjectEncryptionAlgValuesSupported() {
+        return requestObjectEncryptionAlgValuesSupported;
+    }
+
+    public List<String> getRequestObjectEncryptionEncValuesSupported() {
+        return requestObjectEncryptionEncValuesSupported;
+    }
+
+    public boolean getRequestParameterSupported() {
+        return requestParameterSupported;
+    }
+
+    public boolean getRequestUriParameterSupported() {
+        return requestUriParameterSupported;
+    }
+
     public void setResource(String resource) {
         this.resource = resource;
     }
@@ -648,4 +692,91 @@ public String getTokenSignatureAlgorithm() {
         return tokenSignatureAlgorithm;
     }
 
+    public String getAuthenticationRequestFormat() {
+        return authenticationRequestFormat;
+    }
+
+    public void setAuthenticationRequestFormat(String authenticationRequestFormat) {
+        this.authenticationRequestFormat = authenticationRequestFormat;
+    }
+
+    public String getRequestObjectSigningAlgorithm() {
+        return requestObjectSigningAlgorithm;
+    }
+
+    public void setRequestObjectSigningAlgorithm(String requestObjectSigningAlgorithm) {
+        this.requestObjectSigningAlgorithm = requestObjectSigningAlgorithm;
+    }
+
+    public String getRequestObjectEncryptionAlgValue() {
+        return requestObjectEncryptionAlgValue;
+    }
+
+    public void setRequestObjectEncryptionAlgValue(String requestObjectEncryptionAlgValue) {
+        this.requestObjectEncryptionAlgValue = requestObjectEncryptionAlgValue;
+    }
+
+    public String getRequestObjectEncryptionEncValue() {
+        return requestObjectEncryptionEncValue;
+    }
+
+    public void setRequestObjectEncryptionEncValue(String requestObjectEncryptionEncValue) {
+        this.requestObjectEncryptionEncValue = requestObjectEncryptionEncValue;
+    }
+
+    public String getRequestObjectSigningKeyStoreFile() {
+        return requestObjectSigningKeyStoreFile;
+    }
+
+    public void setRequestObjectSigningKeyStoreFile(String keyStoreFile) {
+        this.requestObjectSigningKeyStoreFile = keyStoreFile;
+    }
+
+    public String getRequestObjectSigningKeyStorePassword() {
+        return requestObjectSigningKeyStorePassword;
+    }
+
+    public void setRequestObjectSigningKeyStorePassword(String requestObjectSigningKeyStorePassword) {
+        this.requestObjectSigningKeyStorePassword = requestObjectSigningKeyStorePassword;
+    }
+
+    public String getRequestObjectSigningKeyPassword() {
+        return requestObjectSigningKeyPassword;
+    }
+
+    public void setRequestObjectSigningKeyPassword(String requestObjectSigningKeyPassword) {
+        this.requestObjectSigningKeyPassword = requestObjectSigningKeyPassword;
+    }
+
+    public String getRequestObjectSigningKeyStoreType() {
+        return requestObjectSigningKeyStoreType;
+    }
+
+    public void setRequestObjectSigningKeyStoreType(String requestObjectSigningKeyStoreType) {
+        this.requestObjectSigningKeyStoreType = requestObjectSigningKeyStoreType;
+    }
+
+    public String getRequestObjectSigningKeyAlias() {
+        return requestObjectSigningKeyAlias;
+    }
+
+    public void setRequestObjectSigningKeyAlias(String requestObjectSigningKeyAlias) {
+        this.requestObjectSigningKeyAlias = requestObjectSigningKeyAlias;
+    }
+
+    public String getPushedAuthorizationRequestEndpoint() {
+        return pushedAuthorizationRequestEndpoint;
+    }
+
+    public void setPushedAuthorizationRequestEndpoint(String pushedAuthorizationRequestEndpoint) {
+        this.pushedAuthorizationRequestEndpoint = pushedAuthorizationRequestEndpoint;
+    }
+
+    public void setEncryptionPublicKeyLocator(JWKEncPublicKeyLocator publicKeySetExtractor) {
+        this.encryptionPublicKeyLocator = publicKeySetExtractor;
+    }
+
+    public JWKEncPublicKeyLocator getEncryptionPublicKeyLocator() {
+        return this.encryptionPublicKeyLocator;
+    }
 }
diff --git a/http/oidc/src/main/java/org/wildfly/security/http/oidc/OidcClientConfigurationBuilder.java b/http/oidc/src/main/java/org/wildfly/security/http/oidc/OidcClientConfigurationBuilder.java
index f2d757e493c..43bebace9f6 100644
--- a/http/oidc/src/main/java/org/wildfly/security/http/oidc/OidcClientConfigurationBuilder.java
+++ b/http/oidc/src/main/java/org/wildfly/security/http/oidc/OidcClientConfigurationBuilder.java
@@ -18,7 +18,11 @@
 
 package org.wildfly.security.http.oidc;
 
+import static org.jose4j.jws.AlgorithmIdentifiers.NONE;
 import static org.wildfly.security.http.oidc.ElytronMessages.log;
+import static org.wildfly.security.http.oidc.Oidc.AuthenticationRequestFormat.OAUTH2;
+import static org.wildfly.security.http.oidc.Oidc.AuthenticationRequestFormat.REQUEST;
+import static org.wildfly.security.http.oidc.Oidc.AuthenticationRequestFormat.REQUEST_URI;
 import static org.wildfly.security.http.oidc.Oidc.SSLRequired;
 import static org.wildfly.security.http.oidc.Oidc.TokenStore;
 
@@ -103,6 +107,41 @@ protected OidcClientConfiguration internalBuild(final OidcJsonConfiguration oidc
         if (oidcJsonConfiguration.getScope() != null) {
             oidcClientConfiguration.setScope(oidcJsonConfiguration.getScope());
         }
+        if (oidcJsonConfiguration.getAuthenticationRequestFormat() != null) {
+            if (!(oidcJsonConfiguration.getAuthenticationRequestFormat().equals(OAUTH2.getValue()) ||
+            oidcJsonConfiguration.getAuthenticationRequestFormat().equals(REQUEST.getValue()) ||
+            oidcJsonConfiguration.getAuthenticationRequestFormat().equals(REQUEST_URI.getValue()))) {
+                throw log.invalidAuthenticationRequestFormat();
+            }
+            oidcClientConfiguration.setAuthenticationRequestFormat(oidcJsonConfiguration.getAuthenticationRequestFormat());
+        } else {
+            oidcClientConfiguration.setAuthenticationRequestFormat(OAUTH2.getValue());
+        }
+        if (oidcJsonConfiguration.getRequestObjectSigningAlgorithm() != null) {
+            oidcClientConfiguration.setRequestObjectSigningAlgorithm(oidcJsonConfiguration.getRequestObjectSigningAlgorithm());
+        } else {
+            oidcClientConfiguration.setRequestObjectSigningAlgorithm(NONE);
+        }
+        if (oidcJsonConfiguration.getRequestObjectEncryptionAlgValue() != null && oidcJsonConfiguration.getRequestObjectEncryptionEncValue() != null) { //both are required to encrypt the request object
+            oidcClientConfiguration.setRequestObjectEncryptionAlgValue(oidcJsonConfiguration.getRequestObjectEncryptionAlgValue());
+            oidcClientConfiguration.setRequestObjectEncryptionEncValue(oidcJsonConfiguration.getRequestObjectEncryptionEncValue());
+            JWKEncPublicKeyLocator encryptionPublicKeyLocator = new JWKEncPublicKeyLocator();
+            oidcClientConfiguration.setEncryptionPublicKeyLocator(encryptionPublicKeyLocator);
+        } else if (oidcJsonConfiguration.getRequestObjectEncryptionAlgValue() != null || oidcJsonConfiguration.getRequestObjectEncryptionEncValue() != null) {   //if only one is specified, that is not correct
+            throw log.invalidRequestObjectEncryptionAlgorithmConfiguration();
+        }
+        if (oidcJsonConfiguration.getRequestObjectSigningKeyStoreFile() != null
+                && oidcJsonConfiguration.getRequestObjectSigningKeyStorePassword() != null
+                && oidcJsonConfiguration.getRequestObjectSigningKeyPassword() != null
+                && oidcJsonConfiguration.getRequestObjectSigningKeyAlias() != null) {
+            oidcClientConfiguration.setRequestObjectSigningKeyStoreFile(oidcJsonConfiguration.getRequestObjectSigningKeyStoreFile());
+            oidcClientConfiguration.setRequestObjectSigningKeyStorePassword(oidcJsonConfiguration.getRequestObjectSigningKeyStorePassword());
+            oidcClientConfiguration.setRequestObjectSigningKeyPassword(oidcJsonConfiguration.getRequestObjectSigningKeyPassword());
+            oidcClientConfiguration.setRequestObjectSigningKeyAlias(oidcJsonConfiguration.getRequestObjectSigningKeyAlias());
+            if (oidcJsonConfiguration.getRequestObjectSigningKeyStoreType() != null) {
+                oidcClientConfiguration.setRequestObjectSigningKeyStoreType(oidcJsonConfiguration.getRequestObjectSigningKeyStoreType());
+            }
+        }
         if (oidcJsonConfiguration.getPrincipalAttribute() != null) oidcClientConfiguration.setPrincipalAttribute(oidcJsonConfiguration.getPrincipalAttribute());
 
         oidcClientConfiguration.setResourceCredentials(oidcJsonConfiguration.getCredentials());
@@ -193,8 +232,8 @@ public static OidcJsonConfiguration loadOidcJsonConfiguration(InputStream is) {
         return adapterConfig;
     }
 
-
     public static OidcClientConfiguration build(OidcJsonConfiguration oidcJsonConfiguration) {
         return new OidcClientConfigurationBuilder().internalBuild(oidcJsonConfiguration);
     }
+
 }
diff --git a/http/oidc/src/main/java/org/wildfly/security/http/oidc/OidcClientContext.java b/http/oidc/src/main/java/org/wildfly/security/http/oidc/OidcClientContext.java
index 3c249bb846b..f5d930bd525 100644
--- a/http/oidc/src/main/java/org/wildfly/security/http/oidc/OidcClientContext.java
+++ b/http/oidc/src/main/java/org/wildfly/security/http/oidc/OidcClientContext.java
@@ -525,6 +525,107 @@ public String getTokenSignatureAlgorithm() {
         public void setTokenSignatureAlgorithm(String tokenSignatureAlgorithm) {
             delegate.setTokenSignatureAlgorithm(tokenSignatureAlgorithm);
         }
+
+        @Override
+        public String getAuthenticationRequestFormat() {
+            return delegate.getAuthenticationRequestFormat();
+        }
+
+        @Override
+        public void setAuthenticationRequestFormat(String authFormat) {
+            delegate.setAuthenticationRequestFormat(authFormat);
+        }
+
+        @Override
+        public String getRequestObjectSigningAlgorithm() {
+            return delegate.getRequestObjectSigningAlgorithm();
+        }
+
+        @Override
+        public void setRequestObjectSigningAlgorithm(String requestSignature) {
+            delegate.setRequestObjectSigningAlgorithm(requestSignature);
+        }
+
+        @Override
+        public String getRequestObjectEncryptionAlgValue() {
+            return delegate.getRequestObjectEncryptionAlgValue();
+        }
+
+        @Override
+        public void setRequestObjectEncryptionAlgValue(String requestObjectEncryptionAlgValue) {
+            delegate.setRequestObjectEncryptionAlgValue(requestObjectEncryptionAlgValue);
+        }
+
+        @Override
+        public String getRequestObjectEncryptionEncValue() {
+            return delegate.requestObjectEncryptionEncValue;
+        }
+
+        @Override
+        public void setRequestObjectEncryptionEncValue (String requestObjectEncryptionEncValue) {
+            delegate.requestObjectEncryptionEncValue = requestObjectEncryptionEncValue;
+        }
+
+        @Override
+        public String getRequestObjectSigningKeyStoreFile() {
+            return delegate.requestObjectSigningKeyStoreFile;
+        }
+
+        @Override
+        public void setRequestObjectSigningKeyStoreFile(String keyStoreFile) {
+            delegate.requestObjectSigningKeyStoreFile = keyStoreFile;
+        }
+
+        @Override
+        public String getRequestObjectSigningKeyStorePassword() {
+            return delegate.requestObjectSigningKeyStorePassword;
+        }
+
+        @Override
+        public void setRequestObjectSigningKeyStorePassword(String requestObjectSigningKeyStorePassword) {
+            delegate.requestObjectSigningKeyStorePassword = requestObjectSigningKeyStorePassword;
+        }
+
+        @Override
+        public String getRequestObjectSigningKeyPassword() {
+            return delegate.requestObjectSigningKeyPassword;
+        }
+
+        @Override
+        public void setRequestObjectSigningKeyPassword(String requestObjectSigningKeyPassword) {
+            delegate.requestObjectSigningKeyPassword = requestObjectSigningKeyPassword;
+        }
+
+        @Override
+        public String getRequestObjectSigningKeyStoreType() {
+            return delegate.requestObjectSigningKeyStoreType;
+        }
+
+        @Override
+        public void setRequestObjectSigningKeyStoreType(String type) {
+            delegate.requestObjectSigningKeyStoreType = type;
+        }
+
+        @Override
+        public String getRequestObjectSigningKeyAlias() {
+            return delegate.requestObjectSigningKeyAlias;
+        }
+
+        @Override
+        public void setRequestObjectSigningKeyAlias(String alias) {
+            delegate.requestObjectSigningKeyAlias = alias;
+        }
+
+        @Override
+        public boolean getRequestParameterSupported() {
+            return delegate.requestParameterSupported;
+        }
+
+        @Override
+        public boolean getRequestUriParameterSupported() {
+            return delegate.requestUriParameterSupported;
+        }
+
     }
 
     protected String getAuthServerBaseUrl(OidcHttpFacade facade, String base) {
diff --git a/http/oidc/src/main/java/org/wildfly/security/http/oidc/OidcJsonConfiguration.java b/http/oidc/src/main/java/org/wildfly/security/http/oidc/OidcJsonConfiguration.java
index f835cc4fbc2..29d2d785e3c 100644
--- a/http/oidc/src/main/java/org/wildfly/security/http/oidc/OidcJsonConfiguration.java
+++ b/http/oidc/src/main/java/org/wildfly/security/http/oidc/OidcJsonConfiguration.java
@@ -38,15 +38,18 @@
         "resource", "public-client", "credentials",
         "use-resource-role-mappings", "use-realm-role-mappings",
         "enable-cors", "cors-max-age", "cors-allowed-methods", "cors-exposed-headers",
-        "expose-token", "bearer-only", "autodetect-bearer-only",
-        "connection-pool-size",
+        "expose-token", "bearer-only", "autodetect-bearer-only", "connection-pool-size",
         "allow-any-hostname", "disable-trust-manager", "truststore", "truststore-password",
         "client-keystore", "client-keystore-password", "client-key-password",
         "always-refresh-token",
         "register-node-at-startup", "register-node-period", "token-store", "adapter-state-cookie-path", "principal-attribute",
         "proxy-url", "turn-off-change-session-id-on-login", "token-minimum-time-to-live",
         "min-time-between-jwks-requests", "public-key-cache-ttl",
-        "ignore-oauth-query-parameter", "verify-token-audience", "token-signature-algorithm", "scope"
+        "ignore-oauth-query-parameter", "verify-token-audience", "token-signature-algorithm", "scope",
+        "authentication-request-format", "request-object-signing-algorithm", "request-object-encryption-alg-value",
+        "request-object-encryption-enc-value", "request-object-signing-keystore-file",
+        "request-object-signing-keystore-password","request-object-signing-key-password", "request-object-signing-key-alias",
+        "request-object-signing-keystore-type"
 })
 public class OidcJsonConfiguration {
 
@@ -64,6 +67,16 @@ public class OidcJsonConfiguration {
     protected String clientKeystorePassword;
     @JsonProperty("client-key-password")
     protected String clientKeyPassword;
+    @JsonProperty("request-object-signing-keystore-file")
+    protected String requestObjectSigningKeyStoreFile;
+    @JsonProperty("request-object-signing-keystore-password")
+    protected String requestObjectSigningKeyStorePassword;
+    @JsonProperty("request-object-signing-key-password")
+    protected String requestObjectSigningKeyPassword;
+    @JsonProperty("request-object-signing-key-alias")
+    protected String requestObjectSigningKeyAlias;
+    @JsonProperty("request-object-signing-keystore-type")
+    protected String requestObjectSigningKeyStoreType;
     @JsonProperty("connection-pool-size")
     protected int connectionPoolSize = 20;
     @JsonProperty("always-refresh-token")
@@ -142,6 +155,17 @@ public class OidcJsonConfiguration {
 
     @JsonProperty("scope")
     protected String scope;
+    @JsonProperty("authentication-request-format")
+    protected String authenticationRequestFormat;
+
+    @JsonProperty("request-object-signing-algorithm")
+    protected String requestObjectSigningAlgorithm;
+
+    @JsonProperty("request-object-encryption-alg-value")
+    protected String requestObjectEncryptionAlgValue;
+
+    @JsonProperty("request-object-encryption-enc-value")
+    protected String requestObjectEncryptionEncValue;
 
     /**
      * The Proxy url to use for requests to the auth-server, configurable via the adapter config property {@code proxy-url}.
@@ -181,6 +205,13 @@ public void setTruststorePassword(String truststorePassword) {
         this.truststorePassword = truststorePassword;
     }
 
+    public String getRequestObjectSigningKeyStoreFile() {
+        return requestObjectSigningKeyStoreFile;
+    }
+
+    public void setRequestObjectSigningKeyStoreFile(String requestObjectSigningKeyStoreFile) {
+        this.requestObjectSigningKeyStoreFile = requestObjectSigningKeyStoreFile;
+    }
     public String getClientKeystore() {
         return clientKeystore;
     }
@@ -189,6 +220,22 @@ public void setClientKeystore(String clientKeystore) {
         this.clientKeystore = clientKeystore;
     }
 
+    public String getRequestObjectSigningKeyStoreType() {
+        return requestObjectSigningKeyStoreType;
+    }
+
+    public void setRequestObjectSigningKeyStoreType(String requestObjectSigningKeyStoreType) {
+        this.requestObjectSigningKeyStoreType = requestObjectSigningKeyStoreType;
+    }
+
+    public String getRequestObjectSigningKeyAlias() {
+        return requestObjectSigningKeyAlias;
+    }
+
+    public void setRequestObjectSigningKeyAlias(String requestObjectSigningKeyAlias) {
+        this.requestObjectSigningKeyAlias = requestObjectSigningKeyAlias;
+    }
+
     public String getClientKeystorePassword() {
         return clientKeystorePassword;
     }
@@ -201,10 +248,26 @@ public String getClientKeyPassword() {
         return clientKeyPassword;
     }
 
+    public String getRequestObjectSigningKeyPassword() {
+        return requestObjectSigningKeyPassword;
+    }
+
+    public String getRequestObjectSigningKeyStorePassword() {
+        return requestObjectSigningKeyStorePassword;
+    }
+
     public void setClientKeyPassword(String clientKeyPassword) {
         this.clientKeyPassword = clientKeyPassword;
     }
 
+    public void setRequestObjectSigningKeyStorePassword(String requestObjectSigningKeyStorePassword) {
+        this.requestObjectSigningKeyStorePassword = requestObjectSigningKeyStorePassword;
+    }
+
+    public void setRequestObjectSigningKeyPassword(String requestObjectSigningKeyPassword) {
+        this.requestObjectSigningKeyPassword = requestObjectSigningKeyPassword;
+    }
+
     public int getConnectionPoolSize() {
         return connectionPoolSize;
     }
@@ -521,5 +584,36 @@ public String getScope() {
     public void setScope(String scope) {
         this.scope = scope;
     }
+    public String getAuthenticationRequestFormat() {
+        return authenticationRequestFormat;
+    }
+
+    public void setAuthenticationRequestFormat(String authenticationRequestFormat) {
+        this.authenticationRequestFormat = authenticationRequestFormat;
+    }
+
+    public String getRequestObjectSigningAlgorithm() {
+        return requestObjectSigningAlgorithm;
+    }
+
+    public void setRequestObjectSigningAlgorithm(String requestObjectSigningAlgorithm) {
+        this.requestObjectSigningAlgorithm = requestObjectSigningAlgorithm;
+    }
+
+    public String getRequestObjectEncryptionAlgValue() {
+        return requestObjectEncryptionAlgValue;
+    }
+
+    public void setRequestObjectEncryptionAlgValue(String requestObjectEncryptionAlgValue) {
+        this.requestObjectEncryptionAlgValue = requestObjectEncryptionAlgValue;
+    }
+
+    public String getRequestObjectEncryptionEncValue() {
+        return requestObjectEncryptionEncValue;
+    }
+
+    public void setRequestObjectEncryptionEncValue (String requestObjectEncryptionEncValue) {
+        this.requestObjectEncryptionEncValue = requestObjectEncryptionEncValue;
+    }
 }
 
diff --git a/http/oidc/src/main/java/org/wildfly/security/http/oidc/OidcProviderMetadata.java b/http/oidc/src/main/java/org/wildfly/security/http/oidc/OidcProviderMetadata.java
index 9984de7c023..6c964dbfe1b 100644
--- a/http/oidc/src/main/java/org/wildfly/security/http/oidc/OidcProviderMetadata.java
+++ b/http/oidc/src/main/java/org/wildfly/security/http/oidc/OidcProviderMetadata.java
@@ -114,6 +114,9 @@ public class OidcProviderMetadata {
     @JsonProperty("request_uri_parameter_supported")
     private Boolean requestUriParameterSupported;
 
+    @JsonProperty("pushed_authorization_request_endpoint")
+    private String pushedAuthorizationRequestEndpoint;
+
     @JsonProperty("revocation_endpoint")
     private String revocationEndpoint;
 
@@ -142,6 +145,12 @@ public class OidcProviderMetadata {
     @JsonProperty("tls_client_certificate_bound_access_tokens")
     private Boolean tlsClientCertificateBoundAccessTokens;
 
+    @JsonProperty("request_object_encryption_enc_values_supported")
+    private List<String> requestObjectEncryptionEncValuesSupported;
+
+    @JsonProperty("request_object_encryption_alg_values_supported")
+    private List<String> requestObjectEncryptionAlgValuesSupported;
+
     protected Map<String, Object> otherClaims = new HashMap<String, Object>();
 
     public String getIssuer() {
@@ -411,6 +420,30 @@ public Boolean getTlsClientCertificateBoundAccessTokens() {
         return tlsClientCertificateBoundAccessTokens;
     }
 
+    public List<String> getRequestObjectEncryptionAlgValuesSupported() {
+        return requestObjectEncryptionAlgValuesSupported;
+    }
+
+    public void setRequestObjectEncryptionAlgValuesSupported(List<String> requestObjectEncryptionAlgValuesSupported) {
+        this.requestObjectEncryptionAlgValuesSupported = requestObjectEncryptionAlgValuesSupported;
+    }
+
+    public List<String> getRequestObjectEncryptionEncValuesSupported() {
+        return requestObjectEncryptionEncValuesSupported;
+    }
+
+    public void setRequestObjectEncryptionEncValuesSupported(List<String> requestObjectEncryptionEncValuesSupported) {
+        this.requestObjectEncryptionEncValuesSupported = requestObjectEncryptionEncValuesSupported;
+    }
+
+    public String getPushedAuthorizationRequestEndpoint() {
+        return pushedAuthorizationRequestEndpoint;
+    }
+
+    public void setPushedAuthorizationRequestEndpoint(String url) {
+        this.pushedAuthorizationRequestEndpoint = url;
+    }
+
     @JsonAnyGetter
     public Map<String, Object> getOtherClaims() {
         return otherClaims;
diff --git a/http/oidc/src/main/java/org/wildfly/security/http/oidc/OidcRequestAuthenticator.java b/http/oidc/src/main/java/org/wildfly/security/http/oidc/OidcRequestAuthenticator.java
index bf67e938598..5ef5c26122e 100644
--- a/http/oidc/src/main/java/org/wildfly/security/http/oidc/OidcRequestAuthenticator.java
+++ b/http/oidc/src/main/java/org/wildfly/security/http/oidc/OidcRequestAuthenticator.java
@@ -18,6 +18,10 @@
 
 package org.wildfly.security.http.oidc;
 
+import static org.jose4j.jws.AlgorithmIdentifiers.HMAC_SHA256;
+import static org.jose4j.jws.AlgorithmIdentifiers.HMAC_SHA384;
+import static org.jose4j.jws.AlgorithmIdentifiers.HMAC_SHA512;
+import static org.jose4j.jws.AlgorithmIdentifiers.NONE;
 import static org.wildfly.security.http.oidc.ElytronMessages.log;
 import static org.wildfly.security.http.oidc.Oidc.ALLOW_QUERY_PARAMS_PROPERTY_NAME;
 import static org.wildfly.security.http.oidc.Oidc.CLIENT_ID;
@@ -32,13 +36,17 @@
 import static org.wildfly.security.http.oidc.Oidc.PROMPT;
 import static org.wildfly.security.http.oidc.Oidc.REDIRECT_URI;
 import static org.wildfly.security.http.oidc.Oidc.RESPONSE_TYPE;
+import static org.wildfly.security.http.oidc.Oidc.REQUEST;
+import static org.wildfly.security.http.oidc.Oidc.REQUEST_URI;
 import static org.wildfly.security.http.oidc.Oidc.SCOPE;
 import static org.wildfly.security.http.oidc.Oidc.SESSION_STATE;
 import static org.wildfly.security.http.oidc.Oidc.STATE;
 import static org.wildfly.security.http.oidc.Oidc.UI_LOCALES;
+import static org.wildfly.security.http.oidc.Oidc.ClientCredentialsProviderType.SECRET;
+
+import static org.wildfly.security.http.oidc.Oidc.logToken;
 import static org.wildfly.security.http.oidc.Oidc.generateId;
 import static org.wildfly.security.http.oidc.Oidc.getQueryParamValue;
-import static org.wildfly.security.http.oidc.Oidc.logToken;
 import static org.wildfly.security.http.oidc.Oidc.stripQueryParam;
 
 import java.io.IOException;
@@ -47,6 +55,10 @@
 import java.net.URL;
 import java.security.AccessController;
 import java.security.PrivilegedAction;
+import java.nio.charset.StandardCharsets;
+import java.security.Key;
+import java.security.KeyPair;
+import java.security.PublicKey;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.HashSet;
@@ -54,10 +66,16 @@
 import java.util.Map;
 import java.util.Set;
 
-import org.apache.http.HttpStatus;
 import org.apache.http.NameValuePair;
+import org.apache.http.HttpStatus;
 import org.apache.http.client.utils.URIBuilder;
 import org.apache.http.message.BasicNameValuePair;
+import org.jose4j.jwa.AlgorithmConstraints;
+import org.jose4j.jwe.JsonWebEncryption;
+import org.jose4j.jws.JsonWebSignature;
+import org.jose4j.jwt.JwtClaims;
+import org.jose4j.keys.HmacKey;
+import org.jose4j.lang.JoseException;
 import org.wildfly.security.http.HttpConstants;
 
 /**
@@ -201,18 +219,73 @@ protected String getRedirectUri(String state) {
                 return null;
             }
 
-            URIBuilder redirectUriBuilder = new URIBuilder(deployment.getAuthUrl())
-                    .addParameter(RESPONSE_TYPE, CODE)
-                    .addParameter(CLIENT_ID, deployment.getResourceName())
-                    .addParameter(REDIRECT_URI, rewrittenRedirectUri(url))
-                    .addParameter(STATE, state);
-            redirectUriBuilder.addParameters(forwardedQueryParams);
+            String redirectUri = rewrittenRedirectUri(url);
+            URIBuilder redirectUriBuilder = new URIBuilder(deployment.getAuthUrl());
+            redirectUriBuilder.addParameter(RESPONSE_TYPE, CODE)
+                    .addParameter(CLIENT_ID, deployment.getResourceName());
+
+            switch (deployment.getAuthenticationRequestFormat()) {
+                case REQUEST:
+                    if (deployment.getRequestParameterSupported()) {
+                        // add request objects into request parameter
+                        try {
+                            createRequestWithRequestParameter(REQUEST, redirectUriBuilder, redirectUri, state, forwardedQueryParams);
+                        } catch (IOException | JoseException e) {
+                            throw log.unableToCreateRequestWithRequestParameter(e);
+                        }
+                    } else {
+                        // send request as usual
+                        createOAuthRequest(redirectUriBuilder, redirectUri, state, forwardedQueryParams);
+                        log.requestParameterNotSupported();
+                    }
+                    break;
+                case REQUEST_URI:
+                    if (deployment.getRequestUriParameterSupported()) {
+                        try {
+                            createRequestWithRequestParameter(REQUEST_URI, redirectUriBuilder, redirectUri, state, forwardedQueryParams);
+                        } catch (IOException | JoseException e) {
+                            throw log.unableToCreateRequestUriWithRequestParameter(e);
+                        }
+                    } else {
+                        // send request as usual
+                        createOAuthRequest(redirectUriBuilder, redirectUri, state, forwardedQueryParams);
+                        log.requestParameterNotSupported();
+                    }
+                    break;
+                default:
+                    createOAuthRequest(redirectUriBuilder, redirectUri, state, forwardedQueryParams);
+                    break;
+            }
             return redirectUriBuilder.build().toString();
         } catch (URISyntaxException e) {
             throw log.unableToCreateRedirectResponse(e);
         }
     }
 
+    protected URIBuilder createOAuthRequest(URIBuilder redirectUriBuilder, String redirectUri, String state, List<NameValuePair> forwardedQueryParams) {
+        redirectUriBuilder.addParameter(REDIRECT_URI, redirectUri)
+                .addParameter(STATE, state)
+                .addParameters(forwardedQueryParams);
+        return redirectUriBuilder;
+    }
+
+    protected URIBuilder createRequestWithRequestParameter(String requestFormat, URIBuilder redirectUriBuilder, String redirectUri, String state, List<NameValuePair> forwardedQueryParams) throws JoseException, IOException {
+        String request = convertToRequestParameter(redirectUriBuilder, redirectUri, state, forwardedQueryParams);
+
+        switch (requestFormat) {
+            case REQUEST:
+                redirectUriBuilder.addParameter(REDIRECT_URI, redirectUri)
+                        .addParameter(REQUEST, request);
+                break;
+            case REQUEST_URI:
+                String request_uri = ServerRequest.getRequestUri(request, deployment);
+                redirectUriBuilder.addParameter("request_uri", request_uri)
+                        .addParameter(REDIRECT_URI, redirectUri);
+                break;
+        }
+        return redirectUriBuilder;
+    }
+
     protected int getSSLRedirectPort() {
         return sslRedirectPort;
     }
@@ -461,4 +534,92 @@ private void addScopes(String scopes, Set<String> allScopes) {
             allScopes.addAll(Arrays.asList(scopes.split("\\s+")));
         }
     }
+
+    private String convertToRequestParameter(URIBuilder redirectUriBuilder, String redirectUri, String state, List<NameValuePair> forwardedQueryParams) throws JoseException, IOException {
+        redirectUriBuilder.addParameter(SCOPE, OIDC_SCOPE);
+
+        JwtClaims jwtClaims = new JwtClaims();
+        jwtClaims.setIssuer(deployment.getResourceName());
+        jwtClaims.setAudience(deployment.getIssuerUrl());
+
+        for ( NameValuePair parameter: forwardedQueryParams) {
+            jwtClaims.setClaim(parameter.getName(), parameter.getValue());
+        }
+        jwtClaims.setClaim(STATE, state);
+        jwtClaims.setClaim(REDIRECT_URI, redirectUri);
+        jwtClaims.setClaim(RESPONSE_TYPE, CODE);
+        jwtClaims.setClaim(CLIENT_ID, deployment.getResourceName());
+
+        // sign JWT first before encrypting
+        JsonWebSignature signedRequest = signRequest(jwtClaims, deployment);
+
+        // Encrypting optional
+        if (deployment.getRequestObjectEncryptionAlgValue() != null && !deployment.getRequestObjectEncryptionAlgValue().isEmpty() &&
+            deployment.getRequestObjectEncryptionEncValue() != null && !deployment.getRequestObjectEncryptionEncValue().isEmpty()) {
+            return encryptRequest(signedRequest).getCompactSerialization();
+        } else {
+            return signedRequest.getCompactSerialization();
+        }
+    }
+
+    private static KeyPair getkeyPair(OidcClientConfiguration deployment) throws IOException {
+        if (!deployment.getRequestObjectSigningAlgorithm().equals(NONE) && deployment.getRequestObjectSigningKeyStoreFile() == null){
+            throw log.invalidKeyStoreConfiguration();
+        } else {
+            return JWTSigningUtils.loadKeyPairFromKeyStore(deployment.getRequestObjectSigningKeyStoreFile(),
+                    deployment.getRequestObjectSigningKeyStorePassword(), deployment.getRequestObjectSigningKeyPassword(),
+                    deployment.getRequestObjectSigningKeyAlias(), deployment.getRequestObjectSigningKeyStoreType());
+        }
+    }
+
+    private static JsonWebSignature signRequest(JwtClaims jwtClaims, OidcClientConfiguration deployment) throws IOException, JoseException {
+        JsonWebSignature jsonWebSignature = new JsonWebSignature();
+        jsonWebSignature.setPayload(jwtClaims.toJson());
+
+        if (!deployment.getRequestObjectSigningAlgValuesSupported().contains(deployment.getRequestObjectSigningAlgorithm())) {
+            throw log.invalidRequestObjectSignatureAlgorithm();
+        } else {
+            if (deployment.getRequestObjectSigningAlgorithm().equals(NONE)) { //unsigned
+                jsonWebSignature.setAlgorithmConstraints(AlgorithmConstraints.NO_CONSTRAINTS);
+                jsonWebSignature.setAlgorithmHeaderValue(NONE);
+            } else if (deployment.getRequestObjectSigningAlgorithm().equals(HMAC_SHA256)
+                    || deployment.getRequestObjectSigningAlgorithm().equals(HMAC_SHA384)
+                    || deployment.getRequestObjectSigningAlgorithm().equals(HMAC_SHA512)) { //signed with symmetric key
+                jsonWebSignature.setAlgorithmHeaderValue(deployment.getRequestObjectSigningAlgorithm());
+                String secretKey = (String) deployment.getResourceCredentials().get(SECRET.getValue());
+                if (secretKey == null) {
+                    throw log.clientSecretNotConfigured();
+                } else {
+                    Key key = new HmacKey(secretKey.getBytes(StandardCharsets.UTF_8));   //the client secret is a shared secret between the server and the client
+                    jsonWebSignature.setKey(key);
+                }
+            } else { //signed with asymmetric key
+                KeyPair keyPair = getkeyPair(deployment);
+                jsonWebSignature.setKey(keyPair.getPrivate());
+                jsonWebSignature.setAlgorithmHeaderValue(deployment.getRequestObjectSigningAlgorithm());
+            }
+            if (!deployment.getRequestObjectSigningAlgorithm().equals(NONE))
+                jsonWebSignature.sign();
+            else
+                log.unsignedRequestObjectIsUsed();
+            return jsonWebSignature;
+        }
+    }
+
+    private JsonWebEncryption encryptRequest(JsonWebSignature signedRequest) throws JoseException, IOException {
+        if (!deployment.getRequestObjectEncryptionAlgValuesSupported().contains(deployment.getRequestObjectEncryptionAlgValue())) {
+            throw log.invalidRequestObjectEncryptionAlgorithm();
+        } else if (!deployment.getRequestObjectEncryptionEncValuesSupported().contains(deployment.getRequestObjectEncryptionEncValue())) {
+            throw log.invalidRequestObjectEncryptionEncValue();
+        } else {
+            JsonWebEncryption jsonEncryption = new JsonWebEncryption();
+            jsonEncryption.setPayload(signedRequest.getCompactSerialization());
+            jsonEncryption.setAlgorithmConstraints(new AlgorithmConstraints(AlgorithmConstraints.ConstraintType.PERMIT, deployment.getRequestObjectEncryptionAlgValue(), deployment.getRequestObjectEncryptionEncValue()));
+            jsonEncryption.setAlgorithmHeaderValue(deployment.getRequestObjectEncryptionAlgValue());
+            jsonEncryption.setEncryptionMethodHeaderParameter(deployment.getRequestObjectEncryptionEncValue());
+            PublicKey encPublicKey = deployment.getEncryptionPublicKeyLocator().getPublicKey(null, deployment);
+            jsonEncryption.setKey(encPublicKey);
+            return jsonEncryption;
+        }
+    }
 }
diff --git a/http/oidc/src/main/java/org/wildfly/security/http/oidc/ServerRequest.java b/http/oidc/src/main/java/org/wildfly/security/http/oidc/ServerRequest.java
index ad50d715c56..3a203541ee4 100644
--- a/http/oidc/src/main/java/org/wildfly/security/http/oidc/ServerRequest.java
+++ b/http/oidc/src/main/java/org/wildfly/security/http/oidc/ServerRequest.java
@@ -25,13 +25,14 @@
 import static org.wildfly.security.http.oidc.Oidc.KEYCLOAK_CLIENT_CLUSTER_HOST;
 import static org.wildfly.security.http.oidc.Oidc.PASSWORD;
 import static org.wildfly.security.http.oidc.Oidc.REDIRECT_URI;
+import static org.wildfly.security.http.oidc.Oidc.REQUEST;
 import static org.wildfly.security.http.oidc.Oidc.USERNAME;
 
 import java.io.BufferedReader;
 import java.io.ByteArrayOutputStream;
-import java.io.IOException;
 import java.io.InputStream;
 import java.io.InputStreamReader;
+import java.io.IOException;
 import java.nio.charset.Charset;
 import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;
@@ -46,6 +47,8 @@
 import org.apache.http.client.methods.HttpPost;
 import org.apache.http.message.BasicNameValuePair;
 import org.apache.http.util.EntityUtils;
+import org.jose4j.jwt.JwtClaims;
+import org.jose4j.jwt.consumer.InvalidJwtException;
 import org.wildfly.security.jose.util.JsonSerialization;
 
 /**
@@ -274,4 +277,34 @@ public static AccessAndIDTokenResponse getBearerToken(OidcClientConfiguration oi
         }
         return tokenResponse;
     }
+
+    public static String getRequestUri(String request, OidcClientConfiguration deployment) throws OidcException {
+        if (deployment.getPushedAuthorizationRequestEndpoint() == null) {
+            throw log.pushedAuthorizationRequestEndpointNotAvailable();
+        }
+        HttpPost parRequest = new HttpPost(deployment.getPushedAuthorizationRequestEndpoint());
+        List<NameValuePair> formParams = new ArrayList<NameValuePair>();
+        formParams.add(new BasicNameValuePair(REQUEST, request));
+        ClientCredentialsProviderUtils.setClientCredentials(deployment, parRequest, formParams);
+
+        UrlEncodedFormEntity form = new UrlEncodedFormEntity(formParams, StandardCharsets.UTF_8);
+        parRequest.setEntity(form);
+
+        HttpResponse response;
+        try {
+            response = deployment.getClient().execute(parRequest);
+        } catch (Exception e) {
+            throw log.failedToSendPushedAuthorizationRequest(e);
+        }
+        if (response.getStatusLine().getStatusCode() != HttpStatus.SC_CREATED) {
+            EntityUtils.consumeQuietly(response.getEntity());
+            throw log.unexpectedResponseCodeFromOidcProvider(response.getStatusLine().getStatusCode());
+        }
+        try (InputStream inputStream = response.getEntity().getContent()) {
+            JwtClaims jwt = JwtClaims.parse(readString(inputStream, StandardCharsets.UTF_8));
+            return jwt.getClaimValueAsString("request_uri");
+        } catch (IOException | InvalidJwtException e) {
+            throw log.failedToDecodeRequestUri(e);
+        }
+    }
 }
diff --git a/http/oidc/src/test/java/org/wildfly/security/http/oidc/KeycloakConfiguration.java b/http/oidc/src/test/java/org/wildfly/security/http/oidc/KeycloakConfiguration.java
index 4bb5e2b33b0..8ebf4051bf2 100644
--- a/http/oidc/src/test/java/org/wildfly/security/http/oidc/KeycloakConfiguration.java
+++ b/http/oidc/src/test/java/org/wildfly/security/http/oidc/KeycloakConfiguration.java
@@ -20,12 +20,23 @@
 
 import static org.wildfly.security.http.oidc.OidcBaseTest.TENANT1_REALM;
 import static org.wildfly.security.http.oidc.OidcBaseTest.TENANT2_REALM;
+import static org.wildfly.security.http.oidc.Oidc.OIDC_SCOPE;
 
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.InputStream;
+import java.security.KeyStore;
+import java.security.cert.X509Certificate;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Base64;
 import java.util.Collections;
 import java.util.List;
+import java.util.Objects;
+import javax.security.auth.x500.X500Principal;
 
+import org.keycloak.protocol.oidc.OIDCAdvancedConfigWrapper;
 import org.keycloak.representations.AccessTokenResponse;
 import org.keycloak.representations.idm.ClientRepresentation;
 import org.keycloak.representations.idm.CredentialRepresentation;
@@ -33,10 +44,9 @@
 import org.keycloak.representations.idm.RoleRepresentation;
 import org.keycloak.representations.idm.RolesRepresentation;
 import org.keycloak.representations.idm.UserRepresentation;
-
+import org.wildfly.security.ssl.test.util.CAGenerationTool;
 import io.restassured.RestAssured;
 
-import static org.wildfly.security.http.oidc.Oidc.OIDC_SCOPE;
 
 /**
  * Keycloak configuration for testing.
@@ -53,6 +63,24 @@ public class KeycloakConfiguration {
     private static final String BOB_PASSWORD = "bob123+";
     public static final String ALLOWED_ORIGIN = "http://somehost";
     public static final boolean EMAIL_VERIFIED = false;
+    public static final String RSA_KEYSTORE_FILE_NAME = "jwt.keystore";
+    public static final String EC_KEYSTORE_FILE_NAME = "jwtEC.keystore";
+    public static final String KEYSTORE_ALIAS = "jwtKeystore";
+    public static final String KEYSTORE_PASS = "Elytron";
+    public static final String PKCS12_KEYSTORE_TYPE = "PKCS12";
+    public static String KEYSTORE_CLASSPATH;
+
+    /* Accepted Request Object Encrypting Algorithms for KeyCloak*/
+    public static final String RSA_OAEP = "RSA-OAEP";
+    public static final String RSA_OAEP_256 = "RSA-OAEP-256";
+    public static final String RSA1_5 = "RSA1_5";
+
+    /* Accepted Request Object Encryption Methods for KeyCloak*/
+    public static final String A128CBC_HS256 = "A128CBC-HS256";
+    public static final String A192CBC_HS384 = "A192CBC-HS384";
+    public static final String A256CBC_HS512 = "A256CBC-HS512";
+    public static CAGenerationTool caGenerationTool = null;
+    public X509Certificate caCertificate = null;
 
     // the users below are for multi-tenancy tests specifically
     public static final String TENANT1_USER = "tenant1_user";
@@ -76,20 +104,20 @@ public class KeycloakConfiguration {
      * </ul>
      */
     public static RealmRepresentation getRealmRepresentation(final String realmName, String clientId, String clientSecret,
-                                                             String clientHostName, int clientPort, String clientApp, boolean configureClientScopes) {
+                                                             String clientHostName, int clientPort, String clientApp, boolean configureClientScopes) throws Exception {
         return createRealm(realmName, clientId, clientSecret, clientHostName, clientPort, clientApp, configureClientScopes);
     }
 
     public static RealmRepresentation getRealmRepresentation(final String realmName, String clientId, String clientSecret,
                                                              String clientHostName, int clientPort, String clientApp, int accessTokenLifespan,
-                                                             int ssoSessionMaxLifespan, boolean configureClientScopes, boolean multiTenancyApp) {
+                                                             int ssoSessionMaxLifespan, boolean configureClientScopes, boolean multiTenancyApp) throws Exception {
         return createRealm(realmName, clientId, clientSecret, clientHostName, clientPort, clientApp, accessTokenLifespan, ssoSessionMaxLifespan, configureClientScopes, multiTenancyApp);
     }
 
     public static RealmRepresentation getRealmRepresentation(final String realmName, String clientId, String clientSecret,
                                                              String clientHostName, int clientPort, String clientApp,
                                                              boolean directAccessGrantEnabled, String bearerOnlyClientId,
-                                                             String corsClientId) {
+                                                             String corsClientId) throws Exception {
         return createRealm(realmName, clientId, clientSecret, clientHostName, clientPort, clientApp, directAccessGrantEnabled, bearerOnlyClientId, corsClientId);
     }
 
@@ -126,25 +154,25 @@ public static String getAccessToken(String authServerUrl, String realmName, Stri
     private static RealmRepresentation createRealm(final String realmName, String clientId, String clientSecret,
                                String clientHostName, int clientPort, String clientApp,
                                boolean directAccessGrantEnabled, String bearerOnlyClientId,
-                               String corsClientId) {
+                               String corsClientId) throws Exception {
        return createRealm(realmName, clientId, clientSecret, clientHostName, clientPort, clientApp, directAccessGrantEnabled, bearerOnlyClientId, corsClientId, false);
     }
 
     private static RealmRepresentation createRealm(String name, String clientId, String clientSecret,
-                                                   String clientHostName, int clientPort, String clientApp, boolean configureClientScopes) {
+                                                   String clientHostName, int clientPort, String clientApp, boolean configureClientScopes) throws Exception {
         return createRealm(name, clientId, clientSecret, clientHostName, clientPort, clientApp, false, null, null, configureClientScopes);
     }
 
     private static RealmRepresentation createRealm(String name, String clientId, String clientSecret,
                                                    String clientHostName, int clientPort, String clientApp, int accessTokenLifeSpan, int ssoSessionMaxLifespan,
-                                                   boolean configureClientScopes, boolean multiTenancyApp) {
+                                                   boolean configureClientScopes, boolean multiTenancyApp) throws Exception {
         return createRealm(name, clientId, clientSecret, clientHostName, clientPort, clientApp, false, null, null, accessTokenLifeSpan, ssoSessionMaxLifespan, configureClientScopes, multiTenancyApp);
     }
 
     private static RealmRepresentation createRealm(String name, String clientId, String clientSecret,
                                                    String clientHostName, int clientPort, String clientApp,
                                                    boolean directAccessGrantEnabled, String bearerOnlyClientId,
-                                                   String corsClientId, boolean configureClientScopes) {
+                                                   String corsClientId, boolean configureClientScopes) throws Exception {
         return createRealm(name, clientId, clientSecret, clientHostName, clientPort, clientApp, directAccessGrantEnabled, bearerOnlyClientId, corsClientId, 3, 3, configureClientScopes, false);
     }
 
@@ -152,7 +180,7 @@ private static RealmRepresentation createRealm(String name, String clientId, Str
                                                    String clientHostName, int clientPort, String clientApp,
                                                    boolean directAccessGrantEnabled, String bearerOnlyClientId,
                                                    String corsClientId, int accessTokenLifespan, int ssoSessionMaxLifespan,
-                                                   boolean configureClientScopes, boolean multiTenancyApp) {
+                                                   boolean configureClientScopes, boolean multiTenancyApp) throws Exception {
         RealmRepresentation realm = new RealmRepresentation();
         realm.setRealm(name);
         realm.setEnabled(true);
@@ -201,17 +229,12 @@ private static RealmRepresentation createRealm(String name, String clientId, Str
     }
 
     private static ClientRepresentation createWebAppClient(String clientId, String clientSecret, String clientHostName, int clientPort, String clientApp,
-                                                           boolean directAccessGrantEnabled, boolean multiTenancyApp) {
+                                                           boolean directAccessGrantEnabled, boolean multiTenancyApp) throws Exception {
         return createWebAppClient(clientId, clientSecret, clientHostName, clientPort, clientApp, directAccessGrantEnabled, null, multiTenancyApp);
     }
 
     private static ClientRepresentation createWebAppClient(String clientId, String clientSecret, String clientHostName, int clientPort,
-                                                           String clientApp, boolean directAccessGrantEnabled, String allowedOrigin) {
-        return createWebAppClient(clientId, clientSecret, clientHostName, clientPort, clientApp, directAccessGrantEnabled, allowedOrigin, false);
-    }
-
-    private static ClientRepresentation createWebAppClient(String clientId, String clientSecret, String clientHostName, int clientPort,
-                                                           String clientApp, boolean directAccessGrantEnabled, String allowedOrigin, boolean multiTenancyApp) {
+                                                           String clientApp, boolean directAccessGrantEnabled, String allowedOrigin, boolean multiTenancyApp) throws Exception {
         ClientRepresentation client = new ClientRepresentation();
         client.setClientId(clientId);
         client.setPublicClient(false);
@@ -224,9 +247,29 @@ private static ClientRepresentation createWebAppClient(String clientId, String c
         }
         client.setEnabled(true);
         client.setDirectAccessGrantsEnabled(directAccessGrantEnabled);
+
         if (allowedOrigin != null) {
             client.setWebOrigins(Collections.singletonList(allowedOrigin));
         }
+
+        OIDCAdvancedConfigWrapper oidcAdvancedConfigWrapper = OIDCAdvancedConfigWrapper.fromClientRepresentation(client);
+        oidcAdvancedConfigWrapper.setUseJwksUrl(false);
+        KEYSTORE_CLASSPATH = Objects.requireNonNull(KeycloakConfiguration.class.getClassLoader().getResource("")).getPath();
+        File ksFile = new File(KEYSTORE_CLASSPATH + RSA_KEYSTORE_FILE_NAME);
+        if (ksFile.exists()) {
+            InputStream stream = findFile(KEYSTORE_CLASSPATH + RSA_KEYSTORE_FILE_NAME);
+            KeyStore keyStore = KeyStore.getInstance(PKCS12_KEYSTORE_TYPE);
+            keyStore.load(stream, KEYSTORE_PASS.toCharArray());
+            client.getAttributes().put("jwt.credential.certificate", Base64.getEncoder().encodeToString(keyStore.getCertificate(KEYSTORE_ALIAS).getEncoded()));
+        } else {
+            caGenerationTool = CAGenerationTool.builder()
+                    .setBaseDir(KEYSTORE_CLASSPATH)
+                    .setRequestIdentities(CAGenerationTool.Identity.values()) // Create all identities.
+                    .build();
+            X500Principal principal = new X500Principal("OU=Elytron, O=Elytron, C=UK, ST=Elytron, CN=OcspResponder");
+            X509Certificate rsaCert = caGenerationTool.createIdentity(KEYSTORE_ALIAS, principal, RSA_KEYSTORE_FILE_NAME, CAGenerationTool.Identity.CA);
+            client.getAttributes().put("jwt.credential.certificate", Base64.getEncoder().encodeToString(rsaCert.getEncoded()));
+        }
         return client;
     }
 
@@ -257,4 +300,12 @@ private static UserRepresentation createUser(String username, String password, L
         return user;
     }
 
+    private static InputStream findFile(String keystoreFile) {
+        try {
+            return new FileInputStream(keystoreFile);
+        } catch (FileNotFoundException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
 }
\ No newline at end of file
diff --git a/http/oidc/src/test/java/org/wildfly/security/http/oidc/OidcBaseTest.java b/http/oidc/src/test/java/org/wildfly/security/http/oidc/OidcBaseTest.java
index de3115d96b0..b604af8a8f8 100644
--- a/http/oidc/src/test/java/org/wildfly/security/http/oidc/OidcBaseTest.java
+++ b/http/oidc/src/test/java/org/wildfly/security/http/oidc/OidcBaseTest.java
@@ -81,7 +81,7 @@
 public class OidcBaseTest extends AbstractBaseHttpTest {
 
     public static final String CLIENT_ID = "test-webapp";
-    public static final String CLIENT_SECRET = "secret";
+    public static final String CLIENT_SECRET = "longerclientsecretthatisstleast256bitslong";
     public static KeycloakContainer KEYCLOAK_CONTAINER;
     public static final String TEST_REALM = "WildFly";
     public static final String TEST_REALM_WITH_SCOPES = "WildFlyScopes";
@@ -100,6 +100,13 @@ public class OidcBaseTest extends AbstractBaseHttpTest {
     public static final String TENANT2_ENDPOINT = "tenant2";
     protected HttpServerAuthenticationMechanismFactory oidcFactory;
 
+    public enum RequestObjectErrorType {
+        INVALID_ALGORITHM,
+        MISSING_CLIENT_SECRET,
+        INVALID_REQUEST_FORMAT,
+        MISSING_ENC_VALUE
+    }
+
     @AfterClass
     public static void generalCleanup() throws Exception {
         if (KEYCLOAK_CONTAINER != null) {
diff --git a/http/oidc/src/test/java/org/wildfly/security/http/oidc/OidcTest.java b/http/oidc/src/test/java/org/wildfly/security/http/oidc/OidcTest.java
index b7e1ce6ec6a..1c6d4b290a4 100644
--- a/http/oidc/src/test/java/org/wildfly/security/http/oidc/OidcTest.java
+++ b/http/oidc/src/test/java/org/wildfly/security/http/oidc/OidcTest.java
@@ -18,6 +18,20 @@
 
 package org.wildfly.security.http.oidc;
 
+import static org.jose4j.jws.AlgorithmIdentifiers.NONE;
+import static org.jose4j.jws.AlgorithmIdentifiers.RSA_USING_SHA256;
+import static org.jose4j.jws.AlgorithmIdentifiers.RSA_USING_SHA512;
+import static org.jose4j.jws.AlgorithmIdentifiers.HMAC_SHA256;
+import static org.jose4j.jws.AlgorithmIdentifiers.RSA_PSS_USING_SHA256;
+import static org.wildfly.security.http.oidc.KeycloakConfiguration.KEYSTORE_CLASSPATH;
+import static org.wildfly.security.http.oidc.KeycloakConfiguration.KEYSTORE_PASS;
+import static org.wildfly.security.http.oidc.KeycloakConfiguration.PKCS12_KEYSTORE_TYPE;
+import static org.wildfly.security.http.oidc.KeycloakConfiguration.RSA1_5;
+import static org.wildfly.security.http.oidc.KeycloakConfiguration.RSA_OAEP;
+import static org.wildfly.security.http.oidc.KeycloakConfiguration.RSA_OAEP_256;
+import static org.wildfly.security.http.oidc.KeycloakConfiguration.A128CBC_HS256;
+import static org.wildfly.security.http.oidc.KeycloakConfiguration.A192CBC_HS384;
+import static org.wildfly.security.http.oidc.KeycloakConfiguration.A256CBC_HS512;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
@@ -32,6 +46,9 @@
 import static org.wildfly.security.http.oidc.KeycloakConfiguration.TENANT2_USER;
 import static org.wildfly.security.http.oidc.Oidc.OIDC_NAME;
 import static org.wildfly.security.http.oidc.Oidc.OIDC_SCOPE;
+import static org.wildfly.security.http.oidc.Oidc.AuthenticationRequestFormat.OAUTH2;
+import static org.wildfly.security.http.oidc.Oidc.AuthenticationRequestFormat.REQUEST;
+import static org.wildfly.security.http.oidc.Oidc.AuthenticationRequestFormat.REQUEST_URI;
 
 import java.io.ByteArrayInputStream;
 import java.io.InputStream;
@@ -42,19 +59,18 @@
 
 import javax.security.auth.callback.CallbackHandler;
 
-import org.apache.http.HttpStatus;
-import org.junit.AfterClass;
-import org.junit.BeforeClass;
-import org.junit.Test;
-import org.wildfly.security.http.HttpServerAuthenticationMechanism;
-
+import com.gargoylesoftware.htmlunit.html.HtmlPage;
 import com.gargoylesoftware.htmlunit.TextPage;
 import com.gargoylesoftware.htmlunit.WebClient;
-import com.gargoylesoftware.htmlunit.html.HtmlPage;
-
 import io.restassured.RestAssured;
 import okhttp3.mockwebserver.MockWebServer;
 import okhttp3.mockwebserver.QueueDispatcher;
+import org.apache.http.HttpStatus;
+import org.junit.AfterClass;
+import org.junit.Assert;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.wildfly.security.http.HttpServerAuthenticationMechanism;
 
 /**
  * Tests for the OpenID Connect authentication mechanism.
@@ -237,6 +253,100 @@ public void testOpenIDWithMultipleScopeValue() throws Exception {
                 true, HttpStatus.SC_MOVED_TEMPORARILY, getClientUrl(), CLIENT_PAGE_TEXT, expectedScope, false);
     }
 
+    // Note: The tests will fail if `localhost` is not listed first in `/etc/hosts` file for the loopback addresses (IPv4 and IPv6).
+    @Test
+    public void testSuccessfulOauth2Request() throws Exception {
+        performAuthentication(getOidcConfigurationInputStreamWithRequestParameter(OAUTH2.getValue(), "", "", ""), KeycloakConfiguration.ALICE, KeycloakConfiguration.ALICE_PASSWORD,
+                true, HttpStatus.SC_MOVED_TEMPORARILY, getClientUrl(), CLIENT_PAGE_TEXT);
+    }
+
+    @Test
+    public void testSuccessfulPlaintextRequest() throws Exception {
+        performAuthentication(getOidcConfigurationInputStreamWithRequestParameter(REQUEST.getValue(), NONE, "", ""), KeycloakConfiguration.ALICE, KeycloakConfiguration.ALICE_PASSWORD,
+                true, HttpStatus.SC_MOVED_TEMPORARILY, getClientUrl(), CLIENT_PAGE_TEXT);
+    }
+
+    @Test
+    public void testSuccessfulPlaintextEncryptedRequest() throws Exception {
+        performAuthentication(getOidcConfigurationInputStreamWithRequestParameter(REQUEST.getValue(), NONE, RSA_OAEP, A128CBC_HS256), KeycloakConfiguration.ALICE, KeycloakConfiguration.ALICE_PASSWORD,
+                true, HttpStatus.SC_MOVED_TEMPORARILY, getClientUrl(), CLIENT_PAGE_TEXT);
+    }
+
+    @Test
+    public void testSuccessfulRsaSignedAndEncryptedRequest() throws Exception {
+        performAuthentication(getOidcConfigurationInputStreamWithRequestParameter(REQUEST.getValue(), RSA_USING_SHA512, RSA_OAEP, A192CBC_HS384, KEYSTORE_CLASSPATH + KeycloakConfiguration.RSA_KEYSTORE_FILE_NAME, KeycloakConfiguration.KEYSTORE_ALIAS, PKCS12_KEYSTORE_TYPE), KeycloakConfiguration.ALICE, KeycloakConfiguration.ALICE_PASSWORD,
+                true, HttpStatus.SC_MOVED_TEMPORARILY, getClientUrl(), CLIENT_PAGE_TEXT);
+    }
+
+    @Test
+    public void testSuccessfulPsSignedAndRsaEncryptedRequest() throws Exception {
+        performAuthentication(getOidcConfigurationInputStreamWithRequestParameter(REQUEST.getValue(), RSA_PSS_USING_SHA256, RSA_OAEP_256, A256CBC_HS512, KEYSTORE_CLASSPATH + KeycloakConfiguration.RSA_KEYSTORE_FILE_NAME, KeycloakConfiguration.KEYSTORE_ALIAS, PKCS12_KEYSTORE_TYPE), KeycloakConfiguration.ALICE, KeycloakConfiguration.ALICE_PASSWORD,
+                true, HttpStatus.SC_MOVED_TEMPORARILY, getClientUrl(), CLIENT_PAGE_TEXT);
+    }
+
+    @Test
+    public void testInvalidSigningAlgorithm() throws Exception {
+        //ES256K is a valid signature algorithm, but not one of the ones supported by keycloak
+        testRequestObjectInvalidConfiguration(getOidcConfigurationInputStreamWithRequestParameter(REQUEST.getValue(), "ES256K", RSA_OAEP_256, A256CBC_HS512, KEYSTORE_CLASSPATH + KeycloakConfiguration.RSA_KEYSTORE_FILE_NAME, KeycloakConfiguration.KEYSTORE_ALIAS, PKCS12_KEYSTORE_TYPE), RequestObjectErrorType.INVALID_ALGORITHM);
+    }
+
+    @Test
+    public void testSuccessfulRsaSignedRequest() throws Exception {
+        performAuthentication(getOidcConfigurationInputStreamWithRequestParameter(REQUEST.getValue(), RSA_USING_SHA256, "", "", KEYSTORE_CLASSPATH + KeycloakConfiguration.RSA_KEYSTORE_FILE_NAME, KeycloakConfiguration.KEYSTORE_ALIAS, PKCS12_KEYSTORE_TYPE), KeycloakConfiguration.ALICE, KeycloakConfiguration.ALICE_PASSWORD,
+                true, HttpStatus.SC_MOVED_TEMPORARILY, getClientUrl(), CLIENT_PAGE_TEXT);
+    }
+
+    @Test
+    public void testSuccessfulPsSignedRequest() throws Exception {
+        performAuthentication(getOidcConfigurationInputStreamWithRequestParameter(REQUEST.getValue(), RSA_PSS_USING_SHA256, "", "", KEYSTORE_CLASSPATH + KeycloakConfiguration.RSA_KEYSTORE_FILE_NAME, KeycloakConfiguration.KEYSTORE_ALIAS, PKCS12_KEYSTORE_TYPE), KeycloakConfiguration.ALICE, KeycloakConfiguration.ALICE_PASSWORD,
+                true, HttpStatus.SC_MOVED_TEMPORARILY, getClientUrl(), CLIENT_PAGE_TEXT);
+    }
+    @Test
+    public void testInvalidRequestEncryptionAlgorithm() throws Exception {
+        // None is not a valid algorithm for encrypting jwt's and RSA-OAEP is not a valid algorithm for signing
+        testRequestObjectInvalidConfiguration(getOidcConfigurationInputStreamWithRequestParameter(REQUEST.getValue(), RSA1_5, NONE, NONE, KEYSTORE_CLASSPATH + KeycloakConfiguration.RSA_KEYSTORE_FILE_NAME, KeycloakConfiguration.KEYSTORE_ALIAS, PKCS12_KEYSTORE_TYPE), RequestObjectErrorType.INVALID_ALGORITHM);
+    }
+
+    @Test
+    public void testSuccessfulPlaintextRequestUri() throws Exception {
+        performAuthentication(getOidcConfigurationInputStreamWithRequestParameter(REQUEST_URI.getValue(), NONE, "", ""), KeycloakConfiguration.ALICE, KeycloakConfiguration.ALICE_PASSWORD,
+                true, HttpStatus.SC_MOVED_TEMPORARILY, getClientUrl(), CLIENT_PAGE_TEXT);
+    }
+
+    @Test
+    public void testSuccessfulHmacSignedRequestUri() throws Exception {
+        performAuthentication(getOidcConfigurationInputStreamWithRequestParameter(REQUEST.getValue(), HMAC_SHA256, "", ""), KeycloakConfiguration.ALICE, KeycloakConfiguration.ALICE_PASSWORD,
+                true, HttpStatus.SC_MOVED_TEMPORARILY, getClientUrl(), CLIENT_PAGE_TEXT);
+    }
+
+    @Test
+    public void testSuccessfulHmacSignedAndEncryptedRequestUri() throws Exception {
+        performAuthentication(getOidcConfigurationInputStreamWithRequestParameter(REQUEST.getValue(), HMAC_SHA256, RSA_OAEP, A128CBC_HS256), KeycloakConfiguration.ALICE, KeycloakConfiguration.ALICE_PASSWORD,
+                true, HttpStatus.SC_MOVED_TEMPORARILY, getClientUrl(), CLIENT_PAGE_TEXT);
+    }
+
+    @Test
+    public void testSuccessfulSignedAndEncryptedRequestUri() throws Exception {
+        performAuthentication(getOidcConfigurationInputStreamWithRequestParameter(REQUEST_URI.getValue(), RSA_USING_SHA256, RSA_OAEP_256, A256CBC_HS512, KEYSTORE_CLASSPATH + KeycloakConfiguration.RSA_KEYSTORE_FILE_NAME, KeycloakConfiguration.KEYSTORE_ALIAS, PKCS12_KEYSTORE_TYPE), KeycloakConfiguration.ALICE, KeycloakConfiguration.ALICE_PASSWORD,
+                true, HttpStatus.SC_MOVED_TEMPORARILY, getClientUrl(), CLIENT_PAGE_TEXT);
+    }
+
+    @Test
+    public void testSuccessfulHmacSignedRequestObjectWithoutSecret() throws Exception {
+        // this is supposed to fail since for symmetric algorithms we sign the request object with the client secret
+        testRequestObjectInvalidConfiguration(getOidcConfigurationInputStreamWithRequestObjectPublicClient(REQUEST.getValue(), HMAC_SHA256), RequestObjectErrorType.MISSING_CLIENT_SECRET);
+    }
+
+    @Test
+    public void testIncorrectAuthenticationFormat() throws Exception {
+        testRequestObjectInvalidConfiguration(getOidcConfigurationInputStreamWithRequestObjectPublicClient("INVALID_REQUEST_PARAMETER", HMAC_SHA256), RequestObjectErrorType.INVALID_REQUEST_FORMAT);
+    }
+
+    @Test
+    public void testRequestObjectConfigMissingENCValue() throws Exception {
+        testRequestObjectInvalidConfiguration(getOidcConfigurationInputStreamWithoutEncValue(REQUEST.getValue(), RSA_OAEP), RequestObjectErrorType.MISSING_ENC_VALUE);
+    }
+
     /*****************************************************************************************************************************************
      * Tests for multi-tenancy.
      *
@@ -496,6 +606,51 @@ private void performTenantRequest(String username, String password, String tenan
         }
     }
 
+    private void testRequestObjectInvalidConfiguration(InputStream oidcConfig, RequestObjectErrorType requestObjectErrorType) throws Exception {
+        try {
+            Map<String, Object> props = new HashMap<>();
+            try {
+                OidcClientConfiguration oidcClientConfiguration = OidcClientConfigurationBuilder.build(oidcConfig);
+                assertEquals(OidcClientConfiguration.RelativeUrlsUsed.NEVER, oidcClientConfiguration.getRelativeUrls());
+
+                OidcClientContext oidcClientContext = new OidcClientContext(oidcClientConfiguration);
+                oidcFactory = new OidcMechanismFactory(oidcClientContext);
+                HttpServerAuthenticationMechanism mechanism;
+
+                if (oidcClientConfiguration.getAuthenticationRequestFormat().contains(REQUEST.getValue())) {
+                    mechanism = oidcFactory.createAuthenticationMechanism(OIDC_NAME, props, getCallbackHandler(true, "+phone+profile+email"));
+                } else {
+                    mechanism = oidcFactory.createAuthenticationMechanism(OIDC_NAME, props, getCallbackHandler());
+                }
+
+                URI requestUri = new URI(getClientUrl());
+                TestingHttpServerRequest request = new TestingHttpServerRequest(null, requestUri);
+                try {
+                    mechanism.evaluateRequest(request);
+                    Assert.fail("No error was thrown while attempting to evaluate the request");
+                } catch (Exception e) {
+
+                    if (requestObjectErrorType == RequestObjectErrorType.INVALID_ALGORITHM) {
+                        assertTrue(e.getMessage().contains("Failed to create the authentication request"));
+                    } else if (requestObjectErrorType == RequestObjectErrorType.MISSING_CLIENT_SECRET) {
+                        assertTrue(e.getMessage().contains("The client secret has not been configured."));
+                    } else {
+                        throw e;
+                    }
+                }
+            } catch (Exception e) {
+                if (requestObjectErrorType == RequestObjectErrorType.INVALID_REQUEST_FORMAT) {
+                    assertTrue(e.getMessage().contains("Authentication request format must be one of the following: oauth2, request, request_uri."));
+                } else if (requestObjectErrorType == RequestObjectErrorType.MISSING_ENC_VALUE) {
+                    assertTrue(e.getMessage().contains("Both request object encryption algorithm and request object content encryption algorithm must be configured to encrypt the request object."));
+                }
+            }
+        } finally {
+            client.setDispatcher(new QueueDispatcher());
+        }
+    }
+
+
     private InputStream getOidcConfigurationInputStream() {
         return getOidcConfigurationInputStream(CLIENT_SECRET);
     }
@@ -582,7 +737,6 @@ private InputStream getOidcConfigurationInputStreamWithTokenSignatureAlgorithm()
                 "}";
         return new ByteArrayInputStream(oidcConfig.getBytes(StandardCharsets.UTF_8));
     }
-
     private InputStream getOidcConfigurationInputStreamWithScope(String scopeValue){
         String oidcConfig = "{\n" +
                 "    \"client-id\" : \"" + CLIENT_ID + "\",\n" +
@@ -590,6 +744,25 @@ private InputStream getOidcConfigurationInputStreamWithScope(String scopeValue){
                 "    \"public-client\" : \"false\",\n" +
                 "    \"scope\" : \"" + scopeValue + "\",\n" +
                 "    \"ssl-required\" : \"EXTERNAL\",\n" +
+                "    \"ssl-required\" : \"EXTERNAL\",\n" +
+                "    \"credentials\" : {\n" +
+                "        \"secret\" : \"" + CLIENT_SECRET + "\"\n" +
+                "    }\n" +
+                "}";
+        return new ByteArrayInputStream(oidcConfig.getBytes(StandardCharsets.UTF_8));
+    }
+    private InputStream getOidcConfigurationInputStreamWithRequestParameter(String requestParameter, String signingAlgorithm, String encryptionAlgorithm, String encMethod){
+        String oidcConfig = "{\n" +
+                "    \"client-id\" : \"" + CLIENT_ID + "\",\n" +
+                "    \"provider-url\" : \"" + KEYCLOAK_CONTAINER.getAuthServerUrl() + "/realms/" + TEST_REALM_WITH_SCOPES + "/" + "\",\n" +
+                "    \"public-client\" : \"false\",\n" +
+                "    \"ssl-required\" : \"EXTERNAL\",\n" +
+                "    \"ssl-required\" : \"EXTERNAL\",\n" +
+                "    \"authentication-request-format\" : \"" + requestParameter + "\",\n" +
+                "    \"request-object-signing-algorithm\" : \"" + signingAlgorithm + "\",\n" +
+                "    \"request-object-encryption-alg-value\" : \"" + encryptionAlgorithm + "\",\n" +
+                "    \"request-object-encryption-enc-value\" : \"" + encMethod + "\",\n" +
+                "    \"scope\" : \"profile email phone\",\n" +
                 "    \"credentials\" : {\n" +
                 "        \"secret\" : \"" + CLIENT_SECRET + "\"\n" +
                 "    }\n" +
@@ -597,6 +770,59 @@ private InputStream getOidcConfigurationInputStreamWithScope(String scopeValue){
         return new ByteArrayInputStream(oidcConfig.getBytes(StandardCharsets.UTF_8));
     }
 
+    private InputStream getOidcConfigurationInputStreamWithoutEncValue(String requestParameter, String encryptionAlgorithm){
+        String oidcConfig = "{\n" +
+                "    \"client-id\" : \"" + CLIENT_ID + "\",\n" +
+                "    \"provider-url\" : \"" + KEYCLOAK_CONTAINER.getAuthServerUrl() + "/realms/" + TEST_REALM_WITH_SCOPES + "/" + "\",\n" +
+                "    \"public-client\" : \"false\",\n" +
+                "    \"ssl-required\" : \"EXTERNAL\",\n" +
+                "    \"ssl-required\" : \"EXTERNAL\",\n" +
+                "    \"authentication-request-format\" : \"" + requestParameter + "\",\n" +
+                "    \"request-object-encryption-alg-value\" : \"" + encryptionAlgorithm + "\",\n" +
+                "    \"scope\" : \"profile email phone\",\n" +
+                "    \"credentials\" : {\n" +
+                "        \"secret\" : \"" + CLIENT_SECRET + "\"\n" +
+                "    }\n" +
+                "}";
+        return new ByteArrayInputStream(oidcConfig.getBytes(StandardCharsets.UTF_8));
+    }
+
+    private InputStream getOidcConfigurationInputStreamWithRequestParameter(String requestParameter, String signingAlgorithm, String encryptionAlgorithm, String encMethod, String keyStorePath, String alias, String keyStoreType){
+        String oidcConfig = "{\n" +
+                "    \"client-id\" : \"" + CLIENT_ID + "\",\n" +
+                "    \"provider-url\" : \"" + KEYCLOAK_CONTAINER.getAuthServerUrl() + "/realms/" + TEST_REALM_WITH_SCOPES + "/" + "\",\n" +
+                "    \"public-client\" : \"false\",\n" +
+                "    \"ssl-required\" : \"EXTERNAL\",\n" +
+                "    \"authentication-request-format\" : \"" + requestParameter + "\",\n" +
+                "    \"request-object-signing-algorithm\" : \"" + signingAlgorithm + "\",\n" +
+                "    \"request-object-encryption-alg-value\" : \"" + encryptionAlgorithm + "\",\n" +
+                "    \"request-object-encryption-enc-value\" : \"" + encMethod + "\",\n" +
+                "    \"request-object-signing-keystore-file\" : \"" + keyStorePath + "\",\n" +
+                "    \"request-object-signing-keystore-type\" : \"" + keyStoreType + "\",\n" +
+                "    \"request-object-signing-keystore-password\" : \"" + KEYSTORE_PASS + "\",\n" +
+                "    \"request-object-signing-key-password\" : \"" + KEYSTORE_PASS + "\",\n" +
+                "    \"request-object-signing-key-alias\" : \"" + alias + "\",\n" +
+                "    \"scope\" : \"email phone profile\",\n" +
+                "    \"credentials\" : {\n" +
+                "        \"secret\" : \"" + CLIENT_SECRET + "\"\n" +
+                "    }\n" +
+                "}";
+        return new ByteArrayInputStream(oidcConfig.getBytes(StandardCharsets.UTF_8));
+    }
+
+    private InputStream getOidcConfigurationInputStreamWithRequestObjectPublicClient(String requestParameter, String signingAlgorithm){
+        String oidcConfig = "{\n" +
+                "    \"client-id\" : \"" + CLIENT_ID + "\",\n" +
+                "    \"provider-url\" : \"" + KEYCLOAK_CONTAINER.getAuthServerUrl() + "/realms/" + TEST_REALM_WITH_SCOPES + "/" + "\",\n" +
+                "    \"public-client\" : \"true\",\n" +
+                "    \"ssl-required\" : \"EXTERNAL\",\n" +
+                "    \"authentication-request-format\" : \"" + requestParameter + "\",\n" +
+                "    \"request-object-signing-algorithm\" : \"" + signingAlgorithm + "\",\n" +
+                "    \"scope\" : \"email phone profile\"\n" +
+                "}";
+        return new ByteArrayInputStream(oidcConfig.getBytes(StandardCharsets.UTF_8));
+    }
+
     private InputStream getOidcConfigurationInputStreamWithPrincipalAttribute(String principalAttributeValue) {
         String oidcConfig = "{\n" +
                 "    \"principal-attribute\" : \"" + principalAttributeValue + "\",\n" +
@@ -642,3 +868,4 @@ private static final String getClientPageTestForTenant(String tenant) {
         return tenant.equals(TENANT1_ENDPOINT) ? TENANT1_ENDPOINT : TENANT2_ENDPOINT + ":" + CLIENT_PAGE_TEXT;
     }
 }
+
diff --git a/pom.xml b/pom.xml
index 20543860b1e..e71dc5e2d33 100644
--- a/pom.xml
+++ b/pom.xml
@@ -99,6 +99,7 @@
         <version.io.rest-assured>4.3.3</version.io.rest-assured>
         <version.net.sourceforge.htmlunit.htmlunit>2.40.0</version.net.sourceforge.htmlunit.htmlunit>
         <version.org.apache.santuario>2.3.0</version.org.apache.santuario>
+        <version.org.keycloak.keycloak-services>3.1.0.Final</version.org.keycloak.keycloak-services>
 
         <test.level>INFO</test.level>
         <!-- Checkstyle configuration -->
@@ -1152,6 +1153,12 @@
                 <version>${version.org.bouncycastle}</version>
                 <scope>test</scope>
             </dependency>
+            <dependency>
+                <groupId>org.keycloak</groupId>
+                <artifactId>keycloak-services</artifactId>
+                <version>${version.org.keycloak.keycloak-services}</version>
+                <scope>test</scope>
+            </dependency>
 
             <!-- HSQL Database-->
             <dependency>