Redirecting on Success & the User Provider

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

If our authenticator is able to return a User from getUser() and we return true from checkCredentials():

... lines 1 - 11
class LoginFormAuthenticator extends AbstractFormLoginAuthenticator
{
... lines 14 - 35
public function getUser($credentials, UserProviderInterface $userProvider)
{
return $this->userRepository->findOneBy(['email' => $credentials['email']]);
}
public function checkCredentials($credentials, UserInterface $user)
{
// only needed if we need to check a password - we'll do that later!
return true;
}
... lines 46 - 55
}

Then, congrats! Our user is logged in! The last question Symfony asks us is: now what? Now that the user is authenticated, what do you want to do?

For a form login system, the answer is: redirect to another page. For an API token system, the answer is... um... nothing! Just allow the request to continue like normal.

This is why, once authentication is successful, Symfony calls onAuthenticationSuccess():

... lines 1 - 11
class LoginFormAuthenticator extends AbstractFormLoginAuthenticator
{
... lines 14 - 46
public function onAuthenticationSuccess(Request $request, TokenInterface $token, $providerKey)
{
dd('success!');
}
... lines 51 - 55
}

We can either return a Response object here - which will be immediately sent back to the user - or nothing... in which case, the request would continue to the controller.

Redirecting on Success

So, hmm, we want to redirect the user to another page. So... how do we redirect in Symfony? If you're in a controller, there's a redirectToRoute() shortcut method. Hold Command or Ctrl and click into that. I want to see what this does.

Ok, it leverages two other methods: redirect() and generateUrl(). Look at redirect(). Oh.... So, to redirect in Symfony, you return a RedirectResponse object, which is a sub-class of the normal Response. It just sets the status code to 301 or 302 and adds a Location header that points to where the user should go. That makes sense: a redirect is just a special type of response!

The other method, generateUrl(), is a shortcut to use the "router" to convert a route name into its URL. Go back to the controller and clear out our dummy code.

Back in LoginFormAuthenticator, return a new RedirectResponse(). Hmm, let's just send the user to the homepage. But, of course, we don't ever hardcode URLs in Symfony. Instead, we need to generate a URL to the route named app_homepage:

... lines 1 - 13
class ArticleController extends AbstractController
{
... lines 16 - 25
/**
* @Route("/", name="app_homepage")
*/
public function homepage(ArticleRepository $repository)
{
... lines 31 - 35
}
... lines 37 - 63
}

We know how to generate URLs in Twig - the path() function. But, how can we do it in PHP? The answer is... with Symfony's router service. To find out how to get it, run:

php bin/console debug:autowiring

Look for something related to routing... there it is! Actually, there are a few different router-related interfaces... but they're all different ways to get the same service. I usually use RouterInterface.

Back on top, add a second constructor argument: RouterInterface $router:

... lines 1 - 7
use Symfony\Component\Routing\RouterInterface;
... lines 9 - 13
class LoginFormAuthenticator extends AbstractFormLoginAuthenticator
{
... lines 16 - 18
public function __construct(UserRepository $userRepository, RouterInterface $router)
{
... lines 21 - 22
}
... lines 24 - 59
}

I'll hit Alt+Enter and select "Initialize Fields" to create that property and set it:

... lines 1 - 7
use Symfony\Component\Routing\RouterInterface;
... lines 9 - 13
class LoginFormAuthenticator extends AbstractFormLoginAuthenticator
{
... line 16
private $router;
public function __construct(UserRepository $userRepository, RouterInterface $router)
{
... line 21
$this->router = $router;
}
... lines 24 - 59
}

Then, back down below, use $this->router->generate() to make a URL to app_homepage:

... lines 1 - 13
class LoginFormAuthenticator extends AbstractFormLoginAuthenticator
{
... lines 16 - 50
public function onAuthenticationSuccess(Request $request, TokenInterface $token, $providerKey)
{
return new RedirectResponse($this->router->generate('app_homepage'));
}
... lines 55 - 59
}

Ok! We still have one empty method:

... lines 1 - 13
class LoginFormAuthenticator extends AbstractFormLoginAuthenticator
{
... lines 16 - 55
protected function getLoginUrl()
{
// TODO: Implement getLoginUrl() method.
}
}

But, forget that! We're ready! Go back to your browser, and hit enter to show the login page again. Let's walk through the entire process. Use the same email, any password and... enter! It worked! How do I know? Check out the web debug toolbar! We are logged in as spacebar1@example.com!

Authentication & the Session: User Provider

This is even cooler than it looks. Think about it: we made a POST request to /login and became authenticated thanks to our authenticator. Then, we were redirected to the homepage... where our authenticator did nothing, because its supports() method returned false.

The only reason we're still logged in - even though our authenticator did nothing on this request - is that user authentication info is stored to the session. At the beginning of every request, that info is loaded from the session and we're logged in. Cool!

Look back at your security.yaml file. Remember this user provider thing that was setup for us?

security:
# https://symfony.com/doc/current/security.html#where-do-users-come-from-user-providers
providers:
# used to reload user from session & other features (e.g. switch_user)
app_user_provider:
entity:
class: App\Entity\User
property: email
... lines 9 - 33

This is a class that helps with the process of loading the user info from the session.

Honestly, it's a little bit confusing, but super important. Here's the deal: when you refresh the page, the User object is loaded from the session. But, we need to make sure that the object isn't out of date with the database. Think about it. Imagine we login at work. Then, we login at home and update our first name in the database. The next day, when we go back to work, we reload the page. Well... if we did nothing else, the User object we reloaded from the session for that browser would have our old first name. That would probably cause some weird issues.

So, that's the job of the user provider. When we refresh, the user provider takes the User object from the session and uses its id to query for a fresh User object. It all happens invisibly, which is great. But it is an important, background detail.

Next, I want to see what happens when we fail authentication. What does the user see? How are errors displayed? And how can we control them?

Leave a comment!

  • 2020-06-18 weaverryan

    Hey Vinz Stoned Orgies!

    Yes, it makes *more* sense now... but maybe not *all* the sense... yet ;).

    > the user has the cookie => is this case I can't find any event to listen to make my redirection. The onSecurityInteractiveLogin is not called either

    Let's talk about the exact "use flow" that you're trying to solve here. Obviously, if an admin users is actively clicking around the admin section, you don't want to "intercept" that request and redirect them back to "/admin" :). So, is the user flow you want to achieve this?

    > If the user leaves the site (closes the browser, navigates away, whatever) and then access the site by going directly to the domain (i.e. the homepage) - like https://MyExtraNet.com, then if they are an admin, they should be redirected to the homepage.

    Is this the case you want to solve? And, if that same user navigated away, then directly tried to go to https://MyExtraNet.com/admi... (for example), you would do nothing and allow them to visit this page, right?

    If I *am* correct that this is the user flow you want (and that's a big if! Please tell me if I'm still wrong), then this sorta has nothing to do with authentication. There is no way for Symfony (or any backend technology) to know that the user navigated away for a little bit, and then came back. What you would *really* want to do here is add a listener to kernel.request with this logic:


    public function onKernelRequest(RequestEvent $event)
    {
    $request = $event->getRequest();

    // if on the homepage
    if ($request->getPathInfo() === '/') {
    $event->setResponse(new RedirectResponse($this->router->generate('easyadmin'));
    }
    }

    Does that makes sense? Or am I still "off" on what you want to accomplish. Let me know!

    Cheers!

  • 2020-06-17 Vinz Stoned Orgies

    weaverryan to be clearer, my use case is this:
    - my whole app requires to log in to access anything (it is an extranet)
    - if user 1 is a simple customer, then he can be redirected to the homepage (or any other page he tried to access) after the login
    - if user 2 is an admin (has ROLE_ADMIN), then I want him to be redirected to the /admin (easyadmin) part of the website, not the homepage or any other page

    This being said, my app uses the Security feature "remember_me" which sets - if I'm correct - a cookie when it is activated. So I have also two different "login" cases:
    - the user doesn't have the cookie => in this case the login form is displayed, and I can listen to the onSecurityInteractiveLogin event to redirect the user (within a Listener)
    public function onSecurityInteractiveLogin(InteractiveLoginEvent $event)
    {
    $token = $event->getAuthenticationToken();
    $roles = $token->getRoleNames();
    if (in_array('ROLE_ADMIN', $roles, true)) {
    return new RedirectResponse($this->urlGenerator->generate('easyadmin'));
    }
    }

    - the user has the cookie => is this case I can't find any event to listen to make my redirection. The onSecurityInteractiveLogin is not called either.

    Does it make more sense to you now?

  • 2020-06-16 weaverryan

    Hey Vinz Stoned Orgies !

    Sorry for the slow reply!

    > just when the user arrives on the app actually

    So that's tricky... because it depends on what you mean by this :). Do you mean:

    A) When they log in?
    or
    B) When they come to the site... but maybe they are already logged in... but they haven't been there in awhile

    For (A), you're looking for the InteractiveLoginEvent. But as you said earlier, that's only when they *actually* log in - it doesn't trigger if the session is already set up - i.e. if they are already authenticated. And so that's why I think (?) you really want situation (B). What's your use-case exactly? If the user hasn't been "active" on the site for awhile... and I want to do something when they *become* active, I would:

    1) Set a lastActiveAt DateTime on the User and set it (via a listener to kernel.request) on every request (when the user is authenticated).
    2) Before setting this (in the same listener), I would read the existing value, compare it to NOW, and if it's "been awhile" since they were active, run whatever code you need.

    Let me know if I'm still missing the use-case - it's a bit fuzzy :).

    Cheers!

  • 2020-06-11 Vinz Stoned Orgies

    weaverryan So no, not on every request, just when the user arrives on the app actually... But maybe I can add a condition in the listener that would trigger the check only when the user tries to reach the login or homepage... or somthing like this.

  • 2020-06-11 weaverryan

    Hey Vinz Stoned Orgies!

    > I can't find which even to target that fires at EVERY SINGLE logi

    Do you literally want to run some code on *every* request where a user is authenticated (regardless of if they just logged in or were already logged in via a cookie)? If so, I would register a listener on kernel.request (called RequestEvent also in newer Symfony versions). Inside, check if the user is logged in, and then do something. The point is: I don't think you need to hook into security to do this: just allow security to "do it's thing", then register a listener after and check if the user is authenticated or not.

    But... I may be missing the big picture - it's been a few months!

    Cheers!

  • 2020-06-11 Vinz Stoned Orgies

    HI weaverryan haven't seen your answer either!
    In the meantime I tired to create a listener on the InteractiveLoginEvent, but it just does the same: it is called when the user log in "normally", but is the session is still active or there is a remember_me cookie, it is not. I can't find which even to target that fires at EVERY SINGLE login...

  • 2020-02-02 Cristóbal Rejdych

    Thanks a lot for your fast reply. It helps me a lot ;)

  • 2020-01-29 weaverryan

    Hey Cristóbal Rejdych !

    Excellent question :).

    When you make an AJAX request and that AJAX request returns a "redirect" (a 301 or 302 response code), it does not (as you noticed) make your browser redirect. Instead, your browser will see that redirect and make *another* AJAX request to the URL of the redirect... but that doesn't have any effect on your browser.

    Here is how I usually solve this:

    A) If there is an authentication error, you return JSON. But make sure that the response status code is a 401 - because this is an *error* response, not a successful response. Inside jQuery, you can handle this case with a fail(function() {}).

    B) If authentication is successful, I like to return a 204 status code (it means, success but a response with no content). I then add a Location header with the URL that the user should be redirected to. I use that in JavaScript (just as you've done) to change the window.location. Here's some info about returning this response - https://symfonycasts.com/sc... - I also think it would be fine to return some JSON with a url key like you've done.

    I hope that helps! RedirectResponse is really something that's useful for "traditional" form submits, but not in an API/AJAX context.

    Cheers!

  • 2020-01-27 Cristóbal Rejdych

    Hi guys,
    I am writing here because I had problem with RedirectResponse after success authentication. At first something about sign in process in my project:
    I wanna do ajax call from reactjs component with login form and when data of user is correct do redirect to another page, but when its not: send via JsonResponse error message. Could You explain me why RedirectResponse don't work after ajax response?? When I go to profiler It's look like I was in properly route, but in url in my browser it's not and page content don't reload. At least I did that by send JsonResponse with url to page what I wanna redirect user after success:


    public function onAuthenticationSuccess(Request $request, TokenInterface $token, $providerKey)
    {
    return new JsonResponse(['url' => $this->router->generate('app_home')]);
    }

    After success ajax call i take json response and use js to read it and redirect:

     $.ajax({
    url: '/login',
    method: 'POST',
    data: JSON.stringify(userData)
    }).then(function(destinationUrl) {
    window.location = destinationUrl['url'];
    });

    It's any better way to do that??

    Thank you for the reply.

  • 2020-01-27 weaverryan

    Hey Vinz Stoned Orgies !

    Sorry for the slow reply :). Hmm. I'm not sure the cause, but I'd like you to check for the roles in a different way: inject the Security service and use is isGranted method - similar to what we do in voters: https://symfonycasts.com/sc...

    The problem is that checking for the roles on the token (or the user object) is not exactly the same as using the isGranted() method - the isGranted method does several things that checking the roles directly does not do (for example, isGranted() also checks for role_hierarchy in security.yaml, which checking the roles directly does not).

    So try this out and let me know how it goes. I can't explain (yet) what the cause is - but better to update it to the "right" way and ... we'll just see if that fixes things ;).

    Cheers!

  • 2020-01-22 Vinz Stoned Orgies

    Hi, I would like to redirect my users depending on their role. If the user has ROLE_ADMIN, I would like it to be redirected to easyadmin route. Else, users have to be redirected to app_homepage. I modified my onAuthenticationSuccess function to


    public function onAuthenticationSuccess(Request $request, TokenInterface $token, $providerKey)
    {
    $roles = $token->getRoleNames();
    if (in_array('ROLE_ADMIN', $roles, true)) {
    return new RedirectResponse($this->urlGenerator->generate('easyadmin'));
    }

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

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

    which is working well on a "normal" authentication.

    But if my user with ROLE_ADMIN has checked "remember_me" checkbox, it is not redirected to easyadmin but to app_homepage like a "normal" user...
    Could you please help? I tried to trigger this through a listener, but nothing worked so far...

  • 2019-07-29 Victor Bocharsky

    Hey MarBod,

    It sounds good to do in LoginFormAuthenticator I think, could you explain what problems exactly do you have? As I understand on onAuthenticationSuccess() you should be able to get the actual user.

    Cheers!

  • 2019-07-28 MarBod

    Hello, I want to log the last login from an user in the user table, I have two fields (ip and datetime) in my user table.
    The application doesn't have a special entry site, where it redirect the user on success. So I thought I could implement it in the LoginFormAuthenticator (onAuthenticationSuccess) but I have problems to catch the user, add the parameters and save it.

  • 2019-06-17 Michael Leblanc

    Finally got it working !

    It was indeed a session problem and a kinda dummy one haha.

    In an other project i limited "session.cookie_domain = " to a specific domain. This disabled access to cookies in this project hence the issue.

    Thank you for your time :)

  • 2019-06-17 weaverryan

    Hey Michael Leblanc!

    Hmm, interesting. Well, that at least (most likely) eliminates the possibility that it was the "serialization" thing mentioned on that other comment. So here's one other thing you can do to debug:

    1) Go through the login process like normal and allow it to redirect you
    2) Now go to https://localhost:8000/_profiler - you should see a list of the most recent requests into your app, with the newest on top. The 2nd (or maybe 3rd) on the list will be the POST request to /login. Click the little "sha" link (e.g. abc1234) on the right to open the profiler for that request.
    3) On the left side, find the security tab and click it.

    You're now looking at the security information from the login POST request - *before* redirecting. The key thing we're looking for here is: does it show you as properly authenticated or not?

    A) If yes, then we DO have some sort of a session problem - because you're authenticated here, but it's being lost after the redirect
    B) If no, then the problem is actually during authentication somehow - something is wrong with your authenticator, for example.

    Let me know what you find out!

    Cheers!

  • 2019-06-17 Michael Leblanc

    Hey, thanks for the fast answer.

    Im still having a problem. I tried to implement EquatableInterface and it's required isEqualTo method. I also dont have any serialized function in my user class.

    I followed the course from the beginning

  • 2019-06-17 weaverryan

    Hey Michael Leblanc!

    Ah yes. So, what's *probably* happening is that your getting logged in successfully... then "losing" authentication immediately because of a problem loading the user's details from the session. Check out this conversation (and read the comments below as well) - https://symfonycasts.com/sc... - it contains some a debugging recommendation around using an EquatableInterface and an explanation of what's going on.

    Let me know if that helps you discover anything!

    Cheers!

  • 2019-06-17 Michael Leblanc

    Hey, great video as always.

    Im having a problem with redirecting my user. The connection process is functionnal but as soon as i change page my user goes back to anon.

  • 2018-12-31 weaverryan

    Hi there!

    That's really interesting! How did you create your App\Entity\User class? Very simply, that class MUST implement Symfony's UserInterface - we talk about that here: https://symfonycasts.com/sc...

    Very simply, this error tells me that you are not implementing this interface. You may simply not be implementing it, OR it's possible that you ARE implementing it, but you forgot the use statement for UserInterface.

    Let us know what you find out!

    Cheers!

  • 2018-12-29 MajkellVZ

    I am getting this error.
    The App\Security\LoginFormAuthenticator::getUser() method must return a UserInterface. You returned App\Entity\User.

  • 2018-12-09 weaverryan

    Hey Kristof Kreimeyer!

    Hmm, sorry about the problem - that's no fun! We take a lot of pride in the code in the tutorial being correct and being exactly what we use in the video. However, sometimes, things change over time - or other issues happen. So, I apologize if you've been having any issue that are our fault.

    Let's figure out this problem! On the surface, the problem is obvious (but the cause is quite NOT obvious): the getSession() method on the Request is returning null. Basically, there is no session for some reason! That is quite bizarre. Here is some background on this the session is set onto the request (these are normally not details you should care about - but something isn't working correctly):

    1) A class called SessionListener is called early in Symfony. It fetches the "session" service from the container and sets it on the request. It does this unless, for some reason, there is NOT session in the container.

    2) So, the most likely cause for this to happen is that (somehow) there is no session service in your container. You can verify this by running: php bin/console debug:container session. This *should* return information about the session service. But, my guess is that, for you, this will throw an error about the session not being found (but tell me if I'm wrong!). So, why would the session service not exist? The session needs to be enabled via some configuration. But, when you start a Symfony project, this configuration is included automatically for you in your config/packages/framework.yaml file. You should have this file with some code that looks like this: https://github.com/symfony/...

    So, does any of this look different on your project? Does the session service exist or is it missing? Do you have the session configuration that I listed above?

    Let me know and we can keep debugging if this doesn't help!

    Cheers!

  • 2018-12-05 Kristof Kreimeyer

    Another day, another error or in other words: "same procedure as every video" :
    at the end i tried to refresh the page and get the error "Call to a member function set() on null"
    The error is in in src\Security\LoginFormAuthenticator.php (line 51) and the content of this line is :
    $request->getSession()->set(

    So i asked Google about it, but i haven't found an answer yet.

  • 2018-10-25 Diego Aguiar

    Hey Matt Johnson

    Actually, you do nothing! You just let the request to continue its lifecycle and hit a controller's action to handle the response. Probably you will be interested in watching this chapter (and the next one): https://symfonycasts.com/sc...

    Cheers!

  • 2018-10-25 Matt Johnson

    You briefly mention what onAuthenticationSuccess looks like when doing API tokens. Can you elaborate on that a bit, and what this entire structure would look like if we were using a token? Is it something like:

    1. Credentials submitted & verified
    2. We return bearer token to API (and store it locally, probably with an expires timer)
    3. API sends bearer token to all endpoints except the login authenticator

    In the case above we'd still need this login authenticator, but our success would be a response that just contains the token. Subsequent requests would have another security voter that looks for the bearer token and votes yes or no based on whether or not the token exists and is (not) expired. Right? Any other considerations here?

    Forgive me if this is answered later on in this course.

    Cheers,

    Matt

  • 2018-10-18 weaverryan

    Hey Mickaël Andrieu!

    Oh man, SUCH a good question. You probably didn't realize you were asking something I have such a passionate idea about ;). Basically, form_login is so "invisible" that I'd argue that you'd spend as much time trying to figure out how it works as it would be to implement an authenticator yourself (where you can see all of the code and it's clear what's happening). Well, truthfully, the Guard authentication still *would* take longer to write: but I think it's worth it for how much more "clear" the end result is: you see the logic in your code, vs it being hidden somewhere (and you're not even sure where).

    But, I have 2 other big reasons:

    1) Today's announcement of the php bin/console make:auth command makes using Guard authentication faster to setup than form_login (or, at least equally fast, if you consider that we could, in theory, have made a generator that used form_login). This means you get more clarity with zero cost.

    2) For my actual big reason: form_login is impossible to customize. Suppose form_login works great for 70% of people. Awesome - that's a lot! But now, what if you need to create a custom query for the user? That's possible... but not obvious. What if you want to prevent login if someone has failed there password 5 times? That is *quite* difficult. And what if you need a *third* field in your form (e.g. "company dropdown") and you need to use that in the query for the User? You're dead :). I'm fine with a solution that covers the 70% perfectly, as long as making the last 30% of use-cases work is reasonable and obvious. With form_login, it is neither reasonable nor obvious. That's the *real* reason. And, the core team recently agreed internally to start promoting this as the "main" way, over form_login (though some of the super simple built-in authentication providers like http_basic are still quite good I think).

    Cheers!

  • 2018-10-17 Mickaël Andrieu

    Hello Ryan,

    thinking about it, I guess that for a simple form login we can rely on "form_login" of the Security Bundle...
    what are the pros/cons to use an authenticator instead of what is available in the Security Bundle?

  • 2018-09-27 Diego Aguiar

    Actually you don't have to inject a user like that, you can rely on calling $this->getUser(); in any controller (both ways gives you the same User object)

  • 2018-09-27 shing

    ah ok. missed that 👍

  • 2018-09-27 Victor Bocharsky

    Hey Shing,

    I see it calls refreshUser() anyway, see https://github.com/symfony/...

    Cheers!

  • 2018-09-27 shing

    AH. I ok i understand.

    If anyone is curious, in ContextListener->handle(), it checks for a hasPreviousSession(). It uses that User object if present, else queries for a fresh one.

  • 2018-09-27 shing

    👍👍👍.

    I get it. Because there will always only have 1 user provider thats being set in the security.yaml
    Thats why when u inject a UserInterface, thats the user provider thats going to be injected in.


    Controller.php
    public function index(UserInterface $user )
    {
    dump($user);die;
    }

    Wow. This User object is elegant and a big hairy beast at the same time.

  • 2018-09-25 Diego Aguiar

    Hey @shing

    > I'm guessing the injected UserInterface $user is the fresh user?
    Yes, you are right :)

    > And if using REST JWT, EquatableInterface is not needed...
    Yep, there is no session, you would be passing the token on every request and Symfony will fetch the user based on that token, but I believe it won't execute a query every time you call $this->getUser(), it will just get it from the token object.

    > Is this the same $this->getUser() fresh object?
    Yes, at that point of the request lifecycle you are working with the fresh user object

    Cheers!

  • 2018-09-25 shing

    Just a quick add on.

    in the controller


    public function index(UserInterface $user )
    {
    dump($user);die;
    }

    Is this the same $this->getUser() fresh object?

  • 2018-09-25 shing

    👍👍👍. Thank u! Super clear and concise A-B-C. I've been searching high and low for this explanation.

    Sorry, I made a mistake with
    > \UserInterface->getUser() in the controller.
    I swear, i was trying that and getUser() was there. I think I'm confusing it with the EquatableInterface.


    public function isEqualTo(UserInterface $user)
    {
    //......
    if ($this->username !== $user->getUsername()) {
    return false;
    }
    //......
    }

    I'm guessing the injected UserInterface $user is the fresh user?
    And if using REST JWT, EquatableInterface is not needed. There is not session so $this->getUser() always queries for a fresh user obj. I'm going through ur Guard and Rest tuts on this one.

    Thank u for being so patient. KNP is a very valuable resource!

  • 2018-09-25 weaverryan

    Hi shing!

    Ah, very interesting questions :). First:


    $this->getUser()

    This is always, 100% of the time the "fresh" user. Why? Here's how it works:

    A) At the end of the request, your User object (the one you can get via $this->getUser()) is serialized to the session.
    B) At the beginning of the next request (very early), that User object is deserialized from the session. At this point, it may be out-of-date
    C) At the next moment (so still very early in the request), Symfony reads the "id" from the "out-of-date" User object and uses it to query for a fresh User object. This is what you will receive whenever you access the User for the rest of the request.

    So, there IS one query per request, but it happens just once, automatically, and very early in Symfony. If you want to look at the exact code, the class is called ContextListener.

    You also mentioned EquatableInterface and isEqualTo(). That is a related topic. After Symfony "refreshes the User" (queries the database for a new User), it compares the old User (the one just deserialized from the session) to the new, fresh User. If they are "different" (I'll explain this next), then Symfony logs you out. To determine if they are different, by default, it just compares a few important methods on both objects. You can see that in AbstractToken - https://github.com/symfony/.... You can ALSO see there that IF you User class implements EquatableInterface, then it calls your isEqualTo method so that YOU can control this comparison.

    But, what is the purpose of this comparison? Why are we comparing the old and fresh object to see if they are different? The reason is security. Suppose you are on one computer, logged in. You realize that someone has obtained your password and is logged in on another computer somewhere else. So, you change your password. When you do that, you want that other person to be logged out. That's what this comparison does: once you change your password, the next time that person refreshes, their session User object will not be equal to the database User and they'll be logged out.

    Oh, and there was one part of your question that I didn't understand:

    > so everytime I call \UserInterface->getUser()...

    I don't understand what you mean by this? UserInterface is just an interface that your User class implements. There is no getUser() method on it. Let me know :).

    Cheers!

  • 2018-09-24 shing

    Hi,

    In the controller, there are


    $this->getUser();
    \UserInterface->getUser();

    It also appears in


    public function isEqualTo(UserInterface $user)
    {
    //......
    if ($this->username !== $user->getUsername()) {
    return false;
    }
    //......
    }

    "Finally, the fresh User object is compared to the deserialized User object to make sure that they represent the same user."

    My question is:
    - I'm guessing

    $this->getUser()

    is the in-memory/sessionId deserialized User object.
    - The fresh User object is

    \UserInterface->getUser()

    .
    so everytime I call

    \UserInterface->getUser()

    it queries the DB? Which class handles that?

    -"It may also be useful to implement the EquatableInterface interface, which defines a method to check if the user is equal to the current user"
    How is user and current user different?

    Thank u.

  • 2018-09-24 Victor Bocharsky

    Hey Niumis,

    Thank for helping us answering questions! ;) But your suggestion is relevant if you do not use Guard but Symfony "form_login" feature and affects only failed login. In order to do so with Guard, see my answer: https://symfonycasts.com/sc...

    Cheers!

  • 2018-09-24 Victor Bocharsky

    Hey Dmitriy,

    Good question! Hm, a referer can be not perfect if you write invalid credentials, I suppose on the next login the referer will be the same login page. Actually, we'll cover this feature further in this course.

    But in case you wonder right now, see TargetPathTrait inside AbstractFormLoginAuthenticator that we extend in this course. It has TargetPathTrait::getTargetPath() method - that's exactly what you need, redirect user to the returned URL. In onAuthenticationSuccess() I do:


    public function onAuthenticationSuccess(Request $request, TokenInterface $token, $providerKey)
    {
    // ...

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

    return null;
    }

    Cheers!

  • 2018-09-22 Дмитрий Ченгаев

    Yes thank you. I solved the problem by adding a hidden field to the login form with the value of the referer page.

    It would be nice to look at a more detailed description of the solution to this problem from symfonycasts.

  • 2018-09-22 niumis
  • 2018-09-21 Дмитрий Ченгаев

    How can I redirect a user to the last page on which he was using before he went to the login page and entered a username and password?