-
-
Notifications
You must be signed in to change notification settings - Fork 9.6k
[SecurityBundle] Add a login form Type #20426
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
Conversation
@@ -29,11 +28,11 @@ | |||
*/ | |||
class UserLoginType extends AbstractType |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would rather have this called a UsernamePasswordType
, but tbh, I think this is something the developer should handle him/herself, because in the end, you don't do anything with the form as the security component handles it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I changed the name.
Despite the Security component handling the form request, I think this can be useful to ease rendering the form, as it will use form themes and automatically retrieve the authentication error.
a8b7a36
to
b380f66
Compare
Personally I would like to support the form mapping completely in the security component (optional), you could easily extend the form and add an extra field or flag. |
I'm not sure I understand. Do you suggest to move the Type class to the Security component, e.g. |
Possibly, or a FormBridge in the security component. A form type is to map data to forms, but why read it manually from the request afterwards? Why not simply allow a plug & play variant to handle that form and get the data from there? The data used in the form could then easily be passed to the right method so you could load user by username with an object instead (think company + username as login). |
$event->getForm()->addError(new FormError($error->getMessage())); | ||
} | ||
|
||
$event->setData(array_replace((array) $event->getData(), array( | ||
'username' => $request->getSession()->get(Security::LAST_USERNAME), | ||
'_username' => $this->authenticationUtils->getLastUsername(), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is the same as passing an option right?
->add('_username', 'Symfony\Component\Form\Extension\Core\Type\TextType', [
'data' => $this->authenticationUtils->getLastUsername()
]);
That's how we did it.. and it works great :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@ro0NL : That's not exactly the same. Using data
will lock the data and any event listener on PRE_SET_DATA
would not be able to change it anymore.
https://github.com/symfony/symfony/blob/master/src/Symfony/Component/Form/Form.php#L323
If this form type is meant to be extended (using getParent()
) to provide more features in the final application, it must be considered.
|
||
if ($error) { | ||
$builder->addEventListener(FormEvents::PRE_SET_DATA, function (FormEvent $event) { | ||
if (null !== $error = $this->authenticationUtils->getLastAuthenticationError()) { | ||
$event->getForm()->addError(new FormError($error->getMessage())); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would be nice if these are translated in the current domain :) And not sure.. but i think in that case you should use $error->getMessageKey()
.
@@ -42,31 +41,23 @@ public function __construct(RequestStack $requestStack) | |||
public function buildForm(FormBuilderInterface $builder, array $options) | |||
{ | |||
$builder | |||
->add('username', 'Symfony\Component\Form\Extension\Core\Type\TextType') | |||
->add('password', 'Symfony\Component\Form\Extension\Core\Type\PasswordType') | |||
->add('_username', 'Symfony\Component\Form\Extension\Core\Type\TextType') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should specific field options be configurable?
Ie. add($options['username_field_name'], $options['username_field_type'], $options['username_field_options'])
To clarify, our login was email/password based and field names are configured via security (username/password
without leading _
).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👎 for having custom type and options, this overcomplicates things.
However I wonder if we can take advantage of the new FirewallConfig
class to get the right field names dynamically instead of hardcoding _username
, _password
and _csrf_token
values in the type, that would be cool!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agree.. but if we cant do EmailType
instead of TextType
and assign a label or something im not sure how convenient this actually is. Ie. i think i prefer having this definition in the application layer anyway (like tests did before).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm having an EmailType
as you suggest is a good use case to me.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
However I wonder if we can take advantage of the new FirewallConfig class to get the right field names dynamically instead of hardcoding _username, _password and _csrf_token values in the type, that would be cool!
That's probably too much for the need IMHO.
This will probably require defining parameters in the container from the Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory\FormLoginFactory
, inject the FirewallMap
inside the UserLoginType
along with the RequestStack
, retrieve the firewall name and guess the right parameters to use from it. 😕
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also this would tied the new form type to the SecurityBundle
.
So @iltar 's suggestion could not be applied.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As i literally built this today, i think a cookbook example (How to built a login form type with security) would have been most useful. As this is mostly is about explaining howto integrate it with the security component and what the caveats are.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually I think you're right @ro0NL. I don't see much value having this form type in the core.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I kind of like the feature giving any form login controller the ability to do
$loginForm = $this->createLoginForm()
and having the abstract to do
$this->createForm(UsernamePasswordType::class)
, some options could also be injected at this step.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It heavily relies on the parameter configuration in security, you probably end up with createNamedForm('', UsernamePasswordType::class)
, again, depending on your security configuration.
So i think this is only viable, if a) it's fully driven by security (it just works) and b) it's customizable.
Did i mention remember me services already? Imo. this should add a checkbox if needed...
Hello, I was wandering about this issue lately. My idea for handling this case was not to provide a form type in the framwork (a default one can still be provided) but to provide a new listener for the firewall to handle a form (something like The user could provide it's form type class in the config (or use a default form type like in this pr) What do you think ? |
I think it's the better approach indeed 👍
Not sure it must be a requirement, as the form error comes from the session storage anyway. |
I don't think this is a good idea: you would have to configure the template used to render the error page. What if you need some custom logic then? What if the template requires additional data normally provided by a controller? A form type can be useful for rendering the login and error pages because we can map security errors to it and use form themes. Using it to validate the request has no real added value IMO. |
)); | ||
} | ||
|
||
private function getFirewallOption($firewallName, $optionName, $default) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is the part where I need something like #20591.
The main idea was to have the listener let your login action do it's rendering when there are form errors, and that's why i suggested the login_path and login_check should be the same. On GET request matching the login_path (= login_check) the listener creates the form and make it accessible from a controller (maybe through authenticationUtils ?)
Maybe i missed something but I found it difficult to add a simple email validation to my username field and notblank to the password field. It seems a common use case. Anyway the listener could be configured to default to the form type of this PR so it could be really easy to setup and configure for default setup. @HeahDude @javiereguiluz I don't want to pollute this PR with non related things, so maybe it should be worth re-opening #20645 ? |
For people still interested in a login form type, I published a bundle for that: https://github.com/julienfalque/login-form-bundle. |
Add a
UserLoginType
(actually the one that already exists in tests) to ease creating simple login forms.