Behat authenticate Symfony2 user Behat authenticate Symfony2 user symfony symfony

Behat authenticate Symfony2 user


Oh my. It doesn't work because the DIC inside your FeatureContext isn't shared with your app - your app has separate kernel and DIC. You can get it through Mink. Or, you can simply do it right way :-)

Right way means, that every part of behavior, that is observable by the enduser, should be described inside *.feature, not inside FeatureContext. It means, that if you want to login a user, you should simply describe it with steps (like: "i am on /login", "and i fill in username ...", "i fill in password" and stuf). If you want to do it in multiple times - you should create a metastep.

Metasteps are simply steps, that describe multiple other steps, for example - "i am logged in as everzet". You could read bout them here: http://docs.behat.org/guides/2.definitions.html#step-execution-chaining


Here is an solution for login with OAuth I've used. After number of times of searching for the answer and landing on this page I thought it would be great to share the solution. Hopefully it will help someone.

Background: Symfony2 App using HWIOAuthBundle, hooked up to some OAuth2 provider.

Problem: How do I implement Given I'm logged in when Behat context in not shared with Symfony context?

Solution:

HWIOAuthBundle uses @buzz service for all API calls to OAuth providers. So all you need to do is replace Buzz client with your implementation which doesn't call external services, but returns the result straight away. This is my implementation:

<?phpnamespace Acme\ExampleBundle\Mocks;use Buzz\Client\ClientInterface;use Buzz\Message\MessageInterface;use Buzz\Message\RequestInterface;class HttpClientMock implements ClientInterface{    public function setVerifyPeer()    {        return $this;    }    public function setTimeout()    {        return $this;    }    public function setMaxRedirects()    {        return $this;    }    public function setIgnoreErrors()    {        return $this;    }    public function send(RequestInterface $request, MessageInterface $response)    {        if(preg_match('/\/oauth2\/token/', $request->getResource()))        {            $response->setContent(json_encode([                'access_token' => 'valid',                'token_type' => 'bearer',                'expires_in' => 3600            ]));        }        elseif(preg_match('/\/oauth2\/me/', $request->getResource()))        {            $response->setContent(json_encode([                'id' => 1,                'username' => 'doctor',                'realname' => 'Doctor Who'            ]));        }        else throw new \Exception('This Mock object doesn\'t support this resource');    }}

Next step is to hijack the class used by HWIOAuthBundle/Buzz and replace it with the implementation above. We need to do it only for test environment.

# app/config/config_test.ymlimports:    - { resource: config_dev.yml }parameters:    buzz.client.class: Acme\ExampleBundle\Mocks\HttpClientMock

And finally, you need to set require_previous_session to false for test environment - therefore I suggest to pass it as parameter.

# app/config/security.ymlsecurity:    firewalls:        secured_area:            oauth:                require_previous_session: false

Now you can implement your step like this.

Specification:

Feature: Access restricted resource  Scenario: Access restricted resource    Given I'm logged in    When I go to "/secured-area"    Then I should be on "/secured-area"    And the response status code should be 200

Implementation:

<?php/** * @Given /^I\'m logged in$/ */public function iMLoggedIn(){    $this->getSession()->visit($this->locatePath('/login/check-yourOauthProvider?code=validCode'));}

The code you're passing is not relevant, anything you pass will be OK as it's not being checked. You can customise this behaviour in HttpClientMock::send method.


http://robinvdvleuten.nl/blog/handle-authenticated-users-in-behat-mink/ is simple, clean article on how to create a login session and set the Mink session cookie so that the Mink session is logged in. This is much better than using the login form every time to login a user.