Repository: gravitee-io/graviteeio-access-management
Pull request #678 (2019-11-13 21:23:37)

fix(oidc): apply redirect_uri strict matching
fixes gravitee-io/issues#2190


Patch:
diff --git a/gravitee-am-gateway/gravitee-am-gateway-handler/gravitee-am-gateway-handler-oidc/src/main/java/io/gravitee/am/gateway/handler/oauth2/OAuth2Provider.java b/gravitee-am-gateway/gravitee-am-gateway-handler/gravitee-am-gateway-handler-oidc/src/main/java/io/gravitee/am/gateway/handler/oauth2/OAuth2Provider.java
index d8f9638565..e95f939b85 100644
--- a/gravitee-am-gateway/gravitee-am-gateway-handler/gravitee-am-gateway-handler-oidc/src/main/java/io/gravitee/am/gateway/handler/oauth2/OAuth2Provider.java
+++ b/gravitee-am-gateway/gravitee-am-gateway-handler/gravitee-am-gateway-handler-oidc/src/main/java/io/gravitee/am/gateway/handler/oauth2/OAuth2Provider.java
@@ -174,7 +174,7 @@ public class OAuth2Provider extends AbstractService<ProtocolProvider> implements
 
         // Authorization endpoint
         Handler<RoutingContext> authorizationRequestParseRequiredParametersHandler = new AuthorizationRequestParseRequiredParametersHandler(openIDDiscoveryService);
-        Handler<RoutingContext> authorizationRequestParseClientHandler = new AuthorizationRequestParseClientHandler(clientSyncService);
+        Handler<RoutingContext> authorizationRequestParseClientHandler = new AuthorizationRequestParseClientHandler(domain, clientSyncService);
         Handler<RoutingContext> authorizationRequestParseParametersHandler = new AuthorizationRequestParseParametersHandler(domain);
         Handler<RoutingContext> authorizeEndpoint = new AuthorizationEndpoint(flow, domain);
         Handler<RoutingContext> authorizeFailureEndpoint = new AuthorizationFailureEndpoint(domain);
diff --git a/gravitee-am-gateway/gravitee-am-gateway-handler/gravitee-am-gateway-handler-oidc/src/main/java/io/gravitee/am/gateway/handler/oauth2/resources/handler/authorization/AuthorizationRequestParseClientHandler.java b/gravitee-am-gateway/gravitee-am-gateway-handler/gravitee-am-gateway-handler-oidc/src/main/java/io/gravitee/am/gateway/handler/oauth2/resources/handler/authorization/AuthorizationRequestParseClientHandler.java
index 07d7f01348..074c00a66a 100644
--- a/gravitee-am-gateway/gravitee-am-gateway-handler/gravitee-am-gateway-handler-oidc/src/main/java/io/gravitee/am/gateway/handler/oauth2/resources/handler/authorization/AuthorizationRequestParseClientHandler.java
+++ b/gravitee-am-gateway/gravitee-am-gateway-handler/gravitee-am-gateway-handler-oidc/src/main/java/io/gravitee/am/gateway/handler/oauth2/resources/handler/authorization/AuthorizationRequestParseClientHandler.java
@@ -15,14 +15,15 @@
  */
 package io.gravitee.am.gateway.handler.oauth2.resources.handler.authorization;
 
+import io.gravitee.am.common.exception.oauth2.InvalidRequestException;
 import io.gravitee.am.common.oauth2.GrantType;
 import io.gravitee.am.common.oauth2.Parameters;
-import io.gravitee.am.common.exception.oauth2.InvalidRequestException;
 import io.gravitee.am.gateway.handler.common.client.ClientSyncService;
 import io.gravitee.am.gateway.handler.oauth2.exception.RedirectMismatchException;
 import io.gravitee.am.gateway.handler.oauth2.exception.ServerErrorException;
 import io.gravitee.am.gateway.handler.oauth2.exception.UnauthorizedClientException;
 import io.gravitee.am.model.Client;
+import io.gravitee.am.model.Domain;
 import io.vertx.core.AsyncResult;
 import io.vertx.core.Future;
 import io.vertx.core.Handler;
@@ -44,9 +45,11 @@ import java.util.List;
 public class AuthorizationRequestParseClientHandler implements Handler<RoutingContext> {
 
     private static final String CLIENT_CONTEXT_KEY = "client";
+    private Domain domain;
     private ClientSyncService clientSyncService;
 
-    public AuthorizationRequestParseClientHandler(ClientSyncService clientSyncService) {
+    public AuthorizationRequestParseClientHandler(Domain domain, ClientSyncService clientSyncService) {
+        this.domain = domain;
         this.clientSyncService = clientSyncService;
     }
 
@@ -147,6 +150,12 @@ public class AuthorizationRequestParseClientHandler implements Handler<RoutingCo
     }
 
     private boolean redirectMatches(String requestedRedirect, String registeredClientUri) {
+        // if redirect_uri strict matching mode is enabled, do string matching
+        if (this.domain.isRedirectUriStrictMatching()) {
+            return requestedRedirect.equals(registeredClientUri);
+        }
+
+        // nominal case
         try {
             URL req = new URL(requestedRedirect);
             URL reg = new URL(registeredClientUri);
diff --git a/gravitee-am-model/src/main/java/io/gravitee/am/model/Domain.java b/gravitee-am-model/src/main/java/io/gravitee/am/model/Domain.java
index 039e723cdd..eeb77781d2 100644
--- a/gravitee-am-model/src/main/java/io/gravitee/am/model/Domain.java
+++ b/gravitee-am-model/src/main/java/io/gravitee/am/model/Domain.java
@@ -262,6 +262,10 @@ public class Domain {
                 this.getOidc().getClientRegistrationSettings().isAllowWildCardRedirectUri();
     }
 
+    public boolean isRedirectUriStrictMatching() {
+        return this.getOidc()!=null && this.getOidc().isRedirectUriStrictMatching();
+    }
+
     @Override
     public boolean equals(Object o) {
         if (this == o) return true;
diff --git a/gravitee-am-model/src/main/java/io/gravitee/am/model/oidc/OIDCSettings.java b/gravitee-am-model/src/main/java/io/gravitee/am/model/oidc/OIDCSettings.java
index 88b46e41f0..3d3c58d06f 100644
--- a/gravitee-am-model/src/main/java/io/gravitee/am/model/oidc/OIDCSettings.java
+++ b/gravitee-am-model/src/main/java/io/gravitee/am/model/oidc/OIDCSettings.java
@@ -17,12 +17,18 @@ package io.gravitee.am.model.oidc;
 
 /**
  * @author Alexandre FARIA (contact at alexandrefaria.net)
+ * @author Titouan COMPIEGNE (titouan.compiegne at graviteesource.com)
  * @author GraviteeSource Team
  */
 public class OIDCSettings {
 
     private ClientRegistrationSettings clientRegistrationSettings;
 
+    /**
+     * Enable redirect_uri strict matching during OIDC flow (check for redirect_uri_mismatch exception)
+     */
+    private boolean redirectUriStrictMatching;
+
     public ClientRegistrationSettings getClientRegistrationSettings() {
         return clientRegistrationSettings!=null?clientRegistrationSettings: ClientRegistrationSettings.defaultSettings();
     }
@@ -31,9 +37,19 @@ public class OIDCSettings {
         this.clientRegistrationSettings = clientRegistrationSettings;
     }
 
+    public boolean isRedirectUriStrictMatching() {
+        return redirectUriStrictMatching;
+    }
+
+    public void setRedirectUriStrictMatching(boolean redirectUriStrictMatching) {
+        this.redirectUriStrictMatching = redirectUriStrictMatching;
+    }
+
     public static OIDCSettings defaultSettings() {
         OIDCSettings defaultSettings = new OIDCSettings();
         defaultSettings.setClientRegistrationSettings(ClientRegistrationSettings.defaultSettings());
+        defaultSettings.setRedirectUriStrictMatching(false);
         return defaultSettings;
     }
+
 }
diff --git a/gravitee-am-repository/gravitee-am-repository-mongodb/src/main/java/io/gravitee/am/repository/mongodb/management/MongoDomainRepository.java b/gravitee-am-repository/gravitee-am-repository-mongodb/src/main/java/io/gravitee/am/repository/mongodb/management/MongoDomainRepository.java
index 01006a9eae..ad2fdc7c8b 100644
--- a/gravitee-am-repository/gravitee-am-repository-mongodb/src/main/java/io/gravitee/am/repository/mongodb/management/MongoDomainRepository.java
+++ b/gravitee-am-repository/gravitee-am-repository-mongodb/src/main/java/io/gravitee/am/repository/mongodb/management/MongoDomainRepository.java
@@ -169,6 +169,7 @@ public class MongoDomainRepository extends AbstractManagementMongoRepository imp
         }
 
         OIDCSettings oidcSettings = new OIDCSettings();
+        oidcSettings.setRedirectUriStrictMatching(oidcMongo.isRedirectUriStrictMatching());
         oidcSettings.setClientRegistrationSettings(convert(oidcMongo.getClientRegistrationSettings()));
 
         return oidcSettings;
@@ -199,6 +200,7 @@ public class MongoDomainRepository extends AbstractManagementMongoRepository imp
         }
 
         OIDCSettingsMongo oidcSettings = new OIDCSettingsMongo();
+        oidcSettings.setRedirectUriStrictMatching(oidc.isRedirectUriStrictMatching());
         oidcSettings.setClientRegistrationSettings(convert(oidc.getClientRegistrationSettings()));
 
         return oidcSettings;
diff --git a/gravitee-am-repository/gravitee-am-repository-mongodb/src/main/java/io/gravitee/am/repository/mongodb/management/internal/model/oidc/OIDCSettingsMongo.java b/gravitee-am-repository/gravitee-am-repository-mongodb/src/main/java/io/gravitee/am/repository/mongodb/management/internal/model/oidc/OIDCSettingsMongo.java
index 494a3985aa..682219e4c2 100644
--- a/gravitee-am-repository/gravitee-am-repository-mongodb/src/main/java/io/gravitee/am/repository/mongodb/management/internal/model/oidc/OIDCSettingsMongo.java
+++ b/gravitee-am-repository/gravitee-am-repository-mongodb/src/main/java/io/gravitee/am/repository/mongodb/management/internal/model/oidc/OIDCSettingsMongo.java
@@ -22,6 +22,7 @@ package io.gravitee.am.repository.mongodb.management.internal.model.oidc;
 public class OIDCSettingsMongo {
 
     private ClientRegistrationSettingsMongo clientRegistrationSettings;
+    private boolean redirectUriStrictMatching;
 
     public ClientRegistrationSettingsMongo getClientRegistrationSettings() {
         return clientRegistrationSettings;
@@ -30,4 +31,12 @@ public class OIDCSettingsMongo {
     public void setClientRegistrationSettings(ClientRegistrationSettingsMongo clientRegistrationSettings) {
         this.clientRegistrationSettings = clientRegistrationSettings;
     }
+
+    public boolean isRedirectUriStrictMatching() {
+        return redirectUriStrictMatching;
+    }
+
+    public void setRedirectUriStrictMatching(boolean redirectUriStrictMatching) {
+        this.redirectUriStrictMatching = redirectUriStrictMatching;
+    }
 }
diff --git a/gravitee-am-service/src/main/java/io/gravitee/am/service/model/openid/PatchOIDCSettings.java b/gravitee-am-service/src/main/java/io/gravitee/am/service/model/openid/PatchOIDCSettings.java
index 06b8095c21..1560f0bc87 100644
--- a/gravitee-am-service/src/main/java/io/gravitee/am/service/model/openid/PatchOIDCSettings.java
+++ b/gravitee-am-service/src/main/java/io/gravitee/am/service/model/openid/PatchOIDCSettings.java
@@ -18,6 +18,7 @@ package io.gravitee.am.service.model.openid;
 import com.fasterxml.jackson.annotation.JsonProperty;
 import io.gravitee.am.model.oidc.ClientRegistrationSettings;
 import io.gravitee.am.model.oidc.OIDCSettings;
+import io.gravitee.am.service.utils.SetterUtils;
 
 import java.util.Optional;
 
@@ -32,6 +33,8 @@ public class PatchOIDCSettings {
     @JsonProperty("clientRegistrationSettings")
     private Optional<PatchClientRegistrationSettings> clientRegistrationSettings;
 
+    private Optional<Boolean> redirectUriStrictMatching;
+
     public Optional<PatchClientRegistrationSettings> getClientRegistrationSettings() {
         return clientRegistrationSettings;
     }
@@ -40,22 +43,33 @@ public class PatchOIDCSettings {
         this.clientRegistrationSettings = clientRegistrationSettings;
     }
 
+    public Optional<Boolean> getRedirectUriStrictMatching() {
+        return redirectUriStrictMatching;
+    }
+
+    public void setRedirectUriStrictMatching(Optional<Boolean> redirectUriStrictMatching) {
+        this.redirectUriStrictMatching = redirectUriStrictMatching;
+    }
+
     public OIDCSettings patch(OIDCSettings toPatch) {
 
         //If source may be null, in such case init with default values
-        OIDCSettings result=toPatch!=null?toPatch:OIDCSettings.defaultSettings();
+        if (toPatch == null ) {
+            toPatch = OIDCSettings.defaultSettings();
+        }
+        SetterUtils.safeSet(toPatch::setRedirectUriStrictMatching, this.getRedirectUriStrictMatching(), boolean.class);
 
         if(getClientRegistrationSettings()!=null) {
             //If present apply settings, else return default settings.
             if(getClientRegistrationSettings().isPresent()) {
                 PatchClientRegistrationSettings patcher = getClientRegistrationSettings().get();
-                ClientRegistrationSettings source = result.getClientRegistrationSettings();
-                result.setClientRegistrationSettings(patcher.patch(source));
+                ClientRegistrationSettings source = toPatch.getClientRegistrationSettings();
+                toPatch.setClientRegistrationSettings(patcher.patch(source));
             } else {
-                result.setClientRegistrationSettings(ClientRegistrationSettings.defaultSettings());
+                toPatch.setClientRegistrationSettings(ClientRegistrationSettings.defaultSettings());
             }
         }
 
-        return result;
+        return toPatch;
     }
 }
diff --git a/gravitee-am-ui/src/app/domain/settings/openid/client-registration/settings/settings.component.html b/gravitee-am-ui/src/app/domain/settings/openid/client-registration/settings/settings.component.html
index 2976ed2005..b05a747576 100644
--- a/gravitee-am-ui/src/app/domain/settings/openid/client-registration/settings/settings.component.html
+++ b/gravitee-am-ui/src/app/domain/settings/openid/client-registration/settings/settings.component.html
@@ -66,10 +66,18 @@
           <mat-slide-toggle
             (change)="allowWildCardRedirectUri($event)"
             [checked]="domain.oidc.clientRegistrationSettings.allowWildCardRedirectUri">
-            Allow wildcards redirect_uris.
+            Allow wildcards redirect uris.
           </mat-slide-toggle>
           <mat-hint>Allow clients to be registered with redirect_uris containing wildcards.</mat-hint>
         </div>
+        <div class="domain-client-registration-settings-state" fxLayout="column">
+          <mat-slide-toggle
+            (change)="enableRedirectUriStrictMatching($event)"
+            [checked]="domain.oidc.redirectUriStrictMatching">
+            Enable/Disable redirect uris strict matching.
+          </mat-slide-toggle>
+          <mat-hint>Enable redirect_uris strict matching when calling OpenID Connect flow.</mat-hint>
+        </div>
         <div class="domain-client-registration-settings-form-actions">
           <button mat-raised-button [disabled]="(!domainForm.valid || domainForm.pristine) && !formChanged" type="submit">SAVE</button>
         </div>
diff --git a/gravitee-am-ui/src/app/domain/settings/openid/client-registration/settings/settings.component.ts b/gravitee-am-ui/src/app/domain/settings/openid/client-registration/settings/settings.component.ts
index adeb8803f9..6b5f5e2262 100644
--- a/gravitee-am-ui/src/app/domain/settings/openid/client-registration/settings/settings.component.ts
+++ b/gravitee-am-ui/src/app/domain/settings/openid/client-registration/settings/settings.component.ts
@@ -89,6 +89,11 @@ export class ClientRegistrationSettingsComponent implements OnInit {
     this.formChanged = true;
   }
 
+  enableRedirectUriStrictMatching(event) {
+    this.domain.oidc.redirectUriStrictMatching = event.checked;
+    this.formChanged = true;
+  }
+
   patch() {
     this.domainService.patchOpenidDCRSettings(this.domain.id, this.domain).subscribe(response => {
       this.domain = response;
diff --git a/postman/collections/graviteeio-am-oauth2-collection.json b/postman/collections/graviteeio-am-oauth2-collection.json
index b49bb115a5..0a883506f9 100644
--- a/postman/collections/graviteeio-am-oauth2-collection.json
+++ b/postman/collections/graviteeio-am-oauth2-collection.json
@@ -1,6 +1,6 @@
 {
 	"info": {
-		"_postman_id": "31d34e59-af40-4fe5-9a44-8147d51e86cc",
+		"_postman_id": "363baad7-40ce-452c-a5e1-71e611a8b857",
 		"name": "Gravitee.io - AM - Oauth2",
 		"description": "Test Oauth2 (RFC 6749) specifications",
 		"schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json"
@@ -8295,6 +8295,117 @@
 									},
 									"response": []
 								},
+								{
+									"name": "Patch domain",
+									"event": [
+										{
+											"listen": "test",
+											"script": {
+												"id": "bfe78ac1-144a-4bbd-abf9-55e160e723bf",
+												"exec": [
+													"// wait for sync process",
+													"setTimeout(function(){}, 10000);"
+												],
+												"type": "text/javascript"
+											}
+										}
+									],
+									"request": {
+										"method": "PATCH",
+										"header": [
+											{
+												"key": "Content-Type",
+												"value": "application/json"
+											},
+											{
+												"key": "Authorization",
+												"value": "Bearer {{token}}"
+											}
+										],
+										"body": {
+											"mode": "raw",
+											"raw": "{\n  \"oidc\": {\n  \t\"redirectUriStrictMatching\": true\n  }\n}"
+										},
+										"url": {
+											"raw": "{{management_url}}/management/domains/{{domain}}",
+											"host": [
+												"{{management_url}}"
+											],
+											"path": [
+												"management",
+												"domains",
+												"{{domain}}"
+											]
+										}
+									},
+									"response": []
+								},
+								{
+									"name": "Send a bad redirect_uri strict matching",
+									"event": [
+										{
+											"listen": "test",
+											"script": {
+												"id": "9ac8c6d5-9172-44f1-8e03-9e8d92b4b4ea",
+												"exec": [
+													"pm.test(\"Status code is 302\", function () {",
+													"    pm.response.to.have.status(302);",
+													"});",
+													"",
+													"pm.test(\"Should be redirected\", function () {",
+													"    pm.response.to.be.redirection;",
+													"    pm.response.to.be.header('Content-Length', '0');",
+													"    pm.response.to.have.header('Location');",
+													"    pm.response.to.not.have.header('Set-Cookie');",
+													"});",
+													"",
+													"pm.test(\"Should be a location error\", function() {",
+													"    var location = postman.getResponseHeader('Location');",
+													"    tests['Contains an error query-parameter'] = location.includes('error=redirect_uri_mismatch');",
+													"    tests['Contains an error description query-parameter'] = location.includes('error_description=The+redirect_uri+MUST+match+the+registered+callback+URL+for+this+application');",
+													"});"
+												],
+												"type": "text/javascript"
+											}
+										},
+										{
+											"listen": "prerequest",
+											"script": {
+												"id": "904f0113-b2b9-4645-9511-48bb10791506",
+												"exec": [
+													""
+												],
+												"type": "text/javascript"
+											}
+										}
+									],
+									"request": {
+										"method": "GET",
+										"header": [],
+										"url": {
+											"raw": "{{authorizationEndpoint}}?response_type=code&client_id=my-client2&redirect_uri=http://localhost:4000?extraParam=test",
+											"host": [
+												"{{authorizationEndpoint}}"
+											],
+											"query": [
+												{
+													"key": "response_type",
+													"value": "code"
+												},
+												{
+													"key": "client_id",
+													"value": "my-client2"
+												},
+												{
+													"key": "redirect_uri",
+													"value": "http://localhost:4000?extraParam=test"
+												}
+											]
+										},
+										"description": "The client does not have a redirect_uri define"
+									},
+									"response": []
+								},
 								{
 									"name": "Error with state parameter",
 									"event": [
@@ -12996,4 +13107,4 @@
 		}
 	],
 	"protocolProfileBehavior": {}
-}
+}
\ No newline at end of file


Test patch:
diff --git a/gravitee-am-gateway/gravitee-am-gateway-handler/gravitee-am-gateway-handler-oidc/src/test/java/io/gravitee/am/gateway/handler/oauth2/resources/endpoint/AuthorizationEndpointTest.java b/gravitee-am-gateway/gravitee-am-gateway-handler/gravitee-am-gateway-handler-oidc/src/test/java/io/gravitee/am/gateway/handler/oauth2/resources/endpoint/AuthorizationEndpointTest.java
index ac29f5da8b..dd0700924d 100644
--- a/gravitee-am-gateway/gravitee-am-gateway-handler/gravitee-am-gateway-handler-oidc/src/test/java/io/gravitee/am/gateway/handler/oauth2/resources/endpoint/AuthorizationEndpointTest.java
+++ b/gravitee-am-gateway/gravitee-am-gateway-handler/gravitee-am-gateway-handler-oidc/src/test/java/io/gravitee/am/gateway/handler/oauth2/resources/endpoint/AuthorizationEndpointTest.java
@@ -107,7 +107,7 @@ public class AuthorizationEndpointTest extends RxWebTestBase {
                 .handler(sessionHandler);
         router.route(HttpMethod.GET, "/oauth/authorize")
                 .handler(new AuthorizationRequestParseRequiredParametersHandler(openIDDiscoveryService))
-                .handler(new AuthorizationRequestParseClientHandler(clientSyncService))
+                .handler(new AuthorizationRequestParseClientHandler(domain, clientSyncService))
                 .handler(new AuthorizationRequestParseParametersHandler(domain))
                 .handler(authorizationEndpointHandler);
         router.route()
@@ -287,6 +287,73 @@ public class AuthorizationEndpointTest extends RxWebTestBase {
                 HttpStatusCode.FOUND_302, "Found", null);
     }
 
+    @Test
+    public void shouldNotInvokeAuthorizationEndpoint_mismatchRedirectUri_strictMatching() throws Exception {
+        final Client client = new Client();
+        client.setId("client-id");
+        client.setClientId("client-id");
+        client.setScopes(Collections.singletonList("read"));
+        client.setRedirectUris(Collections.singletonList("http://localhost:9999/authorize/callback"));
+
+        AuthorizationRequest authorizationRequest = new AuthorizationRequest();
+        authorizationRequest.setApproved(true);
+        authorizationRequest.setResponseType(ResponseType.CODE);
+        authorizationRequest.setRedirectUri("http://localhost:9999/authorize/callback?param=param1");
+
+        when(domain.isRedirectUriStrictMatching()).thenReturn(true);
+        when(clientSyncService.findByClientId("client-id")).thenReturn(Maybe.just(client));
+
+        testRequest(
+                HttpMethod.GET,
+                "/oauth/authorize?response_type=code&client_id=client-id&redirect_uri=http://localhost:9999/authorize/callback?param=param1",
+                null,
+                resp -> {
+                    String location = resp.headers().get("location");
+                    assertNotNull(location);
+                    assertTrue(location.contains("/test/oauth/error?client_id=client-id&error=redirect_uri_mismatch&error_description=The+redirect_uri+MUST+match+the+registered+callback+URL+for+this+application"));
+                },
+                HttpStatusCode.FOUND_302, "Found", null);
+    }
+
+    @Test
+    public void shouldInvokeAuthorizationEndpoint_noStrictMatching() throws Exception {
+        io.gravitee.am.model.User user = new io.gravitee.am.model.User();
+
+        final Client client = new Client();
+        client.setId("client-id");
+        client.setClientId("client-id");
+        client.setScopes(Collections.singletonList("read"));
+        client.setRedirectUris(Collections.singletonList("http://localhost:9999/authorize/callback"));
+
+        AuthorizationRequest authorizationRequest = new AuthorizationRequest();
+        authorizationRequest.setApproved(true);
+        authorizationRequest.setResponseType(ResponseType.CODE);
+        authorizationRequest.setRedirectUri("http://localhost:9999/authorize/callback?param=param1");
+
+        AuthorizationResponse authorizationResponse = new AuthorizationCodeResponse();
+        authorizationResponse.setRedirectUri(authorizationRequest.getRedirectUri());
+        ((AuthorizationCodeResponse) authorizationResponse).setCode("test-code");
+
+        router.route().order(-1).handler(routingContext -> {
+            routingContext.setUser(new User(new io.gravitee.am.gateway.handler.common.vertx.web.auth.user.User(user)));
+            routingContext.next();
+        });
+
+        when(clientSyncService.findByClientId("client-id")).thenReturn(Maybe.just(client));
+        when(flow.run(any(), any(), any())).thenReturn(Single.just(authorizationResponse));
+
+        testRequest(
+                HttpMethod.GET,
+                "/oauth/authorize?response_type=code&client_id=client-id&redirect_uri=http://localhost:9999/authorize/callback?param=param1",
+                null,
+                resp -> {
+                    String location = resp.headers().get("location");
+                    assertNotNull(location);
+                    assertEquals("http://localhost:9999/authorize/callback?param=param1&code=test-code", location);
+                },
+                HttpStatusCode.FOUND_302, "Found", null);
+    }
+
     @Test
     public void shouldNotInvokeAuthorizationEndpoint_duplicateParameters() throws Exception {
         final Client client = new Client();
diff --git a/gravitee-am-model/src/test/java/io/gravitee/am/model/DomainTest.java b/gravitee-am-model/src/test/java/io/gravitee/am/model/DomainTest.java
index 64aa26db30..cad3fd70a9 100644
--- a/gravitee-am-model/src/test/java/io/gravitee/am/model/DomainTest.java
+++ b/gravitee-am-model/src/test/java/io/gravitee/am/model/DomainTest.java
@@ -21,6 +21,11 @@ import org.junit.Test;
 
 import static org.junit.Assert.assertFalse;
 
+/**
+ * @author Alexandre FARIA (contact at alexandrefaria.net)
+ * @author Titouan COMPIEGNE (titouan.compiegne at graviteesource.com)
+ * @author GraviteeSource Team
+ */
 public class DomainTest {
 
 
@@ -121,4 +126,13 @@ public class DomainTest {
         boolean isEnabled = domain.isRedirectUriWildcardAllowed();
         assertFalse("By default dcr settings should be disabled",isEnabled);
     }
+
+    @Test
+    public void isRedirectUriStrictMatching() {
+        Domain domain = new Domain();
+        domain.setOidc(OIDCSettings.defaultSettings());
+
+        boolean isEnabled = domain.isRedirectUriStrictMatching();
+        assertFalse("By default strict matching settings should be disabled",isEnabled);
+    }
 }