Repository: hwi/HWIOAuthBundle (php)

Issue:
Incorrect redirect URL since 2.0.0-BETA2 when resource owner used on multiple firewalls
| Q                 | A
| ------------------| ---
| Bug?              | yes
| New Feature?      | no
| Support question? | no
| Version           | 2.x


This issue was introduced in version `2.0.0-BETA2`.
Version `2.0.0-BETA1` is working as expected.

I defined multiples firewalls in my `security.yaml`, 1 for the API, 1 for the website.
Both firewall are using the same resource owner with different URLs:
- API: `/api/{service}-login`
- WEB:  `/{service}-login`

On `2.0.0-BETA1`, if I login using the link provided by `{{ path('hwi_oauth_service_redirect', {'service': 'facebook'}) }}`, the redirect URL is `/fb-login` which is the one I am expecting.

On `2.0.0-BETA2`, the redirect URL is `/api/fb-login`

This is not limited to Facebook, I am now redirected to `/api/{service}-login` for all of them.

```yaml
security:
    firewalls:
        api_login:
            lazy: true
            provider: db_users
            pattern: ^/api/(login|(.+)-login)
            stateless: true
            oauth:
                resource_owners:
                    facebook: /api/fb-login
                    google:   /api/google-login
                    apple:    /api/apple-login
                login_path:   /
                oauth_user_provider:
                    service: App\Repository\UserRepository
        main:
            lazy: true
            provider: db_users
            pattern: ^/
            oauth:
                resource_owners:
                    facebook: /fb-login
                    google:   /google-login
                    apple:    /apple-login
                login_path:   /
                oauth_user_provider:
                    service: App\Repository\UserRepository
```

Pull request description:
Current matched Firewall is respected during generation of check path links.
If matched firewall does not have **oauth** listener/authenticator (aka login_area in old Symfony), the last `potentialResourceOwnerCheckPath` (basically from main firewall) will be taken. 
fixes #1885


Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 6a738cc..69b09e2 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -8,6 +8,7 @@ Changelog
 * Bugfix: Added `OAuth1ResourceOwner` & `OAuth2ResourceOwner` to cover case of implementing custom oauth resource owners,
 * Bugfix: Fixed Authorization Header in `CleverResourceOwner::doGetRequest`,
 * Bugfix: Catch also the `TransportExceptionInterface` in `AbstractResourceOwner::getResponseContent()` method,
+* Bugfix: Current matched Firewall is respected during generation of resource owner check path links.
 
 ## 2.0.0-BETA2 (2022-01-16)
 * Deprecated: configuration parameter `firewall_names`, firewalls are now computed automatically - all firewalls that have defined `oauth` authenticator/provider will be collected,
diff --git a/src/DependencyInjection/CompilerPass/ResourceOwnerMapCompilerPass.php b/src/DependencyInjection/CompilerPass/ResourceOwnerMapCompilerPass.php
index 9e520f1..bad302e 100644
--- a/src/DependencyInjection/CompilerPass/ResourceOwnerMapCompilerPass.php
+++ b/src/DependencyInjection/CompilerPass/ResourceOwnerMapCompilerPass.php
@@ -42,7 +42,7 @@ final class ResourceOwnerMapCompilerPass implements CompilerPassInterface
             $resourceOwnerMapRef = new Reference($resourceOwnerMapId);
 
             $locatorDef->addMethodCall('set', [$firewallName, $resourceOwnerMapRef]);
-            $oauthUtilsDef->addMethodCall('addResourceOwnerMap', [$resourceOwnerMapRef]);
+            $oauthUtilsDef->addMethodCall('addResourceOwnerMap', [$firewallName, $resourceOwnerMapRef]);
         }
     }
 }
diff --git a/src/Resources/config/oauth.php b/src/Resources/config/oauth.php
index a125d11..f5fe3d0 100644
--- a/src/Resources/config/oauth.php
+++ b/src/Resources/config/oauth.php
@@ -58,6 +58,7 @@ return static function (ContainerConfigurator $containerConfigurator): void {
         ->args([
             service('security.http_utils'),
             service('security.authorization_checker'),
+            service('security.firewall.map'),
             '%hwi_oauth.connect%',
             '%hwi_oauth.grant_rule%',
         ]);
diff --git a/src/Security/OAuthUtils.php b/src/Security/OAuthUtils.php
index 5b4f966..11aa3ca 100644
--- a/src/Security/OAuthUtils.php
+++ b/src/Security/OAuthUtils.php
@@ -14,6 +14,7 @@ namespace HWI\Bundle\OAuthBundle\Security;
 use HWI\Bundle\OAuthBundle\OAuth\ResourceOwnerInterface;
 use HWI\Bundle\OAuthBundle\OAuth\State\State;
 use HWI\Bundle\OAuthBundle\Security\Http\ResourceOwnerMapInterface;
+use Symfony\Bundle\SecurityBundle\Security\FirewallMap;
 use Symfony\Component\HttpFoundation\Request;
 use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface;
 use Symfony\Component\Security\Http\HttpUtils;
@@ -32,29 +33,31 @@ final class OAuthUtils
     private bool $connect;
     private string $grantRule;
     private HttpUtils $httpUtils;
+    private AuthorizationCheckerInterface $authorizationChecker;
+    private FirewallMap $firewallMap;
 
     /**
-     * @var ResourceOwnerMapInterface[]
+     * @var array<string, ResourceOwnerMapInterface>
      */
     private array $ownerMaps = [];
 
-    private AuthorizationCheckerInterface $authorizationChecker;
-
     public function __construct(
         HttpUtils $httpUtils,
         AuthorizationCheckerInterface $authorizationChecker,
+        FirewallMap $firewallMap,
         bool $connect,
         string $grantRule
     ) {
         $this->httpUtils = $httpUtils;
         $this->authorizationChecker = $authorizationChecker;
+        $this->firewallMap = $firewallMap;
         $this->connect = $connect;
         $this->grantRule = $grantRule;
     }
 
-    public function addResourceOwnerMap(ResourceOwnerMapInterface $ownerMap): void
+    public function addResourceOwnerMap($firewallName, ResourceOwnerMapInterface $ownerMap): void
     {
-        $this->ownerMaps[] = $ownerMap;
+        $this->ownerMaps[$firewallName] = $ownerMap;
     }
 
     /**
@@ -77,7 +80,8 @@ final class OAuthUtils
 
         if (null === $redirectUrl) {
             if (!$this->connect || !$this->authorizationChecker->isGranted($this->grantRule)) {
-                $redirectUrl = $this->httpUtils->generateUri($request, $this->getResourceOwnerCheckPath($name));
+                $firewallName = $this->getCurrentFirewallName($request);
+                $redirectUrl = $this->httpUtils->generateUri($request, $this->getResourceOwnerCheckPath($name, $firewallName));
             } else {
                 $redirectUrl = $this->getServiceAuthUrl($request, $resourceOwner);
             }
@@ -93,7 +97,9 @@ final class OAuthUtils
     public function getServiceAuthUrl(Request $request, ResourceOwnerInterface $resourceOwner): string
     {
         if ($resourceOwner->getOption('auth_with_one_url')) {
-            return $this->httpUtils->generateUri($request, $this->getResourceOwnerCheckPath($resourceOwner->getName()));
+            $firewallName = $this->getCurrentFirewallName($request);
+
+            return $this->httpUtils->generateUri($request, $this->getResourceOwnerCheckPath($resourceOwner->getName(), $firewallName));
         }
 
         $request->attributes->set('service', $resourceOwner->getName());
@@ -239,15 +245,31 @@ final class OAuthUtils
         throw new \RuntimeException(sprintf("No resource owner with name '%s'.", $name));
     }
 
-    private function getResourceOwnerCheckPath(string $name): ?string
+    private function getCurrentFirewallName(Request $request): ?string
     {
-        foreach ($this->ownerMaps as $ownerMap) {
+        $firewallConfig = $this->firewallMap->getFirewallConfig($request);
+
+        return null === $firewallConfig ? null : $firewallConfig->getName();
+    }
+
+    private function getResourceOwnerCheckPath(string $name, ?string $firewallName = null): ?string
+    {
+        $resourceOwnerMaps = $this->ownerMaps;
+
+        // if 'oauth' for firewall defined search only in corresponding resourceOwnerMao
+        if (null !== $firewallName && isset($this->ownerMaps[$firewallName])) {
+            $resourceOwnerMaps = [$this->ownerMaps[$firewallName]];
+        }
+
+        // otherwise get the latest potential checked (basically from main firewall)
+        $resourceOwnerCheckPath = null;
+        foreach ($resourceOwnerMaps as $ownerMap) {
             if ($potentialResourceOwnerCheckPath = $ownerMap->getResourceOwnerCheckPath($name)) {
-                return $potentialResourceOwnerCheckPath;
+                $resourceOwnerCheckPath = $potentialResourceOwnerCheckPath;
             }
         }
 
-        return null;
+        return $resourceOwnerCheckPath;
     }
 
     /**


Test patch:
diff --git a/tests/App/config/routing.yaml b/tests/App/config/routing.yaml
index a944c1a..b1925d2 100644
--- a/tests/App/config/routing.yaml
+++ b/tests/App/config/routing.yaml
@@ -30,3 +30,18 @@ yahoo_login:
 
 custom_login:
     path: /check-login/custom
+
+app_api_service_redirect:
+  path: /api/connect/{service}
+  defaults:
+    _controller: 'HWI\Bundle\OAuthBundle\Controller\RedirectToServiceController::redirectToServiceAction'
+    methods: GET
+
+api_google_login:
+  path: /api/check-login/google
+
+api_yahoo_login:
+  path: /api/check-login/yahoo
+
+api_custom_login:
+  path: /api/check-login/custom
diff --git a/tests/App/config/security_v4.yaml b/tests/App/config/security_v4.yaml
index 31989da..cbc1d29 100644
--- a/tests/App/config/security_v4.yaml
+++ b/tests/App/config/security_v4.yaml
@@ -11,6 +11,23 @@ security:
       pattern: ^/(login$|connect|login_hwi)
       anonymous: lazy
       context: hwi_context
+    api:
+      pattern: ^/api/
+      anonymous: lazy
+      stateless: true
+      oauth:
+        resource_owners:
+          google: '/api/check-login/google'
+          yahoo:  '/api/check-login/yahoo'
+          custom: '/api/check-login/custom'
+        login_path: /api/login
+        check_path: /api/check-login
+        use_forward: false
+        failure_path: /api/login
+        oauth_user_provider:
+          service: HWI\Bundle\OAuthBundle\Tests\App\UserProvider
+        provider: HWI\Bundle\OAuthBundle\Tests\App\UserProvider
+      context: hwi_context
     main:
       pattern: ^/
       anonymous: lazy
diff --git a/tests/App/config/security_v5.yaml b/tests/App/config/security_v5.yaml
index 15e35a5..c29a041 100644
--- a/tests/App/config/security_v5.yaml
+++ b/tests/App/config/security_v5.yaml
@@ -9,6 +9,22 @@ security:
   enable_authenticator_manager: true
 
   firewalls:
+    api:
+      pattern: ^/api/
+      stateless: true
+      oauth:
+        resource_owners:
+          google: '/api/check-login/google'
+          yahoo:  '/api/check-login/yahoo'
+          custom: '/api/check-login/custom'
+        login_path: /api/login
+        check_path: /api/check-login
+        use_forward: false
+        failure_path: /api/login
+        oauth_user_provider:
+          service: HWI\Bundle\OAuthBundle\Tests\App\UserProvider
+        provider: HWI\Bundle\OAuthBundle\Tests\App\UserProvider
+      context: hwi_context
     main:
       lazy: true
       pattern: ^/
diff --git a/tests/App/config/security_v5_bc.yaml b/tests/App/config/security_v5_bc.yaml
index c80b098..19c1107 100644
--- a/tests/App/config/security_v5_bc.yaml
+++ b/tests/App/config/security_v5_bc.yaml
@@ -13,6 +13,23 @@ security:
       pattern: ^/(login$|connect|login_hwi)
       anonymous: lazy
       context: hwi_context
+    api:
+      pattern: ^/api/
+      anonymous: lazy
+      stateless: true
+      oauth:
+        resource_owners:
+          google: '/api/check-login/google'
+          yahoo:  '/api/check-login/yahoo'
+          custom: '/api/check-login/custom'
+        login_path: /api/login
+        check_path: /api/check-login
+        use_forward: false
+        failure_path: /api/login
+        oauth_user_provider:
+          service: HWI\Bundle\OAuthBundle\Tests\App\UserProvider
+        provider: HWI\Bundle\OAuthBundle\Tests\App\UserProvider
+      context: hwi_context
     main:
       pattern: ^/
       anonymous: lazy
diff --git a/tests/App/config/security_v6.yaml b/tests/App/config/security_v6.yaml
index 94a47be..f36921f 100644
--- a/tests/App/config/security_v6.yaml
+++ b/tests/App/config/security_v6.yaml
@@ -9,6 +9,22 @@ security:
   enable_authenticator_manager: true
 
   firewalls:
+    api:
+      pattern: ^/api/
+      stateless: true
+      oauth:
+        resource_owners:
+          google: '/api/check-login/google'
+          yahoo:  '/api/check-login/yahoo'
+          custom: '/api/check-login/custom'
+        login_path: /api/login
+        check_path: /api/check-login
+        use_forward: false
+        failure_path: /api/login
+        oauth_user_provider:
+          service: HWI\Bundle\OAuthBundle\Tests\App\UserProvider
+        provider: HWI\Bundle\OAuthBundle\Tests\App\UserProvider
+      context: hwi_context
     main:
       lazy: true
       pattern: ^/
diff --git a/tests/App/templates/login.html.twig b/tests/App/templates/login.html.twig
index 5a87928..a752603 100644
--- a/tests/App/templates/login.html.twig
+++ b/tests/App/templates/login.html.twig
@@ -1,3 +1,7 @@
 <a href="{{ path('hwi_oauth_service_redirect', {'service': 'google' }) }}">
     <span>Login with Google</span>
 </a>
+
+<a href="{{ path('app_api_service_redirect', {'service': 'google' }) }}">
+  <span>Api Login with Google</span>
+</a>
diff --git a/tests/Controller/Connect/AbstractConnectControllerTest.php b/tests/Controller/Connect/AbstractConnectControllerTest.php
index 2b6daff..414cae2 100644
--- a/tests/Controller/Connect/AbstractConnectControllerTest.php
+++ b/tests/Controller/Connect/AbstractConnectControllerTest.php
@@ -24,6 +24,8 @@ use HWI\Bundle\OAuthBundle\Test\Fixtures\CustomUserResponse;
 use HWI\Bundle\OAuthBundle\Tests\Fixtures\CustomOAuthToken;
 use PHPUnit\Framework\MockObject\MockObject;
 use PHPUnit\Framework\TestCase;
+use Symfony\Bundle\SecurityBundle\Security\FirewallConfig;
+use Symfony\Bundle\SecurityBundle\Security\FirewallMap;
 use Symfony\Component\EventDispatcher\EventDispatcherInterface;
 use Symfony\Component\Form\FormFactoryInterface;
 use Symfony\Component\HttpFoundation\Request;
@@ -139,6 +141,7 @@ abstract class AbstractConnectControllerTest extends TestCase
         $this->oAuthUtils = new OAuthUtils(
             $httpUtils,
             $this->createMock(AuthorizationCheckerInterface::class),
+            $this->createFirewallMapMock(),
             true,
             'IS_AUTHENTICATED_REMEMBERED'
         );
@@ -170,4 +173,17 @@ abstract class AbstractConnectControllerTest extends TestCase
             ->willReturn($granted)
         ;
     }
+
+    protected function createFirewallMapMock(): FirewallMap
+    {
+        $firewallMap = $this->createMock(FirewallMap::class);
+
+        $firewallMap
+            ->expects($this->any())
+            ->method('getFirewallConfig')
+            ->willReturn(new FirewallConfig('main', '/path/a'))
+        ;
+
+        return $firewallMap;
+    }
 }
diff --git a/tests/Controller/RedirectToServiceControllerTest.php b/tests/Controller/RedirectToServiceControllerTest.php
index 64acb46..27a5ebb 100644
--- a/tests/Controller/RedirectToServiceControllerTest.php
+++ b/tests/Controller/RedirectToServiceControllerTest.php
@@ -21,6 +21,8 @@ use HWI\Bundle\OAuthBundle\Security\OAuthUtils;
 use HWI\Bundle\OAuthBundle\Util\DomainWhitelist;
 use PHPUnit\Framework\MockObject\MockObject;
 use PHPUnit\Framework\TestCase;
+use Symfony\Bundle\SecurityBundle\Security\FirewallConfig;
+use Symfony\Bundle\SecurityBundle\Security\FirewallMap;
 use Symfony\Component\HttpFoundation\Request;
 use Symfony\Component\HttpFoundation\Session\SessionInterface;
 use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
@@ -120,6 +122,19 @@ final class RedirectToServiceControllerTest extends TestCase
         $controller->redirectToServiceAction($this->request, 'facebook');
     }
 
+    protected function createFirewallMapMock(): FirewallMap
+    {
+        $firewallMap = $this->createMock(FirewallMap::class);
+
+        $firewallMap
+            ->expects($this->any())
+            ->method('getFirewallConfig')
+            ->willReturn(new FirewallConfig('main', '/path/a'))
+        ;
+
+        return $firewallMap;
+    }
+
     private function createController(bool $failedUseReferer = false, bool $useReferer = false): RedirectToServiceController
     {
         $resourceOwner = $this->createMock(ResourceOwnerInterface::class);
@@ -140,10 +155,11 @@ final class RedirectToServiceControllerTest extends TestCase
         $utils = new OAuthUtils(
             $this->createMock(HttpUtils::class),
             $this->createMock(AuthorizationCheckerInterface::class),
+            $this->createFirewallMapMock(),
             true,
             'IS_AUTHENTICATED_REMEMBERED'
         );
-        $utils->addResourceOwnerMap($ownerMap);
+        $utils->addResourceOwnerMap('main', $ownerMap);
 
         return new RedirectToServiceController(
             $utils,
diff --git a/tests/Functional/IntegrationTest.php b/tests/Functional/IntegrationTest.php
index c8d42e9..c9aadf0 100644
--- a/tests/Functional/IntegrationTest.php
+++ b/tests/Functional/IntegrationTest.php
@@ -63,6 +63,41 @@ final class IntegrationTest extends WebTestCase
         $this->assertSame($expectedRedirectUrl, $response->headers->get('Location'));
     }
 
+    public function testRequestRedirectApi(): void
+    {
+        $client = static::createClient();
+        $client->disableReboot();
+        $client->getContainer()->set('hwi_oauth.http_client', new MockHttpClient());
+        $client->request('GET', '/private');
+
+        $response = $client->getResponse();
+
+        $this->assertInstanceOf(Response::class, $response);
+        $this->assertSame(302, $response->getStatusCode(), $response->getContent());
+        $this->assertSame('http://localhost/login', $response->headers->get('Location'));
+
+        $crawler = $client->request('GET', $response->headers->get('Location'));
+
+        $response = $client->getResponse();
+        $this->assertInstanceOf(Response::class, $response);
+        $this->assertSame(200, $response->getStatusCode(), 'No landing, got redirect to '.$response->headers->get('Location'));
+
+        $client->click($crawler->selectLink('Api Login')->link());
+
+        $response = $client->getResponse();
+
+        $this->assertInstanceOf(Response::class, $response);
+        $this->assertSame(302, $response->getStatusCode(), $response->getContent());
+        $expectedRedirectUrl = 'https://accounts.google.com/o/oauth2/auth?'
+            .http_build_query([
+                'response_type' => 'code',
+                'client_id' => 'google_client_id',
+                'scope' => 'https://www.googleapis.com/auth/userinfo.email https://www.googleapis.com/auth/userinfo.profile',
+                'redirect_uri' => 'http://localhost/api/check-login/google',
+            ]);
+        $this->assertSame($expectedRedirectUrl, $response->headers->get('Location'));
+    }
+
     public function testRequestCheck(): void
     {
         $redirectLoginFromService = 'http://localhost/check-login/google?'
@@ -110,4 +145,52 @@ final class IntegrationTest extends WebTestCase
         $this->assertSame(302, $response->getStatusCode(), $response->getContent());
         $this->assertSame('http://localhost/', $response->headers->get('Location'));
     }
+
+    public function testRequestCheckApi(): void
+    {
+        $redirectLoginFromService = 'http://localhost/api/check-login/google?'
+            .http_build_query([
+                'code' => 'sOmeRand0m-code',
+                'scope' => 'https://www.googleapis.com/auth/userinfo.email https://www.googleapis.com/auth/userinfo.profile',
+                'authuser' => '0',
+                'session_state' => 'abcde123456789..8787',
+                'prompt' => 'none',
+            ]);
+
+        $httpClient = new MockHttpClient(
+            function ($method, $url, $options) {
+                return new MockResponse(
+                    '{"access_token":"valid-access-token"}',
+                    [
+                        'response_headers' => ['content-type' => 'application/json'],
+                    ]
+                );
+            }
+        );
+
+        $client = static::createClient();
+        $client->disableReboot();
+        $container = $client->getContainer();
+        $container->set('hwi_oauth.http_client', $httpClient);
+
+        $interactiveLoginListener = $this->createMock(CustomEventListener::class);
+        $interactiveLoginListener->expects($this->once())->method('handle');
+        // We attach our custom listener to prove InteractiveLoginEvent fired correctly.
+        // 'security.event_dispatcher.main' Dispatcher is used for Symfony 5.4 and 6.0 under php ^8.0 and ^8.1
+        // and 'event_dispatcher' for all 4.4 and 5.4 under ^7.4
+        foreach (['security.event_dispatcher.api', 'event_dispatcher'] as $dispatcherId) {
+            if ($container->has($dispatcherId)) {
+                $container->get($dispatcherId)
+                    ->addListener(SecurityEvents::INTERACTIVE_LOGIN, [$interactiveLoginListener, 'handle']);
+            }
+        }
+
+        $client->request('GET', $redirectLoginFromService);
+
+        $response = $client->getResponse();
+
+        $this->assertInstanceOf(Response::class, $response);
+        $this->assertSame(302, $response->getStatusCode(), $response->getContent());
+        $this->assertSame('http://localhost/', $response->headers->get('Location'));
+    }
 }
diff --git a/tests/Security/OAuthUtilsTest.php b/tests/Security/OAuthUtilsTest.php
index be80c7d..0c63894 100644
--- a/tests/Security/OAuthUtilsTest.php
+++ b/tests/Security/OAuthUtilsTest.php
@@ -16,6 +16,8 @@ use HWI\Bundle\OAuthBundle\OAuth\State\State;
 use HWI\Bundle\OAuthBundle\Security\Http\ResourceOwnerMap;
 use HWI\Bundle\OAuthBundle\Security\OAuthUtils;
 use PHPUnit\Framework\TestCase;
+use Symfony\Bundle\SecurityBundle\Security\FirewallConfig;
+use Symfony\Bundle\SecurityBundle\Security\FirewallMap;
 use Symfony\Component\DependencyInjection\ServiceLocator;
 use Symfony\Component\HttpFoundation\Request;
 use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
@@ -34,8 +36,8 @@ final class OAuthUtilsTest extends TestCase
 
         $authorizationChecker = $this->createMock(AuthorizationCheckerInterface::class);
 
-        $utils = new OAuthUtils($this->getHttpUtils($url), $authorizationChecker, true, $this->grantRule);
-        $utils->addResourceOwnerMap($this->getMap($url, $redirect, false, true));
+        $utils = new OAuthUtils($this->getHttpUtils($url), $authorizationChecker, $this->createFirewallMapMock(), true, $this->grantRule);
+        $utils->addResourceOwnerMap('main', $this->getMap($url, $redirect, false, true));
 
         $this->assertEquals(
             $redirect,
@@ -51,8 +53,8 @@ final class OAuthUtilsTest extends TestCase
         $request = $this->getRequest($url);
         $redirect = 'https://api.instagram.com/oauth/authorize?redirect='.rawurlencode($url);
 
-        $utils = new OAuthUtils($this->getHttpUtils($url), $this->getAuthorizationChecker(true, $this->grantRule), true, $this->grantRule);
-        $utils->addResourceOwnerMap($this->getMap($url, $redirect, true, false));
+        $utils = new OAuthUtils($this->getHttpUtils($url), $this->getAuthorizationChecker(true, $this->grantRule), $this->createFirewallMapMock(), true, $this->grantRule);
+        $utils->addResourceOwnerMap('main', $this->getMap($url, $redirect, true, false));
 
         $this->assertEquals(
             $redirect,
@@ -76,8 +78,8 @@ final class OAuthUtilsTest extends TestCase
         $request = $this->getRequest($url.'?state='.$state->encode());
         $resource = $this->getMockBuilder(ResourceOwnerInterface::class)->getMock();
 
-        $utils = new OAuthUtils($this->getHttpUtils($url), $this->getAuthorizationChecker(false, $this->grantRule), true, $this->grantRule);
-        $utils->addResourceOwnerMap($this->getMap($url, $redirect, false, false, $resource));
+        $utils = new OAuthUtils($this->getHttpUtils($url), $this->getAuthorizationChecker(false, $this->grantRule), $this->createFirewallMapMock(), true, $this->grantRule);
+        $utils->addResourceOwnerMap('main', $this->getMap($url, $redirect, false, false, $resource));
 
         $resource->expects($this->exactly(2))
             ->method('addStateParameter')
@@ -95,8 +97,8 @@ final class OAuthUtilsTest extends TestCase
         $request = $this->getRequest($url);
         $redirect = 'https://api.instagram.com/oauth/authorize?redirect='.rawurlencode($url);
 
-        $utils = new OAuthUtils($this->getHttpUtils($url), $this->getAuthorizationChecker(false, $this->grantRule), true, $this->grantRule);
-        $utils->addResourceOwnerMap($this->getMap($url, $redirect));
+        $utils = new OAuthUtils($this->getHttpUtils($url), $this->getAuthorizationChecker(false, $this->grantRule), $this->createFirewallMapMock(), true, $this->grantRule);
+        $utils->addResourceOwnerMap('main', $this->getMap($url, $redirect));
 
         $this->assertEquals(
             $redirect,
@@ -115,10 +117,11 @@ final class OAuthUtilsTest extends TestCase
         $utils = new OAuthUtils(
             $this->getHttpUtils($url),
             $this->getAuthorizationChecker(false, 'IS_AUTHENTICATED_FULLY'),
+            $this->createFirewallMapMock(),
             true,
             'IS_AUTHENTICATED_FULLY'
         );
-        $utils->addResourceOwnerMap($this->getMap($url, $redirect));
+        $utils->addResourceOwnerMap('main', $this->getMap($url, $redirect));
 
         $this->assertEquals(
             $redirect,
@@ -155,8 +158,8 @@ final class OAuthUtilsTest extends TestCase
         );
 
         $authorizationChecker = $this->createMock(AuthorizationCheckerInterface::class);
-        $utils = new OAuthUtils($this->getHttpUtils($url), $authorizationChecker, true, $this->grantRule);
-        $utils->addResourceOwnerMap($mapMock);
+        $utils = new OAuthUtils($this->getHttpUtils($url), $authorizationChecker, $this->createFirewallMapMock(), true, $this->grantRule);
+        $utils->addResourceOwnerMap('main', $mapMock);
 
         $resource->expects($this->exactly(2))
             ->method('addStateParameter')
@@ -218,8 +221,8 @@ final class OAuthUtilsTest extends TestCase
             $serviceLocator
         );
 
-        $utils = new OAuthUtils($this->getHttpUtils($url), $authChecker, true, $this->grantRule);
-        $utils->addResourceOwnerMap($mapMock);
+        $utils = new OAuthUtils($this->getHttpUtils($url), $authChecker, $this->createFirewallMapMock(), true, $this->grantRule);
+        $utils->addResourceOwnerMap('main', $mapMock);
 
         $this->assertEquals(
             $url,
@@ -321,6 +324,19 @@ final class OAuthUtilsTest extends TestCase
         return new HttpUtils($urlGenerator);
     }
 
+    private function createFirewallMapMock(): FirewallMap
+    {
+        $firewallMap = $this->createMock(FirewallMap::class);
+
+        $firewallMap
+            ->expects($this->any())
+            ->method('getFirewallConfig')
+            ->willReturn(new FirewallConfig('main', '/path/a'))
+        ;
+
+        return $firewallMap;
+    }
+
     private function getAuthorizationChecker($hasUser, $grantRule)
     {
         $mock = $this->createMock(AuthorizationCheckerInterface::class);