Manual Authentication / Registration

Keep on Learning!

If you liked what you've learned so far, dive in!
Subscribe to get access to this tutorial plus
video, code and script downloads.

Start your All-Access Pass
Buy just this tutorial for $12.00

With a Subscription, click any sentence in the script to jump to that part of the video!

Login Subscribe

Hey! You've made it through almost this entire tutorial! Nice work! I have just a few more tricks to show you before we're done - and they're good ones!

Creating the Registration Form

First, I want to create a registration form. Find your code and open SecurityController. In addition to login and logout, add a new public function register():

... lines 1 - 8
class SecurityController extends AbstractController
{
... lines 11 - 38
public function register()
{
... line 41
}
}

Give it a route - /register and a name: app_register:

... lines 1 - 8
class SecurityController extends AbstractController
{
... lines 11 - 35
/**
* @Route("/register", name="app_register")
*/
public function register()
{
... line 41
}
}

Here's the interesting thing about registration. It has nothing to do with security! Think about it. What is registration? It's just a form that creates a new record in the User table. That's it! That's just database stuff.

So then... why are we even talking about this in a security tutorial? Well... to create the best user experience, there will be just a little bit of security right at the end. Because, after registration, I want to instantly authenticate the new user.

More on that later. Right now, render a template: $this->render('security/register.html.twig'):

... lines 1 - 8
class SecurityController extends AbstractController
{
... lines 11 - 35
/**
* @Route("/register", name="app_register")
*/
public function register()
{
return $this->render('security/register.html.twig');
}
}

Then... I'll cheat: in security/, copy the login.html.twig template, paste and call it register.html.twig:

{% extends 'base.html.twig' %}
{% block title %}Login!{% endblock %}
{% block stylesheets %}
{{ parent() }}
<link rel="stylesheet" href="{{ asset('css/login.css') }}">
{% endblock %}
{% block body %}
<div class="container">
<div class="row">
<div class="col-sm-12">
<form class="form-signin" method="post">
{% if error %}
<div class="alert alert-danger">{{ error.messageKey|trans(error.messageData, 'security') }}</div>
{% endif %}
<h1 class="h3 mb-3 font-weight-normal">Please sign in</h1>
<label for="inputEmail" class="sr-only">Email address</label>
<input type="email" value="{{ last_username }}" name="email" id="inputEmail" class="form-control" placeholder="Email address" required autofocus>
<label for="inputPassword" class="sr-only">Password</label>
<input type="password" name="password" id="inputPassword" class="form-control" placeholder="Password" required>
<input type="hidden" name="_csrf_token"
value="{{ csrf_token('authenticate') }}"
>
<div class="checkbox mb-3">
<label>
<input type="checkbox" name="_remember_me"> Remember me
</label>
</div>
<button class="btn btn-lg btn-primary btn-block" type="submit">
Sign in
</button>
</form>
</div>
</div>
</div>
{% endblock %}

Let's see: change the title, delete the authentication error stuff and I am going to add a little comment here that says that we should replace this with a Symfony form later:

... lines 1 - 2
{% block title %}Register!{% endblock %}
... lines 4 - 10
{% block body %}
<div class="container">
<div class="row">
<div class="col-sm-12">
{# todo - replace with a Symfony form! #}
<form class="form-signin" method="post">
... lines 17 - 30
</form>
</div>
</div>
</div>
{% endblock %}

We haven't talked about the form system yet, so I don't want to use it here. But, normally, I would use the form system because it handles validation and automatically adds CSRF protection.

But, to show off how to manually authenticate a user after registration, this HTML form will work beautifully. Change the h1, remove the value= on the email field so that it always starts blank and take out the CSRF token:

... lines 1 - 2
{% block title %}Register!{% endblock %}
... lines 4 - 10
{% block body %}
<div class="container">
<div class="row">
<div class="col-sm-12">
{# todo - replace with a Symfony form! #}
<form class="form-signin" method="post">
<h1 class="h3 mb-3 font-weight-normal">Register</h1>
<label for="inputEmail" class="sr-only">Email address</label>
<input type="email" name="email" id="inputEmail" class="form-control" placeholder="Email address" required autofocus>
<label for="inputPassword" class="sr-only">Password</label>
<input type="password" name="password" id="inputPassword" class="form-control" placeholder="Password" required>
... lines 22 - 30
</form>
</div>
</div>
</div>
{% endblock %}

We do need CSRF protection on this form... but I'll skip it for now, because we'll refactor this into a Symfony form in a future tutorial.

And finally, hijack the "remember me" checkbox and turn it into a terms box. We'll say:

Agree to terms I for sure read

... lines 1 - 2
{% block title %}Register!{% endblock %}
... lines 4 - 10
{% block body %}
<div class="container">
<div class="row">
<div class="col-sm-12">
{# todo - replace with a Symfony form! #}
<form class="form-signin" method="post">
<h1 class="h3 mb-3 font-weight-normal">Register</h1>
<label for="inputEmail" class="sr-only">Email address</label>
<input type="email" name="email" id="inputEmail" class="form-control" placeholder="Email address" required autofocus>
<label for="inputPassword" class="sr-only">Password</label>
<input type="password" name="password" id="inputPassword" class="form-control" placeholder="Password" required>
<div class="checkbox mb-3">
<label>
<input type="checkbox" name="_remember_me" required> Agree to terms I for sure read
</label>
</div>
... lines 28 - 30
</form>
</div>
</div>
</div>
{% endblock %}

Oh, and update the button: Register:

... lines 1 - 2
{% block title %}Register!{% endblock %}
... lines 4 - 10
{% block body %}
<div class="container">
<div class="row">
<div class="col-sm-12">
{# todo - replace with a Symfony form! #}
<form class="form-signin" method="post">
<h1 class="h3 mb-3 font-weight-normal">Register</h1>
<label for="inputEmail" class="sr-only">Email address</label>
<input type="email" name="email" id="inputEmail" class="form-control" placeholder="Email address" required autofocus>
<label for="inputPassword" class="sr-only">Password</label>
<input type="password" name="password" id="inputPassword" class="form-control" placeholder="Password" required>
<div class="checkbox mb-3">
<label>
<input type="checkbox" name="_remember_me" required> Agree to terms I for sure read
</label>
</div>
<button class="btn btn-lg btn-primary btn-block" type="submit">
Register
</button>
</form>
</div>
</div>
</div>
{% endblock %}

Let's see how it looks! Move over, go to /register and... got it! Logout, then move back over and open up base.html.twig. Scroll down just a little bit to find the "Login" link. Let's create a second link that points to the new app_register route. Say, "Register":

<!doctype html>
<html lang="en">
... lines 3 - 15
<body>
... lines 17 - 22
<nav class="navbar navbar-expand-lg navbar-dark navbar-bg mb-5">
... lines 24 - 27
<div class="collapse navbar-collapse" id="navbarNavDropdown">
... lines 29 - 40
<ul class="navbar-nav ml-auto">
{% if is_granted('ROLE_USER') %}
... lines 43 - 58
<li class="nav-item">
<a style="color: #fff;" class="nav-link" href="{{ path('app_register') }}">Register</a>
</li>
{% endif %}
</ul>
</div>
</nav>
... lines 66 - 83
</body>
</html>

Move back and check it out. Not bad!

Handing the Registration Submit

Just like with the login form, because there is no action= on the form, this will submit right back to the same URL. But, unlike login, because this is just a normal page, we are going to handle that submit logic right inside of the controller.

First, get the Request object by adding an argument with the Request type hint: the one from HttpFoundation. Below, I'm going to add another reminder to use the Symfony form & validation system later:

... lines 1 - 11
class SecurityController extends AbstractController
{
... lines 14 - 41
public function register(Request $request, UserPasswordEncoderInterface $passwordEncoder)
{
// TODO - use Symfony forms & validation
... lines 45 - 55
}
}

Then, to only process the data when the form is being submitted, add if ($request->isMethod('POST')):

... lines 1 - 11
class SecurityController extends AbstractController
{
... lines 14 - 41
public function register(Request $request, UserPasswordEncoderInterface $passwordEncoder)
{
// TODO - use Symfony forms & validation
if ($request->isMethod('POST')) {
... lines 46 - 52
}
... lines 54 - 55
}
}

Inside... our job is simple! Registration is nothing more than a mechanism to create a new User object. So $user = new User(). Then set some data on it: $user->setEmail($request->request->get('email')):

... lines 1 - 11
class SecurityController extends AbstractController
{
... lines 14 - 41
public function register(Request $request, UserPasswordEncoderInterface $passwordEncoder)
{
// TODO - use Symfony forms & validation
if ($request->isMethod('POST')) {
$user = new User();
$user->setEmail($request->request->get('email'));
... lines 48 - 52
}
... lines 54 - 55
}
}

Remember $request->request is the way that you get $_POST data. And, the names of the fields on our form are name="email" and name="password". But before we handle the password, add $user->setFirstName(). This field is required in the database... but, we don't actually have that field on the form. Just use Mystery for now:

... lines 1 - 11
class SecurityController extends AbstractController
{
... lines 14 - 41
public function register(Request $request, UserPasswordEncoderInterface $passwordEncoder)
{
// TODO - use Symfony forms & validation
if ($request->isMethod('POST')) {
$user = new User();
$user->setEmail($request->request->get('email'));
$user->setFirstName('Mystery');
... lines 49 - 52
}
... lines 54 - 55
}
}

In a real app, I would either add this field to the registration form, or make it nullable in the database, so it's optional.

Finally, let's set the password. But... of course! We are never ever, ever, ever going to save the plain password. We need to encode it. We already did this inside of UserFixture:

... lines 1 - 7
use Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface;
class UserFixture extends BaseFixture
{
private $passwordEncoder;
public function __construct(UserPasswordEncoderInterface $passwordEncoder)
{
$this->passwordEncoder = $passwordEncoder;
}
protected function loadData(ObjectManager $manager)
{
$this->createMany(10, 'main_users', function($i) use ($manager) {
... lines 22 - 29
$user->setPassword($this->passwordEncoder->encodePassword(
$user,
'engage'
));
... lines 34 - 40
});
$this->createMany(3, 'admin_users', function($i) {
... lines 44 - 48
$user->setPassword($this->passwordEncoder->encodePassword(
$user,
'engage'
));
... lines 53 - 54
});
... lines 56 - 57
}
}

Ah yes, the key was the UserPasswordEncoderInterface service. In our controller, add another argument: UserPasswordEncoderInterface $passwordEncoder:

... lines 1 - 8
use Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface;
... lines 10 - 11
class SecurityController extends AbstractController
{
... lines 14 - 41
public function register(Request $request, UserPasswordEncoderInterface $passwordEncoder)
{
... lines 44 - 55
}
}

Below, we can say $passwordEncoder->encodePassword(). This needs the User object and the plain password that was just submitted: $request->request->get('password'):

... lines 1 - 11
class SecurityController extends AbstractController
{
... lines 14 - 41
public function register(Request $request, UserPasswordEncoderInterface $passwordEncoder)
{
// TODO - use Symfony forms & validation
if ($request->isMethod('POST')) {
... lines 46 - 48
$user->setPassword($passwordEncoder->encodePassword(
$user,
$request->request->get('password')
));
}
... lines 54 - 55
}
}

We are ready to save! Get the entity manager with $em = $this->getDoctrine()->getManager(). Then, $em->persist($user) and $em->flush():

... lines 1 - 11
class SecurityController extends AbstractController
{
... lines 14 - 41
public function register(Request $request, UserPasswordEncoderInterface $passwordEncoder)
{
// TODO - use Symfony forms & validation
if ($request->isMethod('POST')) {
... lines 46 - 48
$user->setPassword($passwordEncoder->encodePassword(
$user,
$request->request->get('password')
));
$em = $this->getDoctrine()->getManager();
$em->persist($user);
$em->flush();
... lines 57 - 58
}
... lines 60 - 61
}
}

All delightfully boring code. This looks a lot like what we're doing in our fixtures.

Finally, after any successful form submit, we always redirect. Use return $this->redirectToRoute(). This is the shortcut method that we were looking at earlier. Redirect to the account page: app_account:

... lines 1 - 11
class SecurityController extends AbstractController
{
... lines 14 - 41
public function register(Request $request, UserPasswordEncoderInterface $passwordEncoder)
{
// TODO - use Symfony forms & validation
if ($request->isMethod('POST')) {
... lines 46 - 48
$user->setPassword($passwordEncoder->encodePassword(
$user,
$request->request->get('password')
));
$em = $this->getDoctrine()->getManager();
$em->persist($user);
$em->flush();
return $this->redirectToRoute('app_account');
}
... lines 60 - 61
}
}

Awesome! Let's give this thing a spin! I'll register as ryan@symfonycasts.com, password engage. Agree to the terms that I for sure read and... Register! Bah! That smells like a Ryan mistake! Yep! Use $this->getDoctrine()->getManager():

... lines 1 - 11
class SecurityController extends AbstractController
{
... lines 14 - 41
public function register(Request $request, UserPasswordEncoderInterface $passwordEncoder)
{
// TODO - use Symfony forms & validation
if ($request->isMethod('POST')) {
... lines 46 - 53
$em = $this->getDoctrine()->getManager();
... lines 55 - 58
}
... lines 60 - 61
}
}

That's what I meant to do.

Move over and try this again: ryan@symfonycasts.com, password engage, agree to the terms that I read and... Register!

Authentication after Registration

Um... what? We're on the login form? What happened? First, according to the web debug toolbar, we are still anonymous. That makes sense: we registered, but we did not login. After registration, we were redirected to /account...

... lines 1 - 11
class SecurityController extends AbstractController
{
... lines 14 - 41
public function register(Request $request, UserPasswordEncoderInterface $passwordEncoder)
{
// TODO - use Symfony forms & validation
if ($request->isMethod('POST')) {
... lines 46 - 57
return $this->redirectToRoute('app_account');
}
... lines 60 - 61
}
}

But because we are not logged in, that sent us here.

This is not the flow that I want my users to experience. Nope, as soon as the user registers, I want to log them in automatically.

Oh, and there's also another problem. Open LoginFormAuthenticator and find onAuthenticationSuccess():

... lines 1 - 19
class LoginFormAuthenticator extends AbstractFormLoginAuthenticator
{
... lines 22 - 74
public function onAuthenticationSuccess(Request $request, TokenInterface $token, $providerKey)
{
if ($targetPath = $this->getTargetPath($request->getSession(), $providerKey)) {
return new RedirectResponse($targetPath);
}
return new RedirectResponse($this->router->generate('app_homepage'));
}
... lines 83 - 87
}

We added some extra code here to make sure that if the user went to, for example, /admin/comment as an anonymous user, then, after they log in, they would be sent back to /admin/comment.

And... hey! I want that same behavior for my registration form! Imagine that you're building a store. As an anonymous user, I add some things to my cart and finally go to /checkout. But because /checkout requires me to be logged in, I'm sent to the login form. And because I don't have an account yet, I instead click to register and fill out that form. After submitting, where should I be taken to? That's easy! I should definitely be taken back to /checkout so I can continue what I was doing!

These two problems - the fact that we want to automatically authenticate the user after registration and redirect them intelligently - can be solved at the same time! After we save the User to the database, we're basically going to tell Symfony to use our LoginFormAuthenticator class to authenticate the user and redirect by using its onAuthenticationSuccess() method.

Check it out: add two arguments to our controller. First, a service called GuardAuthenticatorHandler $guardHandler. Second, the authenticator that you want to authenticate through: LoginFormAuthenticator $formAuthenticator:

... lines 1 - 5
use App\Security\LoginFormAuthenticator;
... lines 7 - 10
use Symfony\Component\Security\Guard\GuardAuthenticatorHandler;
... lines 12 - 13
class SecurityController extends AbstractController
{
... lines 16 - 43
public function register(Request $request, UserPasswordEncoderInterface $passwordEncoder, GuardAuthenticatorHandler $guardHandler, LoginFormAuthenticator $formAuthenticator)
{
... lines 46 - 68
}
}

Once we have those two things, instead of redirecting to a normal route use return $guardHandler->authenticateUserAndHandleSuccess():

... lines 1 - 13
class SecurityController extends AbstractController
{
... lines 16 - 43
public function register(Request $request, UserPasswordEncoderInterface $passwordEncoder, GuardAuthenticatorHandler $guardHandler, LoginFormAuthenticator $formAuthenticator)
{
// TODO - use Symfony forms & validation
if ($request->isMethod('POST')) {
... lines 48 - 59
return $guardHandler->authenticateUserAndHandleSuccess(
... lines 61 - 64
);
}
... lines 67 - 68
}
}

This needs a few arguments: the $user that's being logged in, the $request object, the authenticator - $formAuthenticator and the "provider key". That's just the name of your firewall: main:

... lines 1 - 13
class SecurityController extends AbstractController
{
... lines 16 - 43
public function register(Request $request, UserPasswordEncoderInterface $passwordEncoder, GuardAuthenticatorHandler $guardHandler, LoginFormAuthenticator $formAuthenticator)
{
// TODO - use Symfony forms & validation
if ($request->isMethod('POST')) {
... lines 48 - 59
return $guardHandler->authenticateUserAndHandleSuccess(
$user,
$request,
$formAuthenticator,
'main'
);
}
... lines 67 - 68
}
}

Cool! Let's try it! Click back to register. This time, make sure that you register as a different user, password engage, agree to the terms, submit and... nice! We're authenticated and sent to the correct place.

Next - we're going to start talking about a very important and very fun feature called "voters". Voters are the way to make more complex access decisions, like, determining that a User can edit this Article because they are its author, but not an Article created by someone else.

Leave a comment!

  • 2020-04-24 Diego Aguiar

    Hey Gaetano Sottile

    If the action you did before doesn't force you to login, then, after a successful login it will redirect you to the homepage by default but you can change it an choose any other route. Or, you can override that method and implement the behavior you want.

    Cheers!

  • 2020-04-23 Gaetano Sottile

    Hi,

    I would like to understand something better. The method onAuthenticationSuccess() allows redirection to the targetpath of session. But, if I am not wrong, this targetpath is null for all routes without access control roles. For example, if I am in the page of an article like an anonymous user and then I log in, the code redirect the user to homepage. Is it normal this flow? Normally it could come back to article page.
    Thanks for your time.

  • 2020-02-24 Vladimir Sadicov

    Hey Gompali

    That's great! Feel free to ask questions if there will be more issues!

    Cheers!

  • 2020-02-23 Gompali

    I found out by myself :-) that I had to create a new Guard with same entry point for FB and the classic login form.
    Great tutorial. Thanks !

  • 2020-02-23 Gompali

    Hi,

    I built a Controller with FB oAuth flow and at the end of this Controller, FB confirms the user email.
    I search the user by this email and the user is correctly found and I would like to login automatically
    I implemented LoginFormAuthenticator and when I follow the code of this page, I have nothing, no errors or warning and the user is not authenticated after it redirects to home page

    Here the firewall :

    Security.yaml

    Here is the controller :

    Controller

    I don't know what's wrong. Any idea ?

  • 2020-02-11 Diego Aguiar

    Hey Dirk J. Faber

    I just tried out the "lazy" option and it works. I get logged in upon registration. Did you upgrade your project to Symfony 4.4? I'm asking because this tutorial is based on Symfony 4.1 and the lazy option was introduced in 4.4 (https://symfony.com/blog/ne... )

    Cheers!

  • 2020-02-08 Dirk J. Faber

    I could not get this to work, not matter what I tried. Turns out I had this set in security.yaml:
    "anonymous: lazy"

    I do like this setting, any way to make this manual authentication work while lazily loading the user object?

  • 2020-02-04 Vladimir Sadicov

    Hey Alexandre Leprêtre

    Thanks for catching it! Sometimes typos happen. It's already fixed, and soon everything will be correct!

    Thanks again, Cheers!

  • 2020-02-04 Alexandre Leprêtre

    Just a little mistake in the script:

    First, a service called GuardAuthenticationHandler $guardHandler


    It's actually GuardAuthenticatorHandler
    The code in the video is completely correct but I guess I'm more of a verbal learner and I was stumped as to why I couldn't inject that class for 10 minutes :D

  • 2019-12-06 Dung Le

    Thanks Diego Aguiar I will give it a try.

  • 2019-12-05 Diego Aguiar

    Hey Dung Le

    I think we don't have that use case covered but it's not difficult to do it by yourself. You only need a form with basically two fields
    First, field would be a repeated field type, where the user submit its new password (and confirms) https://symfony.com/doc/cur...
    And second, a field where user can submit his current password https://symfony.com/doc/cur...
    Also, you should secure that route by checking the role IS_AUTHENTICATED_FULLY it's super easy to do if you are using ACL


    // config/packages/security.yaml
    security:
    access_control:
    - { path: ^/profile/change-password$, roles: IS_AUTHENTICATED_FULLY }

    And that's it! Cheers!

  • 2019-12-05 Dung Le

    I am not sure if I missed out, but I finished the interesting course and never found any where that allows users to change password, I know this can be as simple as an update to field of a table but I wonder if you have done it somewhere in Symfonycasts that I can have access to? Thank you as usual!

  • 2019-12-04 Dung Le

    awesome auth tutorial!

  • 2019-08-19 Lydie

    Indeed .. lol

  • 2019-08-14 Diego Aguiar

    Oh, I think you just forgot to return the response of $guardHandler->authenticateUserAndHandleSuccess() in your controller

    Cheers!

  • 2019-08-14 Lydie

    hummm I think I understood. Checking the guard->authenticateUserAndHandleSuccess method, I see that it does return the "redirect response" but it does not executed it. So I need one more step in the controller, execute the redirection :) Is this correct?

  • 2019-08-14 Lydie

    Thx Diego!

    I made the change to isGranted.
    I have also added 2 dump: one to see if targetpath has a value and one just before the last redirect. I got the 2 dump values: targetPath is null and the last dump value. So normally it should execute the last redirectResponse but in reality, the freshly registered user remains on register page.

    Thx!

  • 2019-08-13 Diego Aguiar

    Hey Lydie

    In theory you are right but that's not the way how you check for roles. You need to delegate that task to Symfony\Component\Security\Core\Security::isGranted(). Also, could you add a dump() on your onAuthenticationSuccess method? So we can be sure that it's being executed

    Cheers!

  • 2019-08-13 Lydie

    Hello!

    I have just added the registration process to my site with automatic authentication at the end of the process.

    In my controller, I have:


    $guard->authenticateUserAndHandleSuccess(
    $user,
    $request,
    $form_authenticator,
    'main'
    );

    The user is indeed authenticated at the end of the registration process but it remains on the registration page (the user clicked on a register link to get the form).

    In my loginauthentication, in onAuthenticationSuccess method, I do have:

    /** @var User $user */
    $user = $token->getUser();
    if (in_array("ROLE_ADMIN", $user->getRoles()))
    return new RedirectResponse($this->urlGenerator->generate('admin_dashboard'));

    if ($targetPath = $this->getTargetPath($request->getSession(), $providerKey)) {
    return new RedirectResponse($targetPath);
    }

    return new RedirectResponse($this->urlGenerator->generate('homepage'));

    If I understand well how it's working, it should do:
    - if the user is an admin, redirect it to the admin dashboard
    - if a target path was set in session, redirect the user to this target
    - otherwise, redirect the user to the homepage

    Is this correct? If yes, what did I miss? I would think that after registration, we are in the third case, no?

    thx in advance!

  • 2019-07-15 Diego Aguiar

    Hey Mike

    Read about the parameter always_remember_me. I think it does exactly what you want: https://symfony.com/doc/cur...

    Cheers!

  • 2019-07-15 Victor Bocharsky

    Hey Mike,

    I think it's impossible to do via @IsGranted annotation because you need to do a redirect and that's why you need to write it in PHP. I'd suggest you to add an additional check for IS_AUTHENTICADED_FULLY in your loginAction() and if so - redirect to somewhere:


    public function loginAction()
    {
    if ($this->isGranted('IS_AUTHENTICADED_FULLY')) {
    return $this->redirectToRoute('some-route-name');
    }
    // ...
    }

    I think you do want to allow IS_AUTHENTICADED_REMEMBERED users to see your login page so they would be able to become IS_AUTHENTICADED_FULLY, otherwise they would not be able to get access to some part of your applications where you required IS_AUTHENTICADED_FULLY - they will be redirected to the login page, but it will redirect them somewhere else - sounds not OK.

    I hope this helps!

    Cheers!

  • 2019-07-15 Victor Bocharsky

    Hey Mike,

    Great! I'm glad it works for you. And thank you for your feedback!

    Cheers!

  • 2019-07-14 Mike

    Tip:
    To deny access for Users to /login and /register (after a user is logged in), I redirect them in both methods to another route.
    Otherwise a logged in user is able to go to /register and /login, even after he's logged in.

    I played with IsGranted like this:


    /**
    * @IsGranted("IS_AUTHENTICATED_ANONYMOUSLY && !IS_AUTHENTICADED_FULLY, IS_AUTHENTICATED_ANONYMOUSLY && !IS_AUTHENTICADED_REMEMBERED")
    */


    but it did not work.

    If there's a way to let Users get "locked out" of /login and /register via @IsGranted, please let me know.

  • 2019-07-14 Mike

    To improve UX I would like to have the _remember_me field hidden and always set to "on".
    Is there any downside of this?

    By example, if you login to Amazon, close your browser and reopen it, you're always logged in. No "Remember Me" Button.
    Just on some occasions you have to reenter your password to make sure you're really you.

  • 2019-07-14 Mike
    We should really make that easier - it's just not something that occurred to me before!


    Any news on that?

    Because your posted answer doesn't work for me :(
    ...
    Update:
    It works, I just had to make sure the _remember_me field has to be set

  • 2019-06-20 Victor Bocharsky

    Hey Egor,

    Hm, weird, difficult to say without debugging what was wrong. Glad it works in a new project

    Cheers!

  • 2019-06-19 Egor Ushakov

    Victor Bocharsky , it's amazing but I just created new Sf project from scratch and add all the code only with `./bin/console` and now it works fine. But in the project i mentioned above there was NO 'password' field in src/Form/RegistrationFormType.php . I haven't managed to fully xdebug the flow (since it stepped in too deep inside the framework code and i just lost control ) but examination of $form in controller showed that $form became invalid after $form->handleRequest(). But there should not be any 'password' in request!!! OK, looks like i add some code somewhere by mistake trying to write more "manual" code. And, yes, there were no constraints at all in User entity.

  • 2019-06-19 Victor Bocharsky

    Hey Egor,

    So, you don't have any password field in src/Form/RegistrationFormType.php but you see that error? Hm, do you have any constraints for $password field in User entity?

    Cheers!

  • 2019-06-17 Egor Ushakov

    Looks like something wrong with make:registration-form command in a whole as we build registration form here by hand instead of Maker command.

    I’ve made all the steps written here https://symfony.com/doc/cur...
    and make:registration-form has produced exactly the same files and code. But when I submit registration form the $form in controller action is ALWAYS invalid. I.e. false === $form->isValid(). As you can see the reason is that data.password in null what is ConstraintViolation.

    But it's really strange since 1) there is no 'password' field in RegistrationFormType and 2) all the constraints generated are for 'plainPassword' field. Seems like entity validation for User executes before form validation. But User entity itself does not have @Assert annotations out of the box and in my project as well.

    Thus one needs 1) to remove generated $form->isValid() check or 2) use hand-made form and logic as described in tutorial above. If I remove $form->isValid() everything is fine. Exactly as in tutorial above.

    Does it means that make:registration-form is not "production-ready" at the moment or I just missed something important using this command?

  • 2019-03-04 weaverryan

    Hey Ian!

    Great question! It's 100% up to you. Honestly, the only reason it's *typically* done in one action is because I subjectively made the decision many years ago to document the form component using this strategy on Symfony.com. And now... that's what you see everywhere ;). The downside of just one action is it's a bit harder to understand the flow. The benefit of one action is that two actions will require a little bit more duplication - a little bit more coding. But, it will work either way - there is no problem with separating the GET and POST into separate actions, and I get this question pretty regularly. Feel free to do what feels best for you :).

    Cheers!

  • 2019-03-02 Ian

    I guess this question is more about routing and forms, but I see this pattern a lot in tutorials and in the Symfony docs where we just have a form post to the same route that it's viewed from, and we have a single action that conditionally handles both GET and POST requests. That feels weird to me. Wouldn't it make more sense to have two separate actions, one tied to GET requests and the other to POST requests? And in this particular case, wouldn't it be more appropriate for the registration form to POST to something like /users? I know it's sort of a side-issue for this authentication lesson, but it always trips me up a bit. Maybe the convention has to do with how the Symfony form component works with the handleRequest and isSubmitted methods.

  • 2019-02-28 Алексей Суворов

    yep! It works for me, thanks!

  • 2019-02-28 weaverryan

    Hey Алексей Суворов !

    Ah, that's an edge case I didn't think of when designing Guard! So, you're right - when you use this manual authentication method, it does not go through the remember me process. So, you will need to do it manually - let me see if I can help :). It's actually a bit trickier than it should be :/.

    1) In your controller, add two new arguments RememberMeServicesInterface $rememberMeServices and TokenStorageInterface $tokenStorage

    2) For authentication, do this:


    $response = $guardHandler->authenticationUserAndHandleSuccess(...);
    $rememberMeServices->loginSuccess($request, $response, $tokenStorage->getToken());

    return $response;

    3) To make the $rememberMeServices argument work, go into config/services.yaml. Add a key under services._defaults.bind:


    services:
    _defaults:
    # ...

    bind:
    $rememberMeServices: '@security.authentication.rememberme.services.simplehash.main'

    Where the "main" part at the end should match your firewall name in config/packages/security.yaml (it's called "main" by default).

    Let me know if that works! We should really make that easier - it's just not something that occurred to me before!

    Cheers!

  • 2019-02-28 Алексей Суворов

    i set `always_remember_me: true` in security.yaml and use `$guardHandler->authenticateUserAndHandleSuccess`, but after this rememberme cookie wasn't created

  • 2019-02-25 Diego Aguiar

    You only have to activate it in your security configuration. Give it a look to the docs: https://symfony.com/doc/cur...

    Cheers!

  • 2019-02-25 Алексей Суворов

    Hey! How can I add remember me to authentication after registration?

  • 2018-12-31 weaverryan

    Hey commenter!

    It depends :). The short answer, if you're following the code of this tutorial, is no - you do NOT need this. In our set up, every page is available anonymously... and THEN we start adding security to secure individual pages.

    But, there is one important thing to check in your app. In OUR app, at this point, the access_control in security.yaml is empty. This means that, when each page loads, there are NO access_control entries to deny access. That means that every page is accessible anonymously (well, at least until you hit the security checks in the controllers themselves). However, in your app, if you DO have some access_control, then they may be causing access to be denied on your registration page. In that case, yes, you may need to add this entry to "whitelist" this one page. We actually talk about this in good detail in a super old tutorial (though the access_control behavior has NOT changed): https://symfonycasts.com/sc...

    Let me know if that make sense... or if I just confused ;)

    Cheers!

  • 2018-12-29 commenter

    Going to register route won't work when you are
    logged out, if the route is not available for anonymous user. Am I right
    ?
    I mean we need to add :
    - { path: ^/register, roles: IS_AUTHENTICATED_ANONYMOUSLY }
    in access_control

  • 2018-11-22 Victor Bocharsky

    Hey Sasa,

    Good question! So, you can do it in onAuthenticationSuccess() - there you have access to $request, so you can check if user is on registration page and if so - redirect him to a different route :)

    Cheers!

  • 2018-11-21 Sasa Milivojevic

    Hi! What if I want to redirect user after registration on different route from user who is logging in?

  • 2018-10-29 weaverryan

    Yo Matt Johnson!

    Ha! Great question and... I have no answer :D. I keep going "back and forth" between what I like better: $this->getDoctrine()->getManager() or EntityManagerInterface $e. There's no technical reason for it.

    But, you're right that, on a GET request, type-hinting it would technically be wasteful. I don't usually worry about these things - it's kind of a micro-optinmization.. and we probably are using the entity manager somewhere else anyways. However, it's a very good detail to notice :).

    Cheers!

  • 2018-10-27 Matt Johnson

    Why not just include EntityManagerInterface $em in the params? The only reason I can think is that we don't necessarily need it (if POST isn't submitted).