Repository: gravitee-io/graviteeio-access-management
Pull request #555 (2019-04-08 23:20:31)

Implement put dcr endpoint
Linked to issue https://github.com/gravitee-io/issues/issues/2134
I have included new OpenId Connect Discovery NRT on postman.


Patch:
diff --git a/.gitignore b/.gitignore
index 143f2ec8c3..e0fb9a8eee 100644
--- a/.gitignore
+++ b/.gitignore
@@ -19,3 +19,6 @@ gravitee-am-management-api/gravitee-am-management-api-standalone/gravitee-am-man
 
 # used in makefile as working directory
 .working
+
+# vert.x
+.vertx
diff --git a/docs/oidc-api-descriptor.yml b/docs/oidc-api-descriptor.yml
index 2d904f1714..cbc7ee3c50 100644
--- a/docs/oidc-api-descriptor.yml
+++ b/docs/oidc-api-descriptor.yml
@@ -170,7 +170,7 @@ paths:
       tags:
       - OpenID Connect
       summary: Patch a registred client.
-      description: Update information about a registred client.
+      description: Partially update information about a registred client.
       consumes:
       - application/json
       produces:
@@ -202,6 +202,42 @@ paths:
           description: Claims about the updated client. Please take into considerations that registration_access_token and registration_client_uri are renewed on each request, with as consequences to revoke previous values.
           schema:
             $ref: '#/definitions/ClientRegistrationResponse'
+    put:
+      tags:
+        - OpenID Connect
+      summary: Update a registred client.
+      description: Update information about a registred client.
+      consumes:
+        - application/json
+      produces:
+        - application/json
+      parameters:
+        - in: header
+          name: Authorization
+          description: Bearer token obtained on the register process through the registration_access_token property giving access only to one client matching the client_id path parameter. An admin token can be also obtained through the client crendentials flow with as mandatory scope "dcr_admin".
+          type: string
+          required: true
+        - in: path
+          name: client_id
+          type: string
+          required: true
+          description: ID of the client
+        - in: body
+          name: request
+          schema:
+            $ref: '#/definitions/ClientRegistrationRequest'
+          required: true
+      responses:
+        '400':
+          description: Invalid Request
+        '401':
+          description: Invalid Token
+        '403':
+          description: Access forbidden
+        '200':
+          description: Claims about the updated client. All missing fields will be set to null. Please take into considerations that registration_access_token and registration_client_uri are renewed on each request, with as consequences to revoke previous values.
+          schema:
+            $ref: '#/definitions/ClientRegistrationResponse'
     delete:
       tags:
       - OpenID Connect
diff --git a/gravitee-am-gateway/gravitee-am-gateway-handler/src/main/java/io/gravitee/am/gateway/handler/oidc/clientregistration/impl/DynamicClientRegistrationServiceImpl.java b/gravitee-am-gateway/gravitee-am-gateway-handler/src/main/java/io/gravitee/am/gateway/handler/oidc/clientregistration/impl/DynamicClientRegistrationServiceImpl.java
index 73803ac386..d104fbb30b 100644
--- a/gravitee-am-gateway/gravitee-am-gateway-handler/src/main/java/io/gravitee/am/gateway/handler/oidc/clientregistration/impl/DynamicClientRegistrationServiceImpl.java
+++ b/gravitee-am-gateway/gravitee-am-gateway-handler/src/main/java/io/gravitee/am/gateway/handler/oidc/clientregistration/impl/DynamicClientRegistrationServiceImpl.java
@@ -149,7 +149,7 @@ public class DynamicClientRegistrationServiceImpl implements DynamicClientRegist
      */
     @Override
     public Single<DynamicClientRegistrationRequest> validateClientRegistrationRequest(final DynamicClientRegistrationRequest request) {
-        LOGGER.debug("Validating dynamic client registration payload : creation");
+        LOGGER.debug("Validating dynamic client registration payload");
 
         if(request==null) {
             return Single.error(new InvalidClientMetadataException());
@@ -166,7 +166,7 @@ public class DynamicClientRegistrationServiceImpl implements DynamicClientRegist
 
     @Override
     public Single<DynamicClientRegistrationRequest> validateClientPatchRequest(DynamicClientRegistrationRequest request) {
-        LOGGER.debug("Validating dynamic client registration payload : update");
+        LOGGER.debug("Validating dynamic client registration payload : patch");
 
         if(request==null) {
             return Single.error(new InvalidClientMetadataException());
diff --git a/gravitee-am-gateway/gravitee-am-gateway-handler/src/main/java/io/gravitee/am/gateway/handler/oidc/request/DynamicClientRegistrationRequest.java b/gravitee-am-gateway/gravitee-am-gateway-handler/src/main/java/io/gravitee/am/gateway/handler/oidc/request/DynamicClientRegistrationRequest.java
index 2cf113020d..aa4e41e1a0 100644
--- a/gravitee-am-gateway/gravitee-am-gateway-handler/src/main/java/io/gravitee/am/gateway/handler/oidc/request/DynamicClientRegistrationRequest.java
+++ b/gravitee-am-gateway/gravitee-am-gateway-handler/src/main/java/io/gravitee/am/gateway/handler/oidc/request/DynamicClientRegistrationRequest.java
@@ -475,5 +475,48 @@ public class DynamicClientRegistrationRequest {
 
         return client;
     }
+
+    public Client update(Client client) {
+        /* set openid request metadata */
+        SetterUtils.set(client::setRedirectUris, this.getRedirectUris());
+        SetterUtils.set(client::setResponseTypes, this.getResponseTypes());
+        SetterUtils.set(client::setAuthorizedGrantTypes, this.getGrantTypes());
+        SetterUtils.set(client::setApplicationType, this.getApplicationType());
+        SetterUtils.set(client::setContacts, this.getContacts());
+        SetterUtils.set(client::setClientName, this.getClientName());
+        SetterUtils.set(client::setLogoUri, this.getLogoUri());
+        SetterUtils.set(client::setClientUri, this.getClientUri());
+        SetterUtils.set(client::setPolicyUri, this.getPolicyUri());
+        SetterUtils.set(client::setTosUri, this.getTosUri());
+
+        SetterUtils.set(client::setJwksUri, this.getJwksUri());
+        SetterUtils.set(client::setJwks, this.getJwks());
+        SetterUtils.set(client::setSectorIdentifierUri, this.getSectorIdentifierUri());
+        SetterUtils.set(client::setSubjectType, this.getSubjectType());
+        SetterUtils.set(client::setIdTokenSignedResponseAlg, this.getIdTokenSignedResponseAlg());
+        SetterUtils.set(client::setIdTokenEncryptedResponseAlg, this.getIdTokenEncryptedResponseAlg());
+        SetterUtils.set(client::setIdTokenEncryptedResponseEnc, this.getIdTokenEncryptedResponseEnc());
+        SetterUtils.set(client::setUserinfoSignedResponseAlg, this.getUserinfoSignedResponseAlg());
+        SetterUtils.set(client::setUserinfoEncryptedResponseAlg, this.getUserinfoEncryptedResponseAlg());
+        SetterUtils.set(client::setUserinfoEncryptedResponseEnc, this.getUserinfoEncryptedResponseEnc());
+        SetterUtils.set(client::setRequestObjectSigningAlg, this.getRequestObjectSigningAlg());
+        SetterUtils.set(client::setRequestObjectEncryptionAlg, this.getRequestObjectEncryptionAlg());
+        SetterUtils.set(client::setRequestObjectEncryptionEnc, this.getRequestObjectEncryptionEnc());
+        SetterUtils.set(client::setTokenEndpointAuthMethod, this.getTokenEndpointAuthMethod());
+        SetterUtils.set(client::setTokenEndpointAuthSigningAlg, this.getTokenEndpointAuthSigningAlg());
+        SetterUtils.set(client::setDefaultMaxAge, this.getDefaultMaxAge());
+        SetterUtils.set(client::setRequireAuthTime, this.getRequireAuthTime());
+        SetterUtils.set(client::setDefaultACRvalues, this.getDefaultACRvalues());
+        SetterUtils.set(client::setInitiateLoginUri, this.getInitiateLoginUri());
+        SetterUtils.set(client::setRequestUris, this.getRequestUris());
+
+        /* set oauth2 request metadata */
+        SetterUtils.set(client::setScopes, this.getScope());
+        SetterUtils.set(client::setSoftwareId, this.getSoftwareId());
+        SetterUtils.set(client::setSoftwareVersion, this.getSoftwareVersion());
+        SetterUtils.set(client::setSoftwareStatement, this.getSoftwareStatement());
+
+        return client;
+    }
 }
 
diff --git a/gravitee-am-gateway/gravitee-am-gateway-handler/src/main/java/io/gravitee/am/gateway/handler/vertx/handler/oidc/OIDCRouter.java b/gravitee-am-gateway/gravitee-am-gateway-handler/src/main/java/io/gravitee/am/gateway/handler/vertx/handler/oidc/OIDCRouter.java
index fe2fec8f56..faf1490888 100644
--- a/gravitee-am-gateway/gravitee-am-gateway-handler/src/main/java/io/gravitee/am/gateway/handler/vertx/handler/oidc/OIDCRouter.java
+++ b/gravitee-am-gateway/gravitee-am-gateway-handler/src/main/java/io/gravitee/am/gateway/handler/vertx/handler/oidc/OIDCRouter.java
@@ -139,6 +139,11 @@ public class OIDCRouter {
                 .consumes(MediaType.APPLICATION_JSON)
                 .handler(dynamicClientAccessHandler)
                 .handler(dynamicClientAccessEndpoint::patch);
+        router
+                .route(HttpMethod.PUT, "/register/:"+CLIENT_ID)
+                .consumes(MediaType.APPLICATION_JSON)
+                .handler(dynamicClientAccessHandler)
+                .handler(dynamicClientAccessEndpoint::update);
         router
                 .route(HttpMethod.DELETE, "/register/:"+CLIENT_ID)
                 .handler(dynamicClientAccessHandler)
diff --git a/gravitee-am-gateway/gravitee-am-gateway-handler/src/main/java/io/gravitee/am/gateway/handler/vertx/handler/oidc/endpoint/DynamicClientAccessEndpoint.java b/gravitee-am-gateway/gravitee-am-gateway-handler/src/main/java/io/gravitee/am/gateway/handler/vertx/handler/oidc/endpoint/DynamicClientAccessEndpoint.java
index bbe22bbca5..47e7a342d6 100644
--- a/gravitee-am-gateway/gravitee-am-gateway-handler/src/main/java/io/gravitee/am/gateway/handler/vertx/handler/oidc/endpoint/DynamicClientAccessEndpoint.java
+++ b/gravitee-am-gateway/gravitee-am-gateway-handler/src/main/java/io/gravitee/am/gateway/handler/vertx/handler/oidc/endpoint/DynamicClientAccessEndpoint.java
@@ -81,7 +81,7 @@ public class DynamicClientAccessEndpoint extends DynamicClientRegistrationEndpoi
      * @param context
      */
     public void patch(RoutingContext context) {
-        LOGGER.debug("Dynamic client registration UPDATE endpoint");
+        LOGGER.debug("Dynamic client registration PATCH endpoint");
 
         this.getClient(context)
                 .flatMapSingle(Single::just)
@@ -103,6 +103,33 @@ public class DynamicClientAccessEndpoint extends DynamicClientRegistrationEndpoi
                 );
     }
 
+    /**
+     * Update/Override client_metadata.
+     * @param context
+     */
+    public void update(RoutingContext context) {
+        LOGGER.debug("Dynamic client registration UPDATE endpoint");
+
+        this.getClient(context)
+                .flatMapSingle(Single::just)
+                .flatMap(client -> this.extractRequest(context)
+                        .flatMap(dcrService::validateClientRegistrationRequest)
+                        .map(request -> request.update(client))
+                        .flatMap(updatedClient -> dcrService.applyRegistrationAccessToken(extractBasePath(context), updatedClient))
+                        .flatMap(clientService::update)
+                        .map(clientSyncService::addDynamicClientRegistred)
+                )
+                .subscribe(
+                        client -> context.response()
+                                .putHeader(HttpHeaders.CACHE_CONTROL, "no-store")
+                                .putHeader(HttpHeaders.PRAGMA, "no-cache")
+                                .putHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON)
+                                .setStatusCode(HttpStatusCode.OK_200)
+                                .end(Json.encodePrettily(DynamicClientRegistrationResponse.fromClient(client)))
+                        , error -> context.fail(error)
+                );
+    }
+
     /**
      * Delete client
      * @param context
@@ -124,6 +151,7 @@ public class DynamicClientAccessEndpoint extends DynamicClientRegistrationEndpoi
         String clientId = context.request().getParam("client_id");
 
         return this.clientSyncService.findByDomainAndClientId(domain,clientId)
+                .map(Client::clone)
                 .switchIfEmpty(Maybe.error(new OAuth2Exception() {
                     @Override
                     public int getHttpStatusCode() {
diff --git a/gravitee-am-model/src/main/java/io/gravitee/am/model/Client.java b/gravitee-am-model/src/main/java/io/gravitee/am/model/Client.java
index b75827bfe2..863a7610e5 100644
--- a/gravitee-am-model/src/main/java/io/gravitee/am/model/Client.java
+++ b/gravitee-am-model/src/main/java/io/gravitee/am/model/Client.java
@@ -24,6 +24,7 @@ import io.gravitee.am.model.oidc.JWKSet;
 import java.time.ZoneId;
 import java.time.ZonedDateTime;
 import java.util.*;
+import java.util.stream.Collectors;
 
 /**
  * @author David BRASSELY (david.brassely at graviteesource.com)
@@ -32,7 +33,7 @@ import java.util.*;
  * See https://openid.net/specs/openid-connect-registration-1_0.html#ClientMetadata
  */
 
-public class Client {
+public class Client implements Cloneable{
 
     public final static int DEFAULT_ACCESS_TOKEN_VALIDITY_SECONDS = 7200;
     public final static int DEFAULT_REFRESH_TOKEN_VALIDITY_SECONDS = 14400;
@@ -618,4 +619,25 @@ public class Client {
     public int hashCode() {
         return Objects.hash(id);
     }
+
+    @Override
+    public Client clone() throws CloneNotSupportedException {
+        Client clone = (Client) super.clone();
+
+        clone.setRedirectUris(this.getRedirectUris()!=null?new ArrayList<>(this.getRedirectUris()):null);
+        clone.setAuthorizedGrantTypes(this.getAuthorizedGrantTypes()!=null?new ArrayList<>(this.getAuthorizedGrantTypes()):null);
+        clone.setResponseTypes(this.getResponseTypes()!=null?new ArrayList<>(this.getResponseTypes()):null);
+        clone.setContacts(this.getContacts()!=null?new ArrayList<>(this.getContacts()):null);
+        clone.setDefaultACRvalues(this.getDefaultACRvalues()!=null?new ArrayList<>(this.getDefaultACRvalues()):null);
+        clone.setRequestUris(this.getRequestUris()!=null?new ArrayList<>(this.getRequestUris()):null);
+        clone.setScopes(this.getScopes()!=null?new ArrayList<>(this.getScopes()):null);
+        clone.setScopeApprovals(this.getScopeApprovals()!=null?new HashMap<>(this.getScopeApprovals()):null);
+        clone.setAutoApproveScopes(this.getAutoApproveScopes()!=null?new ArrayList<>(this.getAutoApproveScopes()):null);
+        clone.setIdentities(this.getIdentities()!=null?new HashSet<>(this.getIdentities()):null);
+        clone.setOauth2Identities(this.getOauth2Identities()!=null?new HashSet<>(this.getOauth2Identities()):null);
+        clone.setIdTokenCustomClaims(this.getIdTokenCustomClaims()!=null?new HashMap<>(this.getIdTokenCustomClaims()):null);
+        clone.setJwks(this.getJwks()!=null?this.getJwks().clone():null);
+
+        return clone;
+    }
 }
diff --git a/gravitee-am-model/src/main/java/io/gravitee/am/model/oidc/JWKSet.java b/gravitee-am-model/src/main/java/io/gravitee/am/model/oidc/JWKSet.java
index e948e9558b..9cd1135a71 100644
--- a/gravitee-am-model/src/main/java/io/gravitee/am/model/oidc/JWKSet.java
+++ b/gravitee-am-model/src/main/java/io/gravitee/am/model/oidc/JWKSet.java
@@ -17,9 +17,10 @@ package io.gravitee.am.model.oidc;
 
 import io.gravitee.am.model.jose.JWK;
 
+import java.util.ArrayList;
 import java.util.List;
 
-public class JWKSet {
+public class JWKSet implements Cloneable{
     private List<JWK> keys;
 
     public List<JWK> getKeys() {
@@ -29,4 +30,11 @@ public class JWKSet {
     public void setKeys(List<JWK> keys) {
         this.keys = keys;
     }
+
+    @Override
+    public JWKSet clone() throws CloneNotSupportedException {
+        JWKSet clone = (JWKSet) super.clone();
+        clone.setKeys(this.getKeys()!=null?new ArrayList<>(this.getKeys()):null);
+        return clone;
+    }
 }
diff --git a/gravitee-am-service/src/main/java/io/gravitee/am/service/utils/SetterUtils.java b/gravitee-am-service/src/main/java/io/gravitee/am/service/utils/SetterUtils.java
index 6a9b920da5..1192671b0d 100644
--- a/gravitee-am-service/src/main/java/io/gravitee/am/service/utils/SetterUtils.java
+++ b/gravitee-am-service/src/main/java/io/gravitee/am/service/utils/SetterUtils.java
@@ -114,4 +114,12 @@ public class SetterUtils {
         LOGGER.warn("should never happen except if class is not a primitive:" + primitive.isPrimitive());
         return null;
     }
+
+    public static<T> void set(final Consumer<T> setter, final Optional<T> value) {
+        if(value == null) {
+            setter.accept(null);
+        } else {
+            setter.accept(value.orElse(null));
+        }
+    }
 }
diff --git a/postman/graviteeam-collection.json b/postman/graviteeam-collection.json
index d22c5a2d9a..94bc0c219f 100644
--- a/postman/graviteeam-collection.json
+++ b/postman/graviteeam-collection.json
@@ -1,6 +1,6 @@
 {
 	"info": {
-		"_postman_id": "8a6e3a44-8fd6-4221-9482-5e3771c7a545",
+		"_postman_id": "2c384dba-a62c-4505-b7fb-a2c1b78e4e93",
 		"name": "Gravitee.io AM",
 		"schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json"
 	},
@@ -11643,6 +11643,93 @@
 		{
 			"name": "OIDC",
 			"item": [
+				{
+					"name": "Discovery",
+					"item": [
+						{
+							"name": "well-known/openid-configuration",
+							"event": [
+								{
+									"listen": "test",
+									"script": {
+										"id": "5332b6f5-7419-4792-a30f-cce968d3d67e",
+										"exec": [
+											"pm.test(\"Status code is 200\", function () {",
+											"    pm.response.to.have.status(200);",
+											"});",
+											"",
+											"pm.test(\"Check discovery endpoints\", function () {",
+											"    pm.response.to.be.header('Content-Type', 'application/json');",
+											"    var body = pm.response.json();",
+											"    ",
+											"    pm.expect(body).to.have.property(\"authorization_endpoint\");",
+											"    pm.expect(body).to.have.property(\"token_endpoint\");",
+											"    pm.expect(body).to.have.property(\"userinfo_endpoint\");",
+											"    pm.expect(body).to.have.property(\"jwks_uri\");",
+											"    pm.expect(body).to.have.property(\"end_session_endpoint\");",
+											"    pm.expect(body).to.have.property(\"revocation_endpoint\");",
+											"    pm.expect(body).to.have.property(\"introspection_endpoint\");",
+											"    pm.expect(body).to.have.property(\"registration_endpoint\");",
+											"    ",
+											"    pm.environment.set('authorizationEndpoint', body.authorization_endpoint);",
+											"    pm.environment.set('tokenEndpoint', body.token_endpoint);",
+											"    pm.environment.set('userinfoEndpoint', body.userinfo_endpoint);",
+											"    pm.environment.set('jwksUriEndpoint', body.jwks_uri);",
+											"    pm.environment.set('logoutEndpoint', body.end_session_endpoint);",
+											"    pm.environment.set('revocationEndpoint', body.revocation_endpoint);",
+											"    pm.environment.set('introspectionEndpoint', body.introspection_endpoint);",
+											"    pm.environment.set('registrationEndpoint', body.registration_endpoint);",
+											"});",
+											"",
+											"pm.test(\"Check discovery properties\", function () {",
+											"    var body = pm.response.json();",
+											"    pm.expect(body).to.have.property(\"issuer\");",
+											"",
+											"    pm.expect(body).to.have.property(\"scopes_supported\");",
+											"    pm.expect(body).to.have.property(\"response_types_supported\");",
+											"    pm.expect(body).to.have.property(\"subject_types_supported\");",
+											"    pm.expect(body).to.have.property(\"id_token_signing_alg_values_supported\");",
+											"    pm.expect(body).to.have.property(\"token_endpoint_auth_methods_supported\");",
+											"    pm.expect(body).to.have.property(\"claim_types_supported\");",
+											"    pm.expect(body).to.have.property(\"claims_supported\");",
+											"    pm.expect(body).to.have.property(\"code_challenge_methods_supported\");",
+											"    pm.expect(body).to.have.property(\"claims_parameter_supported\");",
+											"    pm.expect(body).to.have.property(\"request_parameter_supported\");",
+											"    pm.expect(body).to.have.property(\"request_uri_parameter_supported\");",
+											"    pm.expect(body).to.have.property(\"require_request_uri_registration\");",
+											"    ",
+											"    pm.expect(body.code_challenge_methods_supported).to.eql([ 'plain','S256' ]);",
+											"});"
+										],
+										"type": "text/javascript"
+									}
+								}
+							],
+							"request": {
+								"method": "GET",
+								"header": [],
+								"body": {
+									"mode": "raw",
+									"raw": ""
+								},
+								"url": {
+									"raw": "{{gateway_url}}/test/oidc/.well-known/openid-configuration",
+									"host": [
+										"{{gateway_url}}"
+									],
+									"path": [
+										"test",
+										"oidc",
+										".well-known",
+										"openid-configuration"
+									]
+								}
+							},
+							"response": []
+						}
+					],
+					"_postman_isSubFolder": true
+				},
 				{
 					"name": "ID Token",
 					"item": [
@@ -11723,14 +11810,9 @@
 											]
 										},
 										"url": {
-											"raw": "{{gateway_url}}/test/oauth/token",
+											"raw": "{{tokenEndpoint}}",
 											"host": [
-												"{{gateway_url}}"
-											],
-											"path": [
-												"test",
-												"oauth",
-												"token"
+												"{{tokenEndpoint}}"
 											]
 										}
 									},
@@ -11831,14 +11913,9 @@
 											]
 										},
 										"url": {
-											"raw": "{{gateway_url}}/test/oauth/token",
+											"raw": "{{tokenEndpoint}}",
 											"host": [
-												"{{gateway_url}}"
-											],
-											"path": [
-												"test",
-												"oauth",
-												"token"
+												"{{tokenEndpoint}}"
 											]
 										}
 									},
@@ -11895,14 +11972,9 @@
 											]
 										},
 										"url": {
-											"raw": "{{gateway_url}}/test/oauth/revoke",
+											"raw": "{{revocationEndpoint}}",
 											"host": [
-												"{{gateway_url}}"
-											],
-											"path": [
-												"test",
-												"oauth",
-												"revoke"
+												"{{revocationEndpoint}}"
 											]
 										}
 									},
@@ -12009,14 +12081,9 @@
 											]
 										},
 										"url": {
-											"raw": "{{gateway_url}}/test/oauth/token",
+											"raw": "{{tokenEndpoint}}",
 											"host": [
-												"{{gateway_url}}"
-											],
-											"path": [
-												"test",
-												"oauth",
-												"token"
+												"{{tokenEndpoint}}"
 											]
 										}
 									},
@@ -12073,14 +12140,9 @@
 											]
 										},
 										"url": {
-											"raw": "{{gateway_url}}/test/oauth/revoke",
+											"raw": "{{revocationEndpoint}}",
 											"host": [
-												"{{gateway_url}}"
-											],
-											"path": [
-												"test",
-												"oauth",
-												"revoke"
+												"{{revocationEndpoint}}"
 											]
 										}
 									},
@@ -12187,14 +12249,9 @@
 											]
 										},
 										"url": {
-											"raw": "{{gateway_url}}/test/oauth/token",
+											"raw": "{{tokenEndpoint}}",
 											"host": [
-												"{{gateway_url}}"
-											],
-											"path": [
-												"test",
-												"oauth",
-												"token"
+												"{{tokenEndpoint}}"
 											]
 										}
 									},
@@ -12251,14 +12308,9 @@
 											]
 										},
 										"url": {
-											"raw": "{{gateway_url}}/test/oauth/revoke",
+											"raw": "{{revocationEndpoint}}",
 											"host": [
-												"{{gateway_url}}"
-											],
-											"path": [
-												"test",
-												"oauth",
-												"revoke"
+												"{{revocationEndpoint}}"
 											]
 										}
 									},
@@ -12387,14 +12439,9 @@
 											]
 										},
 										"url": {
-											"raw": "{{gateway_url}}/test/oauth/token",
+											"raw": "{{tokenEndpoint}}",
 											"host": [
-												"{{gateway_url}}"
-											],
-											"path": [
-												"test",
-												"oauth",
-												"token"
+												"{{tokenEndpoint}}"
 											]
 										}
 									},
@@ -12499,14 +12546,9 @@
 											]
 										},
 										"url": {
-											"raw": "{{gateway_url}}/test/oauth/token",
+											"raw": "{{tokenEndpoint}}",
 											"host": [
-												"{{gateway_url}}"
-											],
-											"path": [
-												"test",
-												"oauth",
-												"token"
+												"{{tokenEndpoint}}"
 											]
 										}
 									},
@@ -12563,14 +12605,9 @@
 											]
 										},
 										"url": {
-											"raw": "{{gateway_url}}/test/oauth/revoke",
+											"raw": "{{revocationEndpoint}}",
 											"host": [
-												"{{gateway_url}}"
-											],
-											"path": [
-												"test",
-												"oauth",
-												"revoke"
+												"{{revocationEndpoint}}"
 											]
 										}
 									},
@@ -12627,14 +12664,9 @@
 											]
 										},
 										"url": {
-											"raw": "{{gateway_url}}/test/oauth/revoke",
+											"raw": "{{revocationEndpoint}}",
 											"host": [
-												"{{gateway_url}}"
-											],
-											"path": [
-												"test",
-												"oauth",
-												"revoke"
+												"{{revocationEndpoint}}"
 											]
 										}
 									},
@@ -12737,14 +12769,9 @@
 											"raw": ""
 										},
 										"url": {
-											"raw": "{{gateway_url}}/test/oidc/userinfo",
+											"raw": "{{userinfoEndpoint}}",
 											"host": [
-												"{{gateway_url}}"
-											],
-											"path": [
-												"test",
-												"oidc",
-												"userinfo"
+												"{{userinfoEndpoint}}"
 											]
 										}
 									},
@@ -12797,14 +12824,9 @@
 											"raw": ""
 										},
 										"url": {
-											"raw": "{{gateway_url}}/test/oidc/userinfo",
+											"raw": "{{userinfoEndpoint}}",
 											"host": [
-												"{{gateway_url}}"
-											],
-											"path": [
-												"test",
-												"oidc",
-												"userinfo"
+												"{{userinfoEndpoint}}"
 											]
 										}
 									},
@@ -12857,14 +12879,9 @@
 											"raw": ""
 										},
 										"url": {
-											"raw": "{{gateway_url}}/test/oidc/userinfo",
+											"raw": "{{userinfoEndpoint}}",
 											"host": [
-												"{{gateway_url}}"
-											],
-											"path": [
-												"test",
-												"oidc",
-												"userinfo"
+												"{{userinfoEndpoint}}"
 											]
 										}
 									},
@@ -12961,14 +12978,9 @@
 											]
 										},
 										"url": {
-											"raw": "{{gateway_url}}/test/oauth/token",
+											"raw": "{{tokenEndpoint}}",
 											"host": [
-												"{{gateway_url}}"
-											],
-											"path": [
-												"test",
-												"oauth",
-												"token"
+												"{{tokenEndpoint}}"
 											]
 										}
 									},
@@ -13028,14 +13040,9 @@
 											"raw": ""
 										},
 										"url": {
-											"raw": "{{gateway_url}}/test/oidc/userinfo",
+											"raw": "{{userinfoEndpoint}}",
 											"host": [
-												"{{gateway_url}}"
-											],
-											"path": [
-												"test",
-												"oidc",
-												"userinfo"
+												"{{userinfoEndpoint}}"
 											]
 										}
 									},
@@ -13115,14 +13122,9 @@
 											]
 										},
 										"url": {
-											"raw": "{{gateway_url}}/test/oidc/userinfo",
+											"raw": "{{userinfoEndpoint}}",
 											"host": [
-												"{{gateway_url}}"
-											],
-											"path": [
-												"test",
-												"oidc",
-												"userinfo"
+												"{{userinfoEndpoint}}"
 											]
 										}
 									},
@@ -13179,14 +13181,9 @@
 											]
 										},
 										"url": {
-											"raw": "{{gateway_url}}/test/oauth/revoke",
+											"raw": "{{revocationEndpoint}}",
 											"host": [
-												"{{gateway_url}}"
-											],
-											"path": [
-												"test",
-												"oauth",
-												"revoke"
+												"{{revocationEndpoint}}"
 											]
 										}
 									},
@@ -13272,14 +13269,9 @@
 											]
 										},
 										"url": {
-											"raw": "{{gateway_url}}/test/oauth/token",
+											"raw": "{{tokenEndpoint}}",
 											"host": [
-												"{{gateway_url}}"
-											],
-											"path": [
-												"test",
-												"oauth",
-												"token"
+												"{{tokenEndpoint}}"
 											]
 										}
 									},
@@ -13331,14 +13323,9 @@
 											"raw": ""
 										},
 										"url": {
-											"raw": "{{gateway_url}}/test/oidc/userinfo",
+											"raw": "{{userinfoEndpoint}}",
 											"host": [
-												"{{gateway_url}}"
-											],
-											"path": [
-												"test",
-												"oidc",
-												"userinfo"
+												"{{userinfoEndpoint}}"
 											]
 										}
 									},
@@ -13411,14 +13398,9 @@
 											]
 										},
 										"url": {
-											"raw": "{{gateway_url}}/test/oidc/userinfo",
+											"raw": "{{userinfoEndpoint}}",
 											"host": [
-												"{{gateway_url}}"
-											],
-											"path": [
-												"test",
-												"oidc",
-												"userinfo"
+												"{{userinfoEndpoint}}"
 											]
 										}
 									},
@@ -13506,14 +13488,9 @@
 											"raw": ""
 										},
 										"url": {
-											"raw": "{{gateway_url}}/test/oauth/authorize?response_type=id_token token",
+											"raw": "{{authorizationEndpoint}}?response_type=id_token token",
 											"host": [
-												"{{gateway_url}}"
-											],
-											"path": [
-												"test",
-												"oauth",
-												"authorize"
+												"{{authorizationEndpoint}}"
 											],
 											"query": [
 												{
@@ -13573,14 +13550,9 @@
 											"raw": ""
 										},
 										"url": {
-											"raw": "{{gateway_url}}/test/oauth/authorize?response_type=id_token token&client_id=my-client2&redirect_uri=http://localhost:4000/",
+											"raw": "{{authorizationEndpoint}}?response_type=id_token token&client_id=my-client2&redirect_uri=http://localhost:4000/",
 											"host": [
-												"{{gateway_url}}"
-											],
-											"path": [
-												"test",
-												"oauth",
-												"authorize"
+												"{{authorizationEndpoint}}"
 											],
 											"query": [
 												{
@@ -13648,14 +13620,9 @@
 											"raw": ""
 										},
 										"url": {
-											"raw": "{{gateway_url}}/test/oauth/authorize?response_type=id_token token&client_id=my-client&nonce=n-0S6_WzA2Mj",
+											"raw": "{{authorizationEndpoint}}?response_type=id_token token&client_id=my-client&nonce=n-0S6_WzA2Mj",
 											"host": [
-												"{{gateway_url}}"
-											],
-											"path": [
-												"test",
-												"oauth",
-												"authorize"
+												"{{authorizationEndpoint}}"
 											],
 											"query": [
 												{
@@ -13749,14 +13716,9 @@
 											"raw": ""
 										},
 										"url": {
-											"raw": "{{gateway_url}}/test/oauth/authorize?response_type=id_token token&client_id=my-client2&redirect_uri=http://localhost:4000/&nonce=n-0S6_WzA2Mj",
+											"raw": "{{authorizationEndpoint}}?response_type=id_token token&client_id=my-client2&redirect_uri=http://localhost:4000/&nonce=n-0S6_WzA2Mj",
 											"host": [
-												"{{gateway_url}}"
-											],
-											"path": [
-												"test",
-												"oauth",
-												"authorize"
+												"{{authorizationEndpoint}}"
 											],
 											"query": [
 												{
@@ -14052,13 +14014,9 @@
 											]
 										},
 										"url": {
-											"raw": "{{gateway_url}}/test/logout",
+											"raw": "{{logoutEndpoint}}",
 											"host": [
-												"{{gateway_url}}"
-											],
-											"path": [
-												"test",
-												"logout"
+												"{{logoutEndpoint}}"
 											]
 										},
 										"description": "The client does not have a redirect_uri define"
@@ -14139,14 +14097,9 @@
 											"raw": ""
 										},
 										"url": {
-											"raw": "{{gateway_url}}/test/oauth/authorize?response_type=id_token&client_id=my-client2&redirect_uri=http://localhost:4000/&nonce=n-0S6_WzA2Mj",
+											"raw": "{{authorizationEndpoint}}?response_type=id_token&client_id=my-client2&redirect_uri=http://localhost:4000/&nonce=n-0S6_WzA2Mj",
 											"host": [
-												"{{gateway_url}}"
-											],
-											"path": [
-												"test",
-												"oauth",
-												"authorize"
+												"{{authorizationEndpoint}}"
 											],
 											"query": [
 												{
@@ -14440,13 +14393,9 @@
 											]
 										},
 										"url": {
-											"raw": "{{gateway_url}}/test/logout",
+											"raw": "{{logoutEndpoint}}",
 											"host": [
-												"{{gateway_url}}"
-											],
-											"path": [
-												"test",
-												"logout"
+												"{{logoutEndpoint}}"
 											]
 										},
 										"description": "The client does not have a redirect_uri define"
@@ -14527,14 +14476,9 @@
 											"raw": ""
 										},
 										"url": {
-											"raw": "{{gateway_url}}/test/oauth/authorize?response_type=id_token&client_id=my-client2&redirect_uri=http://localhost:4000/&nonce=n-0S6_WzA2Mj&state=af0ifjsldkj",
+											"raw": "{{authorizationEndpoint}}?response_type=id_token&client_id=my-client2&redirect_uri=http://localhost:4000/&nonce=n-0S6_WzA2Mj&state=af0ifjsldkj",
 											"host": [
-												"{{gateway_url}}"
-											],
-											"path": [
-												"test",
-												"oauth",
-												"authorize"
+												"{{authorizationEndpoint}}"
 											],
 											"query": [
 												{
@@ -14821,13 +14765,9 @@
 											]
 										},
 										"url": {
-											"raw": "{{gateway_url}}/test/logout",
+											"raw": "{{logoutEndpoint}}",
 											"host": [
-												"{{gateway_url}}"
-											],
-											"path": [
-												"test",
-												"logout"
+												"{{logoutEndpoint}}"
 											]
 										},
 										"description": "The client does not have a redirect_uri define"
@@ -14908,14 +14848,9 @@
 											"raw": ""
 										},
 										"url": {
-											"raw": "{{gateway_url}}/test/oauth/authorize?response_type=id_token token&client_id=my-client2&redirect_uri=http://localhost:4000/&nonce=n-0S6_WzA2Mj&state=af0ifjsldkj",
+											"raw": "{{authorizationEndpoint}}?response_type=id_token token&client_id=my-client2&redirect_uri=http://localhost:4000/&nonce=n-0S6_WzA2Mj&state=af0ifjsldkj",
 											"host": [
-												"{{gateway_url}}"
-											],
-											"path": [
-												"test",
-												"oauth",
-												"authorize"
+												"{{authorizationEndpoint}}"
 											],
 											"query": [
 												{
@@ -15202,13 +15137,9 @@
 											]
 										},
 										"url": {
-											"raw": "{{gateway_url}}/test/logout",
+											"raw": "{{logoutEndpoint}}",
 											"host": [
-												"{{gateway_url}}"
-											],
-											"path": [
-												"test",
-												"logout"
+												"{{logoutEndpoint}}"
 											]
 										},
 										"description": "The client does not have a redirect_uri define"
@@ -15296,14 +15227,9 @@
 											"raw": ""
 										},
 										"url": {
-											"raw": "{{gateway_url}}/test/oauth/authorize?response_type=code",
+											"raw": "{{authorizationEndpoint}}?response_type=code",
 											"host": [
-												"{{gateway_url}}"
-											],
-											"path": [
-												"test",
-												"oauth",
-												"authorize"
+												"{{authorizationEndpoint}}"
 											],
 											"query": [
 												{
@@ -15363,14 +15289,9 @@
 											"raw": ""
 										},
 										"url": {
-											"raw": "{{gateway_url}}/test/oauth/authorize?response_type=code&client_id=my-client",
+											"raw": "{{authorizationEndpoint}}?response_type=code&client_id=my-client",
 											"host": [
-												"{{gateway_url}}"
-											],
-											"path": [
-												"test",
-												"oauth",
-												"authorize"
+												"{{authorizationEndpoint}}"
 											],
 											"query": [
 												{
@@ -15457,14 +15378,9 @@
 											"raw": ""
 										},
 										"url": {
-											"raw": "{{gateway_url}}/test/oidc/register",
+											"raw": "{{registrationEndpoint}}",
 											"host": [
-												"{{gateway_url}}"
-											],
-											"path": [
-												"test",
-												"oidc",
-												"register"
+												"{{registrationEndpoint}}"
 											]
 										}
 									},
@@ -15695,14 +15611,9 @@
 											]
 										},
 										"url": {
-											"raw": "{{gateway_url}}/test/oauth/token",
+											"raw": "{{tokenEndpoint}}",
 											"host": [
-												"{{gateway_url}}"
-											],
-											"path": [
-												"test",
-												"oauth",
-												"token"
+												"{{tokenEndpoint}}"
 											]
 										}
 									},
@@ -15750,14 +15661,9 @@
 											"raw": "{\n  \"redirect_uris\": [\n    \"https://client.example.org/callback\",\n    \"https://client.example.org/callback2\"],\n  \"client_name\": \"My Example Client\",\n  \"token_endpoint_auth_method\": \"client_secret_basic\",\n  \"logo_uri\": \"https://client.example.org/logo.png\",\n  \"jwks_uri\": \"https://client.example.org/my_public_keys.jwks\"\n}"
 										},
 										"url": {
-											"raw": "{{gateway_url}}/test/oidc/register",
+											"raw": "{{registrationEndpoint}}",
 											"host": [
-												"{{gateway_url}}"
-											],
-											"path": [
-												"test",
-												"oidc",
-												"register"
+												"{{registrationEndpoint}}"
 											]
 										}
 									},
@@ -15883,14 +15789,9 @@
 											]
 										},
 										"url": {
-											"raw": "{{gateway_url}}/test/oauth/token",
+											"raw": "{{tokenEndpoint}}",
 											"host": [
-												"{{gateway_url}}"
-											],
-											"path": [
-												"test",
-												"oauth",
-												"token"
+												"{{tokenEndpoint}}"
 											]
 										}
 									},
@@ -15900,7 +15801,7 @@
 							"_postman_isSubFolder": true
 						},
 						{
-							"name": "Invalid requests",
+							"name": "Invalid creation requests",
 							"item": [
 								{
 									"name": "Invalid access token",
@@ -15944,14 +15845,9 @@
 											"raw": "{\n  \"redirect_uris\": [\n    \"https://client.example.org/callback\",\n    \"https://client.example.org/callback2\"],\n  \"client_name\": \"My Example Client\",\n  \"token_endpoint_auth_method\": \"client_secret_basic\",\n  \"logo_uri\": \"https://client.example.org/logo.png\",\n  \"jwks_uri\": \"https://client.example.org/my_public_keys.jwks\"\n}"
 										},
 										"url": {
-											"raw": "{{gateway_url}}/test/oidc/register",
+											"raw": "{{registrationEndpoint}}",
 											"host": [
-												"{{gateway_url}}"
-											],
-											"path": [
-												"test",
-												"oidc",
-												"register"
+												"{{registrationEndpoint}}"
 											]
 										}
 									},
@@ -15999,14 +15895,9 @@
 											"raw": "{\n  \"client_name\": \"My Example Client\",\n  \"token_endpoint_auth_method\": \"client_secret_basic\",\n  \"logo_uri\": \"https://client.example.org/logo.png\",\n  \"jwks_uri\": \"https://client.example.org/my_public_keys.jwks\"\n}"
 										},
 										"url": {
-											"raw": "{{gateway_url}}/test/oidc/register",
+											"raw": "{{registrationEndpoint}}",
 											"host": [
-												"{{gateway_url}}"
-											],
-											"path": [
-												"test",
-												"oidc",
-												"register"
+												"{{registrationEndpoint}}"
 											]
 										}
 									},
@@ -16054,14 +15945,9 @@
 											"raw": "{\n  \"redirect_uris\": [\n    \"https://localhost/callback\",\n    \"https://localhost/callback2\"],\n  \"client_name\": \"My Example Client\",\n  \"token_endpoint_auth_method\": \"client_secret_basic\",\n  \"logo_uri\": \"https://client.example.org/logo.png\"\n}"
 										},
 										"url": {
-											"raw": "{{gateway_url}}/test/oidc/register",
+											"raw": "{{registrationEndpoint}}",
 											"host": [
-												"{{gateway_url}}"
-											],
-											"path": [
-												"test",
-												"oidc",
-												"register"
+												"{{registrationEndpoint}}"
 											]
 										}
 									},
@@ -16109,14 +15995,9 @@
 											"raw": "{\n  \"redirect_uris\": [\n    \"https://client.example.org/*/callback\",\n    \"https://client.example.org/*/callback2\"],\n  \"client_name\": \"My Example Client\",\n  \"token_endpoint_auth_method\": \"client_secret_basic\",\n  \"logo_uri\": \"https://client.example.org/logo.png\"\n}"
 										},
 										"url": {
-											"raw": "{{gateway_url}}/test/oidc/register",
+											"raw": "{{registrationEndpoint}}",
 											"host": [
-												"{{gateway_url}}"
-											],
-											"path": [
-												"test",
-												"oidc",
-												"register"
+												"{{registrationEndpoint}}"
 											]
 										}
 									},
@@ -16146,7 +16027,396 @@
 										}
 									],
 									"request": {
-										"method": "POST",
+										"method": "POST",
+										"header": [
+											{
+												"key": "Content-Type",
+												"type": "text",
+												"value": "application/json"
+											},
+											{
+												"key": "Authorization",
+												"type": "text",
+												"value": "Bearer {{access_token}}"
+											}
+										],
+										"body": {
+											"mode": "raw",
+											"raw": "{\n  \"redirect_uris\": [\n    \"http://client.example.org/callback\",\n    \"http://client.example.org/callback2\"],\n  \"client_name\": \"My Example Client\",\n  \"token_endpoint_auth_method\": \"client_secret_basic\",\n  \"logo_uri\": \"https://client.example.org/logo.png\"\n}"
+										},
+										"url": {
+											"raw": "{{registrationEndpoint}}",
+											"host": [
+												"{{registrationEndpoint}}"
+											]
+										}
+									},
+									"response": []
+								},
+								{
+									"name": "Invalid response type",
+									"event": [
+										{
+											"listen": "test",
+											"script": {
+												"id": "c557fd6b-d770-4568-8db7-ccb3267b0256",
+												"exec": [
+													"pm.test(\"Status code is 400\", function () {",
+													"    pm.response.to.have.status(400);",
+													"});",
+													"",
+													"pm.test(\"Invalid response type\", function () {",
+													"    pm.response.to.be.header('Content-Type', 'application/json');",
+													"    var body = pm.response.json();",
+													"    pm.expect(body.error).to.eql('invalid_client_metadata');",
+													"    pm.expect(body.error_description).to.eql('Invalid response type.');",
+													"});"
+												],
+												"type": "text/javascript"
+											}
+										}
+									],
+									"request": {
+										"method": "POST",
+										"header": [
+											{
+												"key": "Content-Type",
+												"type": "text",
+												"value": "application/json"
+											},
+											{
+												"key": "Authorization",
+												"type": "text",
+												"value": "Bearer {{access_token}}"
+											}
+										],
+										"body": {
+											"mode": "raw",
+											"raw": "{\n  \"redirect_uris\": [\n    \"https://client.example.org/callback\",\n    \"https://client.example.org/callback2\"],\n  \"response_types\": [\n\t\"unknown\"\n   ],\n  \"client_name\": \"My Example Client\",\n  \"token_endpoint_auth_method\": \"client_secret_basic\",\n  \"logo_uri\": \"https://client.example.org/logo.png\",\n  \"jwks_uri\": \"https://client.example.org/my_public_keys.jwks\"\n}"
+										},
+										"url": {
+											"raw": "{{registrationEndpoint}}",
+											"host": [
+												"{{registrationEndpoint}}"
+											]
+										}
+									},
+									"response": []
+								},
+								{
+									"name": "Invalid grant type",
+									"event": [
+										{
+											"listen": "test",
+											"script": {
+												"id": "c557fd6b-d770-4568-8db7-ccb3267b0256",
+												"exec": [
+													"pm.test(\"Status code is 400\", function () {",
+													"    pm.response.to.have.status(400);",
+													"});",
+													"",
+													"pm.test(\"Invalid grant type\", function () {",
+													"    pm.response.to.be.header('Content-Type', 'application/json');",
+													"    var body = pm.response.json();",
+													"    pm.expect(body.error).to.eql('invalid_client_metadata');",
+													"    pm.expect(body.error_description).to.eql('Missing or invalid grant type.');",
+													"});"
+												],
+												"type": "text/javascript"
+											}
+										}
+									],
+									"request": {
+										"method": "POST",
+										"header": [
+											{
+												"key": "Content-Type",
+												"type": "text",
+												"value": "application/json"
+											},
+											{
+												"key": "Authorization",
+												"type": "text",
+												"value": "Bearer {{access_token}}"
+											}
+										],
+										"body": {
+											"mode": "raw",
+											"raw": "{\n  \"redirect_uris\": [\n    \"https://client.example.org/callback\",\n    \"https://client.example.org/callback2\"],\n  \"grant_types\": [\n\t\"unknown\"\n   ],\n  \"client_name\": \"My Example Client\",\n  \"token_endpoint_auth_method\": \"client_secret_basic\",\n  \"logo_uri\": \"https://client.example.org/logo.png\",\n  \"jwks_uri\": \"https://client.example.org/my_public_keys.jwks\"\n}"
+										},
+										"url": {
+											"raw": "{{registrationEndpoint}}",
+											"host": [
+												"{{registrationEndpoint}}"
+											]
+										}
+									},
+									"response": []
+								},
+								{
+									"name": "Invalid request uri",
+									"event": [
+										{
+											"listen": "test",
+											"script": {
+												"id": "c557fd6b-d770-4568-8db7-ccb3267b0256",
+												"exec": [
+													"pm.test(\"Status code is 400\", function () {",
+													"    pm.response.to.have.status(400);",
+													"});",
+													"",
+													"pm.test(\"Invalid request uri\", function () {",
+													"    pm.response.to.be.header('Content-Type', 'application/json');",
+													"    var body = pm.response.json();",
+													"    pm.expect(body.error).to.eql('invalid_client_metadata');",
+													"    pm.expect(body.error_description).to.eql('request_uris: malformed_url is not valid.');",
+													"});"
+												],
+												"type": "text/javascript"
+											}
+										}
+									],
+									"request": {
+										"method": "POST",
+										"header": [
+											{
+												"key": "Content-Type",
+												"type": "text",
+												"value": "application/json"
+											},
+											{
+												"key": "Authorization",
+												"type": "text",
+												"value": "Bearer {{access_token}}"
+											}
+										],
+										"body": {
+											"mode": "raw",
+											"raw": "{\n  \"redirect_uris\": [\n    \"https://client.example.org/callback\",\n    \"https://client.example.org/callback2\"],\n  \"request_uris\": [\n\t\"malformed_url\"\n   ],\n  \"client_name\": \"My Example Client\",\n  \"token_endpoint_auth_method\": \"client_secret_basic\",\n  \"logo_uri\": \"https://client.example.org/logo.png\",\n  \"jwks_uri\": \"https://client.example.org/my_public_keys.jwks\"\n}"
+										},
+										"url": {
+											"raw": "{{registrationEndpoint}}",
+											"host": [
+												"{{registrationEndpoint}}"
+											]
+										}
+									},
+									"response": []
+								},
+								{
+									"name": "Invalid scope",
+									"event": [
+										{
+											"listen": "test",
+											"script": {
+												"id": "c557fd6b-d770-4568-8db7-ccb3267b0256",
+												"exec": [
+													"pm.test(\"Status code is 400\", function () {",
+													"    pm.response.to.have.status(400);",
+													"});",
+													"",
+													"pm.test(\"Invalid scope\", function () {",
+													"    pm.response.to.be.header('Content-Type', 'application/json');",
+													"    var body = pm.response.json();",
+													"    pm.expect(body.error).to.eql('invalid_client_metadata');",
+													"    pm.expect(body.error_description).to.eql('scope unkown-domain-scope is not valid.');",
+													"});"
+												],
+												"type": "text/javascript"
+											}
+										}
+									],
+									"request": {
+										"method": "POST",
+										"header": [
+											{
+												"key": "Content-Type",
+												"type": "text",
+												"value": "application/json"
+											},
+											{
+												"key": "Authorization",
+												"type": "text",
+												"value": "Bearer {{access_token}}"
+											}
+										],
+										"body": {
+											"mode": "raw",
+											"raw": "{\n  \"redirect_uris\": [\n    \"https://client.example.org/callback\",\n    \"https://client.example.org/callback2\"],\n  \"scope\": \"unkown-domain-scope\",\n  \"client_name\": \"My Example Client\",\n  \"token_endpoint_auth_method\": \"client_secret_basic\",\n  \"logo_uri\": \"https://client.example.org/logo.png\"\n}"
+										},
+										"url": {
+											"raw": "{{registrationEndpoint}}",
+											"host": [
+												"{{registrationEndpoint}}"
+											]
+										}
+									},
+									"response": []
+								}
+							],
+							"event": [
+								{
+									"listen": "prerequest",
+									"script": {
+										"id": "6344fd45-5776-4bab-a90c-033893e6debb",
+										"type": "text/javascript",
+										"exec": [
+											""
+										]
+									}
+								},
+								{
+									"listen": "test",
+									"script": {
+										"id": "03904ca6-6d82-43ab-8190-f3210787a262",
+										"type": "text/javascript",
+										"exec": [
+											""
+										]
+									}
+								}
+							],
+							"_postman_isSubFolder": true
+						},
+						{
+							"name": "Invalid update requests",
+							"item": [
+								{
+									"name": "Register client",
+									"event": [
+										{
+											"listen": "test",
+											"script": {
+												"id": "d83b2401-3844-4147-bbb3-abe08b43a342",
+												"exec": [
+													"pm.test(\"Status code is 201\", function () {",
+													"    pm.response.to.have.status(201);",
+													"});",
+													"",
+													"pm.test(\"Has default attributes\", function () {",
+													"    var body = pm.response.json();",
+													"    pm.expect(body).to.have.property('response_types');",
+													"    pm.expect(body).to.have.property('grant_types');",
+													"    pm.expect(body).to.have.property('scope');",
+													"    pm.expect(body).to.have.property('client_name');",
+													"    pm.expect(body).to.have.property('client_id');",
+													"    pm.expect(body).to.have.property('client_secret');",
+													"    pm.expect(body.response_types).to.eql(['code']);",
+													"    pm.expect(body.grant_types).to.eql(['authorization_code']);",
+													"    pm.expect(body.scope).to.eql('openid');",
+													"    pm.expect(body.client_name).to.eql('Unknown Client');",
+													"    ",
+													"    pm.environment.set('newClientForDCRTests', body.id);",
+													"    pm.environment.set('registrationAccessToken', body.registration_access_token);",
+													"    pm.environment.set('registrationClientUri', body.registration_client_uri);",
+													"});"
+												],
+												"type": "text/javascript"
+											}
+										}
+									],
+									"request": {
+										"method": "POST",
+										"header": [
+											{
+												"key": "Content-Type",
+												"type": "text",
+												"value": "application/json"
+											},
+											{
+												"key": "Authorization",
+												"type": "text",
+												"value": "Bearer {{access_token}}"
+											}
+										],
+										"body": {
+											"mode": "raw",
+											"raw": "{\n   \"application_type\": \"web\",\n   \"redirect_uris\":\n     [\"https://client.example.org/callback\",\n      \"https://client.example.org/callback2\"],\n   \"logo_uri\": \"https://client.example.org/logo.png\",\n   \"subject_type\": \"pairwise\",\n   \"token_endpoint_auth_method\": \"client_secret_basic\",\n   \"userinfo_encrypted_response_alg\": \"RSA1_5\",\n   \"userinfo_encrypted_response_enc\": \"A128CBC-HS256\",\n   \"contacts\": [\"ve7jtb@example.org\", \"mary@example.org\"],\n   \"request_uris\":\n     [\"https://client.example.org/rf.txt#qpXaRLh_n93TTR9F252ValdatUQvQiJi5BDub2BeznA\"]\n  }"
+										},
+										"url": {
+											"raw": "{{registrationEndpoint}}",
+											"host": [
+												"{{registrationEndpoint}}"
+											]
+										}
+									},
+									"response": []
+								},
+								{
+									"name": "Invalid access token",
+									"event": [
+										{
+											"listen": "test",
+											"script": {
+												"id": "d83b2401-3844-4147-bbb3-abe08b43a342",
+												"exec": [
+													"pm.test(\"Status code is 401\", function () {",
+													"    pm.response.to.have.status(401);",
+													"});",
+													"",
+													"pm.test(\"Invalid bearer\", function () {",
+													"    pm.response.to.be.header('Content-Type', 'application/json');",
+													"    var body = pm.response.json();",
+													"    pm.expect(body.error).to.eql('invalid_token');",
+													"    pm.expect(body.error_description).to.eql('The access token is invalid');",
+													"});"
+												],
+												"type": "text/javascript"
+											}
+										}
+									],
+									"request": {
+										"method": "PUT",
+										"header": [
+											{
+												"key": "Content-Type",
+												"type": "text",
+												"value": "application/json"
+											},
+											{
+												"key": "Authorization",
+												"type": "text",
+												"value": "Bearer wrongToken"
+											}
+										],
+										"body": {
+											"mode": "raw",
+											"raw": "{\n   \"client_name\": \"Should have not been updated\"\n}"
+										},
+										"url": {
+											"raw": "{{registrationClientUri}}",
+											"host": [
+												"{{registrationClientUri}}"
+											]
+										}
+									},
+									"response": []
+								},
+								{
+									"name": "Invalid redirect uri",
+									"event": [
+										{
+											"listen": "test",
+											"script": {
+												"id": "d83b2401-3844-4147-bbb3-abe08b43a342",
+												"exec": [
+													"pm.test(\"Status code is 400\", function () {",
+													"    pm.response.to.have.status(400);",
+													"});",
+													"",
+													"pm.test(\"Redirect uris are missing\", function () {",
+													"    pm.response.to.be.header('Content-Type', 'application/json');",
+													"    var body = pm.response.json();",
+													"    pm.expect(body.error).to.eql('invalid_redirect_uri');",
+													"    pm.expect(body.error_description).to.eql('Missing or invalid redirect_uris.');",
+													"});"
+												],
+												"type": "text/javascript"
+											}
+										}
+									],
+									"request": {
+										"method": "PUT",
 										"header": [
 											{
 												"key": "Content-Type",
@@ -16156,44 +16426,39 @@
 											{
 												"key": "Authorization",
 												"type": "text",
-												"value": "Bearer {{access_token}}"
+												"value": "Bearer {{registrationAccessToken}}"
 											}
 										],
 										"body": {
 											"mode": "raw",
-											"raw": "{\n  \"redirect_uris\": [\n    \"http://client.example.org/callback\",\n    \"http://client.example.org/callback2\"],\n  \"client_name\": \"My Example Client\",\n  \"token_endpoint_auth_method\": \"client_secret_basic\",\n  \"logo_uri\": \"https://client.example.org/logo.png\"\n}"
+											"raw": "{\n   \"client_name\": \"Should have not been updated\"\n}\n"
 										},
 										"url": {
-											"raw": "{{gateway_url}}/test/oidc/register",
+											"raw": "{{registrationClientUri}}",
 											"host": [
-												"{{gateway_url}}"
-											],
-											"path": [
-												"test",
-												"oidc",
-												"register"
+												"{{registrationClientUri}}"
 											]
 										}
 									},
 									"response": []
 								},
 								{
-									"name": "Invalid response type",
+									"name": "Invalid redirect uri - localhost",
 									"event": [
 										{
 											"listen": "test",
 											"script": {
-												"id": "c557fd6b-d770-4568-8db7-ccb3267b0256",
+												"id": "d83b2401-3844-4147-bbb3-abe08b43a342",
 												"exec": [
 													"pm.test(\"Status code is 400\", function () {",
 													"    pm.response.to.have.status(400);",
 													"});",
 													"",
-													"pm.test(\"Invalid response type\", function () {",
+													"pm.test(\"Redirect uris localhost forbidden\", function () {",
 													"    pm.response.to.be.header('Content-Type', 'application/json');",
 													"    var body = pm.response.json();",
-													"    pm.expect(body.error).to.eql('invalid_client_metadata');",
-													"    pm.expect(body.error_description).to.eql('Invalid response type.');",
+													"    pm.expect(body.error).to.eql('invalid_redirect_uri');",
+													"    pm.expect(body.error_description).to.eql('localhost is forbidden');",
 													"});"
 												],
 												"type": "text/javascript"
@@ -16201,7 +16466,7 @@
 										}
 									],
 									"request": {
-										"method": "POST",
+										"method": "PUT",
 										"header": [
 											{
 												"key": "Content-Type",
@@ -16211,44 +16476,39 @@
 											{
 												"key": "Authorization",
 												"type": "text",
-												"value": "Bearer {{access_token}}"
+												"value": "Bearer {{registrationAccessToken}}"
 											}
 										],
 										"body": {
 											"mode": "raw",
-											"raw": "{\n  \"redirect_uris\": [\n    \"https://client.example.org/callback\",\n    \"https://client.example.org/callback2\"],\n  \"response_types\": [\n\t\"unknown\"\n   ],\n  \"client_name\": \"My Example Client\",\n  \"token_endpoint_auth_method\": \"client_secret_basic\",\n  \"logo_uri\": \"https://client.example.org/logo.png\",\n  \"jwks_uri\": \"https://client.example.org/my_public_keys.jwks\"\n}"
+											"raw": "{  \"redirect_uris\": [\n    \"https://localhost/callback\",\n    \"https://localhost/callback2\"\n   ],\n   \"client_name\": \"Should have not been updated\"\n}"
 										},
 										"url": {
-											"raw": "{{gateway_url}}/test/oidc/register",
+											"raw": "{{registrationClientUri}}",
 											"host": [
-												"{{gateway_url}}"
-											],
-											"path": [
-												"test",
-												"oidc",
-												"register"
+												"{{registrationClientUri}}"
 											]
 										}
 									},
 									"response": []
 								},
 								{
-									"name": "Invalid grant type",
+									"name": "Invalid response type",
 									"event": [
 										{
 											"listen": "test",
 											"script": {
-												"id": "c557fd6b-d770-4568-8db7-ccb3267b0256",
+												"id": "d83b2401-3844-4147-bbb3-abe08b43a342",
 												"exec": [
 													"pm.test(\"Status code is 400\", function () {",
 													"    pm.response.to.have.status(400);",
 													"});",
 													"",
-													"pm.test(\"Invalid grant type\", function () {",
+													"pm.test(\"Invalid response type\", function () {",
 													"    pm.response.to.be.header('Content-Type', 'application/json');",
 													"    var body = pm.response.json();",
 													"    pm.expect(body.error).to.eql('invalid_client_metadata');",
-													"    pm.expect(body.error_description).to.eql('Missing or invalid grant type.');",
+													"    pm.expect(body.error_description).to.eql('Invalid response type.');",
 													"});"
 												],
 												"type": "text/javascript"
@@ -16256,7 +16516,7 @@
 										}
 									],
 									"request": {
-										"method": "POST",
+										"method": "PUT",
 										"header": [
 											{
 												"key": "Content-Type",
@@ -16266,44 +16526,39 @@
 											{
 												"key": "Authorization",
 												"type": "text",
-												"value": "Bearer {{access_token}}"
+												"value": "Bearer {{registrationAccessToken}}"
 											}
 										],
 										"body": {
 											"mode": "raw",
-											"raw": "{\n  \"redirect_uris\": [\n    \"https://client.example.org/callback\",\n    \"https://client.example.org/callback2\"],\n  \"grant_types\": [\n\t\"unknown\"\n   ],\n  \"client_name\": \"My Example Client\",\n  \"token_endpoint_auth_method\": \"client_secret_basic\",\n  \"logo_uri\": \"https://client.example.org/logo.png\",\n  \"jwks_uri\": \"https://client.example.org/my_public_keys.jwks\"\n}"
+											"raw": "{\n  \"redirect_uris\": [\n    \"https://client.example.org/callback\",\n    \"https://client.example.org/callback2\"],\n  \"response_types\": [\n\t\"unknown\"\n   ],\n   \"client_name\": \"Should have not been updated\"\n}"
 										},
 										"url": {
-											"raw": "{{gateway_url}}/test/oidc/register",
+											"raw": "{{registrationClientUri}}",
 											"host": [
-												"{{gateway_url}}"
-											],
-											"path": [
-												"test",
-												"oidc",
-												"register"
+												"{{registrationClientUri}}"
 											]
 										}
 									},
 									"response": []
 								},
 								{
-									"name": "Invalid request uri",
+									"name": "Invalid scope",
 									"event": [
 										{
 											"listen": "test",
 											"script": {
-												"id": "c557fd6b-d770-4568-8db7-ccb3267b0256",
+												"id": "d83b2401-3844-4147-bbb3-abe08b43a342",
 												"exec": [
 													"pm.test(\"Status code is 400\", function () {",
 													"    pm.response.to.have.status(400);",
 													"});",
 													"",
-													"pm.test(\"Invalid request uri\", function () {",
+													"pm.test(\"Invalid scope\", function () {",
 													"    pm.response.to.be.header('Content-Type', 'application/json');",
 													"    var body = pm.response.json();",
 													"    pm.expect(body.error).to.eql('invalid_client_metadata');",
-													"    pm.expect(body.error_description).to.eql('request_uris: malformed_url is not valid.');",
+													"    pm.expect(body.error_description).to.eql('scope unkown-domain-scope is not valid.');",
 													"});"
 												],
 												"type": "text/javascript"
@@ -16311,7 +16566,7 @@
 										}
 									],
 									"request": {
-										"method": "POST",
+										"method": "PUT",
 										"header": [
 											{
 												"key": "Content-Type",
@@ -16321,52 +16576,41 @@
 											{
 												"key": "Authorization",
 												"type": "text",
-												"value": "Bearer {{access_token}}"
+												"value": "Bearer {{registrationAccessToken}}"
 											}
 										],
 										"body": {
 											"mode": "raw",
-											"raw": "{\n  \"redirect_uris\": [\n    \"https://client.example.org/callback\",\n    \"https://client.example.org/callback2\"],\n  \"request_uris\": [\n\t\"malformed_url\"\n   ],\n  \"client_name\": \"My Example Client\",\n  \"token_endpoint_auth_method\": \"client_secret_basic\",\n  \"logo_uri\": \"https://client.example.org/logo.png\",\n  \"jwks_uri\": \"https://client.example.org/my_public_keys.jwks\"\n}"
+											"raw": "{\n  \"redirect_uris\": [\n    \"https://client.example.org/callback\",\n    \"https://client.example.org/callback2\"],\n  \"scope\": \"unkown-domain-scope\",\n  \"client_name\": \"Should have not been updated\"\n}"
 										},
 										"url": {
-											"raw": "{{gateway_url}}/test/oidc/register",
+											"raw": "{{registrationClientUri}}",
 											"host": [
-												"{{gateway_url}}"
-											],
-											"path": [
-												"test",
-												"oidc",
-												"register"
+												"{{registrationClientUri}}"
 											]
 										}
 									},
 									"response": []
 								},
 								{
-									"name": "Invalid scope",
+									"name": "Delete client - with registration token",
 									"event": [
 										{
 											"listen": "test",
 											"script": {
-												"id": "c557fd6b-d770-4568-8db7-ccb3267b0256",
+												"id": "d83b2401-3844-4147-bbb3-abe08b43a342",
 												"exec": [
-													"pm.test(\"Status code is 400\", function () {",
-													"    pm.response.to.have.status(400);",
+													"pm.test(\"Status code is 204\", function () {",
+													"    pm.response.to.have.status(204);",
 													"});",
-													"",
-													"pm.test(\"Invalid scope\", function () {",
-													"    pm.response.to.be.header('Content-Type', 'application/json');",
-													"    var body = pm.response.json();",
-													"    pm.expect(body.error).to.eql('invalid_client_metadata');",
-													"    pm.expect(body.error_description).to.eql('scope unkown-domain-scope is not valid.');",
-													"});"
+													""
 												],
 												"type": "text/javascript"
 											}
 										}
 									],
 									"request": {
-										"method": "POST",
+										"method": "DELETE",
 										"header": [
 											{
 												"key": "Content-Type",
@@ -16376,50 +16620,23 @@
 											{
 												"key": "Authorization",
 												"type": "text",
-												"value": "Bearer {{access_token}}"
+												"value": "Bearer {{registrationAccessToken}}"
 											}
 										],
 										"body": {
 											"mode": "raw",
-											"raw": "{\n  \"redirect_uris\": [\n    \"https://client.example.org/callback\",\n    \"https://client.example.org/callback2\"],\n  \"scope\": \"unkown-domain-scope\",\n  \"client_name\": \"My Example Client\",\n  \"token_endpoint_auth_method\": \"client_secret_basic\",\n  \"logo_uri\": \"https://client.example.org/logo.png\"\n}"
+											"raw": ""
 										},
 										"url": {
-											"raw": "{{gateway_url}}/test/oidc/register",
+											"raw": "{{registrationClientUri}}",
 											"host": [
-												"{{gateway_url}}"
-											],
-											"path": [
-												"test",
-												"oidc",
-												"register"
+												"{{registrationClientUri}}"
 											]
 										}
 									},
 									"response": []
 								}
 							],
-							"event": [
-								{
-									"listen": "prerequest",
-									"script": {
-										"id": "6344fd45-5776-4bab-a90c-033893e6debb",
-										"type": "text/javascript",
-										"exec": [
-											""
-										]
-									}
-								},
-								{
-									"listen": "test",
-									"script": {
-										"id": "03904ca6-6d82-43ab-8190-f3210787a262",
-										"type": "text/javascript",
-										"exec": [
-											""
-										]
-									}
-								}
-							],
 							"_postman_isSubFolder": true
 						},
 						{
@@ -16478,14 +16695,9 @@
 											"raw": "{\n   \"application_type\": \"web\",\n   \"redirect_uris\":\n     [\"https://client.example.org/callback\",\n      \"https://client.example.org/callback2\"],\n   \"logo_uri\": \"https://client.example.org/logo.png\",\n   \"subject_type\": \"pairwise\",\n   \"token_endpoint_auth_method\": \"client_secret_basic\",\n   \"userinfo_encrypted_response_alg\": \"RSA1_5\",\n   \"userinfo_encrypted_response_enc\": \"A128CBC-HS256\",\n   \"contacts\": [\"ve7jtb@example.org\", \"mary@example.org\"],\n   \"request_uris\":\n     [\"https://client.example.org/rf.txt#qpXaRLh_n93TTR9F252ValdatUQvQiJi5BDub2BeznA\"]\n  }"
 										},
 										"url": {
-											"raw": "{{gateway_url}}/test/oidc/register",
+											"raw": "{{registrationEndpoint}}",
 											"host": [
-												"{{gateway_url}}"
-											],
-											"path": [
-												"test",
-												"oidc",
-												"register"
+												"{{registrationEndpoint}}"
 											]
 										}
 									},
@@ -16667,6 +16879,63 @@
 									},
 									"response": []
 								},
+								{
+									"name": "Update client - with registration token",
+									"event": [
+										{
+											"listen": "test",
+											"script": {
+												"id": "d83b2401-3844-4147-bbb3-abe08b43a342",
+												"exec": [
+													"pm.test(\"Status code is 200\", function () {",
+													"    pm.response.to.have.status(200);",
+													"});",
+													"",
+													"pm.test(\"Get client with response types\", function () {",
+													"    pm.response.to.be.header('Content-Type', 'application/json');",
+													"    var body = pm.response.json();",
+													"    pm.expect(body.client_name).to.eql('Client name updated via DCR');",
+													"});",
+													"",
+													"pm.test(\"One time Token\", function() {",
+													"    var body = pm.response.json();",
+													"    pm.expect(body).to.have.property('registration_access_token');",
+													"    pm.expect(body.registration_access_token).to.not.eql(pm.environment.get('registrationAccessToken'));",
+													"    pm.environment.set('registrationAccessToken', body.registration_access_token);",
+													"    pm.environment.set('registrationClientUri', body.registration_client_uri);",
+													"});"
+												],
+												"type": "text/javascript"
+											}
+										}
+									],
+									"request": {
+										"method": "PUT",
+										"header": [
+											{
+												"key": "Content-Type",
+												"type": "text",
+												"value": "application/json"
+											},
+											{
+												"key": "Authorization",
+												"type": "text",
+												"value": "Bearer {{registrationAccessToken}}"
+											}
+										],
+										"body": {
+											"mode": "raw",
+											"raw": "{\n    \"redirect_uris\": [\n        \"https://client.example.org/callback\",\n        \"https://client.example.org/callback2\"\n    ],\n    \"response_types\": [\n        \"code\"\n    ],\n    \"grant_types\": [\n        \"authorization_code\"\n    ],\n    \"application_type\": \"web\",\n    \"contacts\": [\n        \"marie@example.org\",\n        \"jeanne@example.org\"\n    ],\n\t\"client_name\": \"Client name updated via DCR\",\n    \"logo_uri\": \"https://client.example.org/logo.png\",\n    \"subject_type\": \"pairwise\",\n    \"userinfo_encrypted_response_alg\": \"RSA1_5\",\n    \"userinfo_encrypted_response_enc\": \"A128CBC-HS256\",\n    \"token_endpoint_auth_method\": \"client_secret_basic\",\n    \"require_auth_time\": false,\n    \"request_uris\": [\n        \"https://client.example.org/rf.txt#qpXaRLh_n93TTR9F252ValdatUQvQiJi5BDub2BeznA\"\n    ],\n    \"scope\": \"openid\"\n}"
+										},
+										"url": {
+											"raw": "{{registrationClientUri}}",
+											"host": [
+												"{{registrationClientUri}}"
+											]
+										}
+									},
+									"response": []
+								},
 								{
 									"name": "Delete client - with registration token",
 									"event": [
@@ -17879,14 +18148,9 @@
 									]
 								},
 								"url": {
-									"raw": "{{gateway_url}}/test/oauth/token",
+									"raw": "{{tokenEndpoint}}",
 									"host": [
-										"{{gateway_url}}"
-									],
-									"path": [
-										"test",
-										"oauth",
-										"token"
+										"{{tokenEndpoint}}"
 									]
 								}
 							},
@@ -20666,14 +20930,9 @@
 									]
 								},
 								"url": {
-									"raw": "{{gateway_url}}/test/oauth/token",
+									"raw": "{{tokenEndpoint}}",
 									"host": [
-										"{{gateway_url}}"
-									],
-									"path": [
-										"test",
-										"oauth",
-										"token"
+										"{{tokenEndpoint}}"
 									]
 								}
 							},
@@ -20740,14 +20999,9 @@
 									]
 								},
 								"url": {
-									"raw": "{{gateway_url}}/test/oauth/token",
+									"raw": "{{tokenEndpoint}}",
 									"host": [
-										"{{gateway_url}}"
-									],
-									"path": [
-										"test",
-										"oauth",
-										"token"
+										"{{tokenEndpoint}}"
 									]
 								}
 							},
@@ -20813,14 +21067,9 @@
 									]
 								},
 								"url": {
-									"raw": "{{gateway_url}}/test/oauth/token",
+									"raw": "{{tokenEndpoint}}",
 									"host": [
-										"{{gateway_url}}"
-									],
-									"path": [
-										"test",
-										"oauth",
-										"token"
+										"{{tokenEndpoint}}"
 									]
 								}
 							},
@@ -20943,14 +21192,9 @@
 									]
 								},
 								"url": {
-									"raw": "{{gateway_url}}/test/oauth/token",
+									"raw": "{{tokenEndpoint}}",
 									"host": [
-										"{{gateway_url}}"
-									],
-									"path": [
-										"test",
-										"oauth",
-										"token"
+										"{{tokenEndpoint}}"
 									]
 								}
 							},
@@ -21124,14 +21368,9 @@
 									]
 								},
 								"url": {
-									"raw": "{{gateway_url}}/test/oauth/token",
+									"raw": "{{tokenEndpoint}}",
 									"host": [
-										"{{gateway_url}}"
-									],
-									"path": [
-										"test",
-										"oauth",
-										"token"
+										"{{tokenEndpoint}}"
 									]
 								}
 							},
@@ -21293,14 +21532,9 @@
 									]
 								},
 								"url": {
-									"raw": "{{gateway_url}}/test/oauth/token",
+									"raw": "{{tokenEndpoint}}",
 									"host": [
-										"{{gateway_url}}"
-									],
-									"path": [
-										"test",
-										"oauth",
-										"token"
+										"{{tokenEndpoint}}"
 									]
 								}
 							},
@@ -21765,14 +21999,9 @@
 									]
 								},
 								"url": {
-									"raw": "{{gateway_url}}/test/oauth/token",
+									"raw": "{{tokenEndpoint}}",
 									"host": [
-										"{{gateway_url}}"
-									],
-									"path": [
-										"test",
-										"oauth",
-										"token"
+										"{{tokenEndpoint}}"
 									]
 								}
 							},
@@ -21899,14 +22128,9 @@
 									]
 								},
 								"url": {
-									"raw": "{{gateway_url}}/test/oauth/token",
+									"raw": "{{tokenEndpoint}}",
 									"host": [
-										"{{gateway_url}}"
-									],
-									"path": [
-										"test",
-										"oauth",
-										"token"
+										"{{tokenEndpoint}}"
 									]
 								}
 							},
@@ -22084,14 +22308,9 @@
 									]
 								},
 								"url": {
-									"raw": "{{gateway_url}}/test/oauth/token",
+									"raw": "{{tokenEndpoint}}",
 									"host": [
-										"{{gateway_url}}"
-									],
-									"path": [
-										"test",
-										"oauth",
-										"token"
+										"{{tokenEndpoint}}"
 									]
 								}
 							},
@@ -22278,14 +22497,9 @@
 									]
 								},
 								"url": {
-									"raw": "{{gateway_url}}/test/oauth/token",
+									"raw": "{{tokenEndpoint}}",
 									"host": [
-										"{{gateway_url}}"
-									],
-									"path": [
-										"test",
-										"oauth",
-										"token"
+										"{{tokenEndpoint}}"
 									]
 								}
 							},
@@ -22348,4 +22562,4 @@
 			]
 		}
 	]
-}
\ No newline at end of file
+}


Test patch:
diff --git a/gravitee-am-gateway/gravitee-am-gateway-handler/src/test/java/io/gravitee/am/gateway/handler/oidc/request/DynamicClientRegistrationRequestTest.java b/gravitee-am-gateway/gravitee-am-gateway-handler/src/test/java/io/gravitee/am/gateway/handler/oidc/request/DynamicClientRegistrationRequestTest.java
index 072ecce269..07dfaf1813 100644
--- a/gravitee-am-gateway/gravitee-am-gateway-handler/src/test/java/io/gravitee/am/gateway/handler/oidc/request/DynamicClientRegistrationRequestTest.java
+++ b/gravitee-am-gateway/gravitee-am-gateway-handler/src/test/java/io/gravitee/am/gateway/handler/oidc/request/DynamicClientRegistrationRequestTest.java
@@ -16,6 +16,7 @@
 package io.gravitee.am.gateway.handler.oidc.request;
 
 import io.gravitee.am.model.Client;
+import org.junit.Before;
 import org.junit.Test;
 
 import java.util.Arrays;
@@ -29,10 +30,13 @@ import static org.junit.Assert.*;
  */
 public class DynamicClientRegistrationRequestTest {
 
-    @Test
-    public void testPatch() {
+    Client toPatch;
+    DynamicClientRegistrationRequest patcher;
+
+    @Before
+    public void setUp() {
         //Build Object to patch
-        Client toPatch = new Client();
+        toPatch = new Client();
         toPatch.setClientName("oldName");
         toPatch.setClientSecret("expectedSecret");
         toPatch.setClientUri("shouldDisappear");
@@ -40,18 +44,40 @@ public class DynamicClientRegistrationRequestTest {
         toPatch.setAccessTokenValiditySeconds(7200);
         toPatch.setRefreshTokenValiditySeconds(3600);
         toPatch.setResponseTypes(Arrays.asList("old","old2"));
+        toPatch.setDefaultMaxAge(1);
 
         //Build patcher
-        DynamicClientRegistrationRequest patcher = new DynamicClientRegistrationRequest();
+        patcher = new DynamicClientRegistrationRequest();
         patcher.setClientName(Optional.of("expectedClientName"));
         patcher.setClientUri(Optional.empty());
         patcher.setGrantTypes(Optional.of(Arrays.asList("grant1","grant2")));
         patcher.setResponseTypes(Optional.empty());
         patcher.setScope(Optional.of("scope1 scope2"));
+    }
 
+    @Test
+    public void testPatch() {
         //Apply patch
         Client result = patcher.patch(toPatch);
 
+        //Checks
+        assertNotNull(result);
+        assertEquals("Client name should have been replaced","expectedClientName",result.getClientName());
+        assertEquals("Client secret should have been kept","expectedSecret", result.getClientSecret());
+        assertNull("Client uri should have been erased",result.getClientUri());
+        assertEquals("Access token validity should have been kept",7200,result.getAccessTokenValiditySeconds());
+        assertEquals("Refresh token validity should have been kept",3600, result.getRefreshTokenValiditySeconds());
+        assertEquals("Default Max Age should have been kept",Integer.valueOf(1),result.getDefaultMaxAge());
+        assertArrayEquals("Grant types should have been replaced",Arrays.asList("grant1","grant2").toArray(),result.getAuthorizedGrantTypes().toArray());
+        assertArrayEquals("Response type should have been replaced by default values",Arrays.asList("code").toArray(),result.getResponseTypes().toArray());
+        assertArrayEquals("Scopes should have been replaced",Arrays.asList("scope1","scope2").toArray(), result.getScopes().toArray());
+    }
+
+    @Test
+    public void testUpdate() {
+        //Apply update
+        Client result = patcher.update(toPatch);
+
         //Checks
         assertNotNull(result);
         assertEquals("Client name should have been replaced","expectedClientName",result.getClientName());
@@ -60,7 +86,8 @@ public class DynamicClientRegistrationRequestTest {
         assertEquals("Access token validity should have been kept",7200,result.getAccessTokenValiditySeconds());
         assertEquals("Refresh token validity should have been kept",3600, result.getRefreshTokenValiditySeconds());
         assertArrayEquals("Grant types should have been replaced",Arrays.asList("grant1","grant2").toArray(),result.getAuthorizedGrantTypes().toArray());
-        assertArrayEquals("Response type should have been replaced by default falues",Arrays.asList("code").toArray(),result.getResponseTypes().toArray());
         assertArrayEquals("Scopes should have been replaced",Arrays.asList("scope1","scope2").toArray(), result.getScopes().toArray());
+        assertNull("Response type should be set to null", result.getResponseTypes());
+        assertNull("Default max age should be set to null", result.getDefaultMaxAge());
     }
 }
diff --git a/gravitee-am-gateway/gravitee-am-gateway-handler/src/test/java/io/gravitee/am/gateway/handler/vertx/handler/oidc/endpoint/DynamicClientAccessEndpointTest.java b/gravitee-am-gateway/gravitee-am-gateway-handler/src/test/java/io/gravitee/am/gateway/handler/vertx/handler/oidc/endpoint/DynamicClientAccessEndpointTest.java
index 142d91cf57..2c0b7ffcf3 100644
--- a/gravitee-am-gateway/gravitee-am-gateway-handler/src/test/java/io/gravitee/am/gateway/handler/vertx/handler/oidc/endpoint/DynamicClientAccessEndpointTest.java
+++ b/gravitee-am-gateway/gravitee-am-gateway-handler/src/test/java/io/gravitee/am/gateway/handler/vertx/handler/oidc/endpoint/DynamicClientAccessEndpointTest.java
@@ -60,6 +60,7 @@ public class DynamicClientAccessEndpointTest extends RxWebTestBase {
 
         router.route(HttpMethod.GET, "/register/:client_id").handler(endpoint::read);
         router.route(HttpMethod.PATCH, "/register/:client_id").handler(endpoint::patch);
+        router.route(HttpMethod.PUT, "/register/:client_id").handler(endpoint::update);
         router.route(HttpMethod.DELETE, "/register/:client_id").handler(endpoint::delete);
         router.route().failureHandler(new ExceptionHandler());
 
@@ -104,6 +105,17 @@ public class DynamicClientAccessEndpointTest extends RxWebTestBase {
                 HttpStatusCode.BAD_REQUEST_400, "Bad Request");
     }
 
+    @Test
+    public void update_noBody() throws Exception{
+        when(dcrService.validateClientRegistrationRequest(any())).thenReturn(Single.just(new DynamicClientRegistrationRequest()));
+        when(clientService.update(any())).thenReturn(Single.just(new Client()));
+        when(clientSyncService.addDynamicClientRegistred(any())).thenReturn(new Client());
+
+        String body = "{\"redirect_uris\": [\"https://redirecturi\"]}";
+        testRequest(
+                HttpMethod.PUT, "/register/my-test-client_id",
+                HttpStatusCode.BAD_REQUEST_400, "Bad Request");
+    }
 
     /*TODO: Implement valid case
     @Test
diff --git a/gravitee-am-model/src/test/java/io/gravitee/am/model/ClientTest.java b/gravitee-am-model/src/test/java/io/gravitee/am/model/ClientTest.java
new file mode 100644
index 0000000000..18656d202f
--- /dev/null
+++ b/gravitee-am-model/src/test/java/io/gravitee/am/model/ClientTest.java
@@ -0,0 +1,60 @@
+/**
+ * Copyright (C) 2015 The Gravitee team (http://gravitee.io)
+ *
+ * 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 io.gravitee.am.model;
+
+import io.gravitee.am.model.oidc.JWKSet;
+import org.junit.Test;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import static org.junit.Assert.*;
+
+/**
+ * @author Alexandre FARIA (contact at alexandrefaria.net)
+ * @author GraviteeSource Team
+ */
+public class ClientTest {
+
+    @Test
+    public void testClone() throws CloneNotSupportedException{
+
+        Map<String,Object> customs = new HashMap<>();
+        customs.put("one","one");
+        customs.put("two","two");
+
+        Client from = new Client();
+        from.setClientName("original");
+        from.setRedirectUris(Stream.of("http://host/callback","http://host/login").collect(Collectors.toList()));
+        from.setIdTokenCustomClaims(customs);
+        from.setJwks(new JWKSet());
+
+        Client to = from.clone();
+        //client name
+        assertTrue("same name",from.getClientName().equals(to.getClientName()));
+
+        //redirect uris
+        assertTrue("same redirect uris size",to.getRedirectUris()!=null && to.getRedirectUris().size()==from.getRedirectUris().size());
+        assertTrue("same redirect uris values",to.getRedirectUris().containsAll(from.getRedirectUris()));
+        assertFalse("not same object reference",from.getRedirectUris()==to.getRedirectUris());
+        //customs
+        assertTrue("same customs information size",to.getIdTokenCustomClaims()!=null && to.getIdTokenCustomClaims().size()==from.getIdTokenCustomClaims().size());
+        //assertTrue("same customs information values",to.getIdTokenCustomClaims().);
+        assertFalse("not same object reference",from.getRedirectUris()==to.getRedirectUris());
+    }
+}
diff --git a/gravitee-am-service/src/test/java/io/gravitee/am/service/utils/SetterUtilsTest.java b/gravitee-am-service/src/test/java/io/gravitee/am/service/utils/SetterUtilsTest.java
index ce55f1c8ed..0abca30d9a 100644
--- a/gravitee-am-service/src/test/java/io/gravitee/am/service/utils/SetterUtilsTest.java
+++ b/gravitee-am-service/src/test/java/io/gravitee/am/service/utils/SetterUtilsTest.java
@@ -138,6 +138,28 @@ public class SetterUtilsTest {
         assertEquals("succeed",basic.getString());
     }
 
+    @Test
+    public void testSet_optionalIsNull() {
+        BasicDto basic = new BasicDto();
+        SetterUtils.set(basic::setString,null);
+        assertNull("was expecting a null value",basic.getString());
+    }
+
+    @Test
+    public void testSet_optionalIsEmpty() {
+        BasicDto basic = new BasicDto();
+        SetterUtils.set(basic::setString,Optional.empty());
+        assertNull("was expecting a null value",basic.getString());
+    }
+
+    @Test
+    public void testSet_optionalIsNotEmpty() {
+        BasicDto basic = new BasicDto();
+        SetterUtils.set(basic::setString,Optional.of("succeed"));
+        assertNotNull(basic.getString());
+        assertEquals("succeed",basic.getString());
+    }
+
     private class BasicDto {
         private String objet;
         private boolean solo;