-
-
Notifications
You must be signed in to change notification settings - Fork 212
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Panther with Authentication #283
Comments
I have the same issue. |
Hello, If anyone is getting the same error about user authentication in functional tests implementing the token, I found the issue. This was not related to Panther but to the security component, indeed I did not notice the logs telling me that the token needed to be refresh and then the user was automatically deauthenticated => cannot refresh the token because the user has changed. This means that symfony was not able to compare properly the user with the authentication system which I guess is by default comparing username password and salt. But in the case of CAS authentication you do not have password or salt but only the username. In order to solve this, you need to implement Symfony\Component\Security\Core\User\EquatableInterface and the related method isEqualTo.
So when the security component will check if it needs a refresh, it will work properly. I am now able to run a classic WebTestCase and to access my home page. The only issue remaining is the invalid cookie domain which is preventing me to use Panther for now. I will investigate on this side and let you know in case I fund something. Cheers |
Hello, I have tried many things on my side, and this is not possible to get rid of the "invalid cookie domain" error, unless you create a new page (simple page with nothing or an h1 is enough) to initiate the process. This is not related to Panther but to php webdriver. I found many entries about it on stackoverflow. The real issue is that my code to log in is working with absolutely no issue doing a WebTestCase but is not working with Panther.
As far as I understand, despite I had the cookie to Panther Cookie Jar it is not used to authenticate and it keeps sending me to the CAS authentication page. Here is the content of the cookie Jar of my Panther client:
Based on the log in my test environment I can see no entry from the Security component like using the WebTestCase. So my conclusion is that the cookie is not even considered and used for authentication in Symfony. PantherClient is just redirecting me to the authentication page. Does anyone have a clue ? Thanks a lot, |
here same problem... some glue? thanks! |
Same problem here... any idea ? |
@andrescevp , @gponty , I ended up using classic classic Web test case and the Crawler which are working perfectly with the code above. |
This it what i had to throw together to get it working. I had to set
|
Hi @JohnstonCode, you should check the upcoming changes on SF 5.1 :) There will be a new auth system for the tests. As far as I understood there will be no need for us anymore to generate this code. Cheers :) |
Thanks, looks good hopefully this resolves our issue. |
Same issue here :/ |
Same here with : $client = static::createPantherClient();
/** @var User $user */
$user = $this->getSuperAdminAccount();
$session = self::$container->get('session');
$token = new UsernamePasswordToken($user, null, 'main', $user->getRoles());
$session->set('_security_main', serialize($token));
$session->save();
$cookie = new Cookie($session->getName(), $session->getId());
$client->getCookieJar()->set($cookie); Any solution ? |
For setting-up cookie's domain, you have to request firstly on the domain, then set-up the cookie...
|
@nizarRebhi thanks for the comment, working a treat for me now |
So I've been investigating the login process as well and it seems Us cypress users are used to just send a POST request to the login Url that gets us logged in for the e2e tests without going through the UI login process. May be panther can allow for POST calls in their client (I've tried this and it just plainly said POST calls are not supported) or provide a shortcut function like |
@shadowc Have you been able to login yourself? I have tried methods outlined earlier - but continue to get CSRF errors. @Sethbass have you found a solution to getting PantherClient to work with authentication? @JohnstonCode did you say you got it working with PantherClient and authentication? Would love to get this working.
|
Dear SethBass, I'm trying to do functional tests on an app protected by CAS and I'm not able to pass the authentication yet. I have a few questions, could you please help me?
Thanks! |
Hi, Has anyone found a solution to use : PS : it doesn't work so (Error: Call to undefined method Symfony\Component\Panther\Client::loginUser()) Symfony 5.3 Thanks! |
For me it was the issue with empty cookies when env switched to "test" Try to comment out mock session storage factory: #framework.yaml
when@test:
framework:
test: true
# session:
# storage_factory_id: session.storage.factory.mock_file |
I want to use panther to test an application which uses a SSO to authenticate. Unfortunantaly up to now it is not possible to test this application with panther because of the missing login fuction wich is implemented in the WebTestCase of the kernel application. I would be so great if here is an update planned. |
I'm also experiencing this same issue.
Previously, I tried creating the panther client with the hostname and port configured so that I can use PhpStorm breakpoints with traffic coming from the Panther browser, and I could see that the cookies are being set as expected. But the |
Still facing this problem, any solution ? |
I can authenticate in Panther with CAS, but I am not using a bundle but rather my own Guard implementation. I can navigate to pages that require authentication, and click through things, but I have hit a wall where my Ajax POST requests are not working. They get triggered but return 404 not found. Posting to json endpoints provided 404, but to HTML endpoints gives a 302 redirect to my login page. More info on my POST issue and my authentication implementation here: #547 While I am not using a CAS bundle, I based by Guard implementation on this (its a few years old so probably a bit out of date, but the same basic idea): https://github.com/PRayno/CasAuthBundle/blob/master/Security/CasAuthenticator.php P.S. I don't implement EquatableInterface, and I do have to hit / before setting my login token. I've tested implementing EquatableInterface just now and it does not solve my POST issue. |
@shadowc, you said
Where is this documented? |
How about using a bundle for CAS ? Wouldn't it be a bit more practical? |
Interestingly, other ajax posts seem to work, so maybe there is something about this specific implementation that's wonky. I will test further and report back. |
@drupol no, I've found it is not. The CAS protocol is very simple and easy to implement (at least on the consumer side). My coworkers who are depending on phpCas and various CAS bundles have experienced breakage from updates to our CAS servers, due to their package implementations. I, on the other hand, have not :) In addition, if something breaks or changes (hasn't happened in 4 years), its much easier/quicker for me to fix it myself rather than wait on third party developers to acknowledge and fix the issue or accept my pull request. In general, programming has become way too reliant on dependencies for simple functionality that can be implemented/controlled in house. This comes with all sorts of problems. I can't say my software if free from this problem altogether, not by a long shot. But, thankfully, it is in the context of CAS. If CAS were more complex, I'd reach for a library, but its very simple XML parsing. Out of curiosity, which CAS bundle do you recommend? P.S. I've resolved my ajax POST mystery, and it turned out authentication was not the problem. |
Try this one: https://github.com/ecphp/cas-bundle Sorry for the brevity, replying from the smartphone. |
all good, thanks for the recommendation EDIT: I see you're the primary contributor, nice! Thanks for the FOSS offering. |
Not documented. I believe I was referring to an error message that came out in the console. This message is a year old so things could have changed since! |
You're welcome! I developed this bundle for the European Commission and we are using everyday in every Symfony app. |
Hi, did anybody has update about this ? Using Symfony 5.4 and still facing the issue. |
Hi! I'm using the following base class for our E2E tests (it is based on previous comments in this discussion and code in //...
use Symfony\Component\Panther\Client as PantherClient;
//...
abstract class PantherTestCase extends KernelTestCase
{
use WebTestAssertionsTrait;
/**
* @param string[] $options An array of options to pass to the createKernel class
* @param string[] $kernelOptions
* @param string[] $managerOptions
*/
protected static function createAuthenticatedPantherClient(
User $user,
array $options = [],
array $kernelOptions = [],
array $managerOptions = []
): PantherClient
{
$client = self::createPantherClient();
// without this request, the session cookie could not be set
$client->request('GET', '/');
// Inspired by \Symfony\Bundle\FrameworkBundle\KernelBrowser::loginUser()
$token = new UsernamePasswordToken($user, 'main', $user->getRoles());
$container = self::getContainer();
$container->get('security.untracked_token_storage')->setToken($token);
$session = $container->get('session.factory')->createSession();
$session->set('_security_main', serialize($token));
$session->save();
$cookie = new Cookie($session->getName(), $session->getId());
$client->getCookieJar()->set($cookie);
return $client;
} |
Thanks, @mhujer , this looks very promising. When I make the first call after getting the client, I get
With the $client->request('GET', '/'); line, I made some progress, but the authenticated route returns the login page. With the when@test:
framework:
test: true
# session:
# storage_factory_id: session.storage.factory.mock_file And got the curious error:
The test itself is using the latest (6.1) symfony demo. use App\Entity\User;
use Symfony\Component\BrowserKit\Cookie;
use Symfony\Component\Panther\Client;
use Symfony\Component\Panther\PantherTestCase;
use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;
class E2eTest extends PantherTestCase
{
/**
* @param string[] $options An array of options to pass to the createKernel class
* @param string[] $kernelOptions
* @param string[] $managerOptions
*/
protected static function createAuthenticatedPantherClient(
// User $user,
?string $username=null,
array $options = [],
array $kernelOptions = [],
array $managerOptions = []
): Client
{
$client = self::createPantherClient();
if ($username) {
$client->request('GET', '/'); // removing this line shows a different error
$container = self::getContainer();
$user = $container->get('doctrine')->getRepository(User::class)->findOneBy(['username' => $username]);
assert($user, "Invalid user $username, not in user database");
// Inspired by \Symfony\Bundle\FrameworkBundle\KernelBrowser::loginUser()
$token = new UsernamePasswordToken($user, 'main', $user->getRoles());
$container = self::getContainer();
$container->get('security.untracked_token_storage')->setToken($token);
$session = $container->get('session.factory')->createSession();
$session->set('_security_main', serialize($token));
$session->save();
$cookie = new Cookie($session->getName(), $session->getId());
$client->getCookieJar()->set($cookie);
}
return $client;
}
public function testMyApp(): void
{
// static $client = static::createPantherClient(); // Your app is automatically started using the built-in web server
$client = $this->createAuthenticatedPantherClient('jane_admin');
$client->request('GET', '/'); You mentioned it was working for you -- can you post the entire test in a gist or repo somewhere? Or here? Thanks. |
@tacman This no longer works since symfony/symfony#45662 . Unfortunately I cannot find ANY workaround, since that change. I can't even login a user using the login form. For some reason it submits it but nothing happens... I literally cannot get Authentication to work with panther |
@shadydealer I've worked around this in 5.4. Here are my related links. symfony/symfony#46961 (comment) I made a PR to allow sessions in tests for persistent interactions, but no one at Symfony has chimed in: My implementation may need work, but if people want this feature, they should comment on and upvote the PR. |
@arderyp thanks for the reply. I managed to get my login form to work. What I had missed and am now realizing is not really part of the documentation and probably should be is that I hadn't set up my panther environment correctly. I had to: framework:
...
#
# remove or comment these 2 lines below.
#session:
# storage_factory_id: session.storage.factory.mock_file why this is needed is explained in the following issue: #159 Once all of these changes are in place I can successfully log in a user using the login form, so at least that works. But I think it should be explained in the documentation a bit better. I realize this solution is not ideal for your case (I think probably everybody would prefer not having to login using their login form) but at least it's something that others might find useful, I think. Unfortunately I've abandoned trying to set panther up and can no longer be of help on this particular issue, but I did try to replicate the |
Hi all. I've got a workable solution for Symfony 6.1. May not be perfect for everyone but maybe can be useful for some people.
Mock session storage is the default mechanism when running tests to help avoid complications with native sessions. It essentially dumps the session to a flat file (i.e., "/app/var/cache/{APP_ENV}/sessions"). So by ensuring that both See below working example of helper method
|
@plotbox-io this is a very interesting alternative to my approach. Great write up and clarity. Your solution feels a little more "proper" than mine, so I may move over to your approach in the coming months |
I think this was exactly the piece that I was missing in order to get this to work, nice work @plotbox-io |
@plotbox-io maybe you can create a pull request and try to get the loginUser method merged? I would also suggest the following changes: protected function logInUser(object $user, String $firewallContext): void
{
if (!interface_exists(UserInterface::class)) {
throw new \LogicException(sprintf('"%s" requires symfony/security-core to be installed.', __METHOD__));
}
if (!$user instanceof UserInterface) {
throw new \LogicException(sprintf('The first argument of "%s" must be instance of "%s", "%s" provided.', __METHOD__, UserInterface::class, get_debug_type($user)));
}
// Make a single request to avoid 'Invalid cookie domain' error
$this->request('GET', '/login');
// This is the key part - I've just hardcoded the mock session file to be where I know the panther one will
// be stored in the other (panther env) webserver
$session = new Session(new MockFileSessionStorage('/app/var/cache/panther/sessions'));
$token = new UsernamePasswordToken($user, $firewallContext, $user->getRoles());
$session->set('_security_'.$firewallContext, serialize($token));
$session->save();
$cookie = new Cookie($session->getName(), $session->getId());
$this->getCookieJar()->set($cookie);
}
(2 and 3 are taken from here) |
@shadydealer Thanks for the updated code. Probably would want to inject SessionStorageFactoryInterface as well rather than hardcode. A client of the library could feasibly use a database, redis, or some other method to share the session between the test/panther environment. I've made a PR here. Let's see what the Symfony team thinks. Regards, |
I like these ideas. Likewise, it would be nice to have an optional |
Hello,
First I would like to thank you to improve this part of symfony and helping us to get rid of Behat.
I have created a symfony app which needs authentication. This means that / is protected by the firewall main and every URL needs an authenticated user.
On top of that I have custom authenticator which extends AbstractGuardAuthenticator. The authentication process is based on CAS (not my choice I have to cope with it).
I have tried to start my functional tests using Panther and followed the documentation here :
https://github.com/symfony/panther
So far so good :) Then I have to deal immediately with the authentication, so I found this tutorial :
https://symfony.com/doc/4.3/testing/http_authentication.html
First thing I have discovered is that I need to visit a page on my website otherwise I get this error :
Ok then I have created a test page accessible anonymously and got rid of the error. Not sure this is a best practice.
Then I was able to run the following code :
The problem is that I am still redirected to the CAS authentication page calling
$crawler = $this->client->request('GET', '/');
My user is always NULL which means I am not authenticated.
I thought Session Storage Mock file could be a suspect but actually all my tests were unsuccessful.
Help :)
Thanks a lot for reading
The text was updated successfully, but these errors were encountered: