Symfony Security: Beautiful Authentication, Powerful Authorization

3:34:13

What you'll be learning

Oh no, it's time to add security! Ahhh!

Wait, come back! Security in Symfony is awesome! Seriously, between things called "voters" and the Guard authentication system, you can do anything you want inside of Symfony, and the code to do it is simple and expressive.

Security has two sides: authentication (who are you?) and authorization (do you have access to do X). We'll talk about each of these, creating an traditional form login system and and API token authentication. Then, we'll turn to authorization, with roles, voters and other good stuff:

  • Making a User with the fancy new make:user command (ooOOOoo)
  • Security & Firewall Fundamentals
  • Creating a custom login form
  • CSRF protection
  • API token authentication system
  • All about Guard authentication
  • User Providers (why you need them, but don't care)
  • Password Encryption
  • Logging out!
  • Protecting entire URLs with access_control(s)
  • IS_AUTHENTICATED_FULLY, IS_AUTHENTICATED_REMEMBERED
  • Checking access with roles! ROLE_USER
  • Denying access in a controller
  • What are voters?
  • Role hierarchies
  • Impersonation (switch_user)
  • Automatic Login (after Registration)

... and how to create a back door into your... spaceship... that will allow it to be destroyed with one careful shot. Just kidding! Let's make some secure sites / spaceships!


Your Guides
Ryan Weaver

Buy Access

Questions? Conversation?

  • 2019-03-11 weaverryan

    Yo Scottie Gutman !

    Nothing exists like that currently - it's actually really cool idea though, however security rules are added in some many ways, it's hard to make a solution that would work for everyone. I *have* seen a company do this in the past who used security annotations for everything - they wrote their own console command. It's actually a bit easier than you may think, it involves:

    1) Iterating over all of your controller classes (I would probably use Finder to find all files in my Controller directory, then do a little string "playing" to convert that into class names)

    2) Inside the loop, use PHP Reflection (http://php.net/manual/en/re... to get all the methods.

    3) For each method, it it is public (if it's not public, just "continue"), use the annotations reader (autowireable by type-hinting the argument with Doctrine\Common\Annotations\Reader) and call getMethodAnnotations() to read the annotations off of that method. This will return all of the annotations, including @Route and @IsGranted. You can use this information to figure out which endpoints use which security config.

    This really requires very consistent use of annotations for security to work - if you have a bunch of security applied inside the function itself (e.g. $this->denyAccessUnlessGranted()</code),>

  • 2019-03-09 Scottie Gutman

    Is it possible to get security debug information that merges annotations and security.yaml? maybe a console command that show all controller actions and their associated rules? or maybe by endpoint?

  • 2019-03-07 Waldemar Dell

    Thank you very much. That's the answera I needed.Wow, I got an answer from Ryan, I feel so good. Love your Tutorials. I watched nearly everything about symfony 3.3. IT was great.

  • 2019-03-07 weaverryan

    Hey Waldemar Dell!

    Yes, in this tutorial, we do all authentication via Guard. The SimplePreAuthenticatorInterface was deprecate in Symfony 4.2 because it basically does the same thing as Guard, but Guard is a bit easier to use.

    Cheers!

  • 2019-03-07 Waldemar Dell

    Hello. IS authentication Made via guard? Because there are Changes in SF 4.2. at SimplePreAuthenticatorInterface. Thanks in advance

  • 2019-03-04 weaverryan

    Hey Justin Voelker!

    Woohoo! Thanks for the update that it worked :).

    > I would like to try again, this time using AJAX to submit the login form. Basically, show the username/password, submit via AJAX, if the user needs 2FA pass back some JSON indicating the 2FA code is needed, show the 2FA field, submit the login again, this time with all three fields populated.

    Yes, except I would only submit the 2FA code the second time - not the *also* the email & password again. It's just not necessary (you can set something on the session that they have passed this first "step") and it *could* get weird because you'll need to keep the email & password fields somewhere on the DOM so you can re-submit those values. Basically, you CAN do what you're saying, just be aware that you will have the password "floating around" the client side a bit longer. But yea, you could use 1 or 2 authenticators really, no matter what you do. It's just a matter of code organization - so do what feels best.

    > Essentially, the only part I'm unsure about is how, if submitting a form via AJAX, are form/field errors handled

    By default, if you're extending the AbstractFormLoginAuthenticator, the onAuthenticationException method stores the validation error to the session and redirects you. I think you're already not relying on this, as you've overridden this method. So basically, you would just need to grab the exception error from the AuthenticationException and put it in the JSON you want to return - you can see what the base class does here: https://github.com/symfony/... - what you actually need is $exception->getMessageKey() - that is the string that contains "what went wrong during authentication". Return that in the JSON, then it will be your responsibility to render that onto your form somehow via JavaScript.

    Let me know if that makes sense! Sounds like the feature will be awesome.

    Cheers!

  • 2019-03-04 Victor Bocharsky

    Hey Hermen,

    To recap, you're interested in translation tutorial that covers StofDoctrineExtensionsBundle as far as I understand. And yeah, I have a great news for you! I'm working on this top secret translation tutorial right now, it should be released soon... I think we'll start releasing it in March! Though I'm going to cover KnpLabsDoctrineBehaviors instead of StofDoctrineExtensionsBundle just because if we're talking about Translatable behavior - it's implemented better by KnpLabs.

    Cheers!

  • 2019-03-02 Hermen

    Sorry Victor, I was sure I had replied... I actually meant the Doctrine Extensions with the StofDoctrineExtensionsBundle. But it looks as if that project is revitalised...

    Any news about this tutorial, is it on the board?

  • 2019-02-28 Justin Voelker

    Awesome! It works perfectly! Quick note to anyone else trying this, the method is onAuthenticationFailure() not onAuthenticationException().

    Once I got this working (yay) I realized maybe I didn't fully think through the UX (boo). I would like to try again, this time using AJAX to submit the login form. Basically, show the username/password, submit via AJAX, if the user needs 2FA pass back some JSON indicating the 2FA code is needed, show the 2FA field, submit the login again, this time with all three fields populated. This would also be nicer as it only needs one authenticator. I'm guessing a POST request to the same path would work but I would need to somehow return JSON on failure.

    Essentially, the only part I'm unsure about is how, if submitting a form via AJAX, are form/field errors handled? Ideally I would keep the functionality of "wrong username or password" for the initial two fields, but then add some "wrong 2fa code" as necessary. Is there some JS form magic for working with forms/returned errors via AJAX?

    Thank you again for the incredible tutorials and assistance!

  • 2019-02-25 weaverryan

    Hey Justin!

    Actually, you nailed it - nice work! Ok, back story: Symfony doesn’t natively handle two factor auth. It’s not that it can’t - but no official solution has been set forward. I’ve never had to implement it, but I’ve been telling people for years to do exactly what you’re proposing. Let me see if I can fill in a few of the fuzzy pieces:

    1) in the first authenticator, once you determine that authentican is successful (you found their User), you cannot return the User from getUser(). That would cause auth to be successful, which is actually not what you want. You’ll need to create a custom Exception class and make it implement Synfony’s AuthenticationException. Add a User argument to the constructor, store the user on a property, and add a getUser() method to the exception class. Then, at the bottom of getUser(), instead of returning the user, throw now NowReadyForSecondStepAuthException($user) - or whatever you call it.

    2) this will cause onAuthenticationException() to be thrown. If you see a different exception, handle it like normal. But if you see this exception, store the user I’d in the session and redirect to whatever URL will hold your second step of auth.

    3) make your second authenticator only return true in supports() if the user is on the correct URL and if that session key exists. Do whatever you need to do here - it’s kind of a normal authenticator, except that it’s checking for and using that value in the session.

    4) and that’s it! You kinda don’t need to prevent the user from navigating away from the 2nd factor auth, because with this method, they are not authenticated at all until they complete step 2. If they navigate away, good for them - they are anonymous still.

    So, you can see that it’s kinda straightforward, but the details are tricky. There are people that definitely want to make this easier.

    Let’s me know what questions you have - happy to help!

    Cheers!

  • 2019-02-25 Justin Voelker

    Hello and thank you for the incredible tutorials!

    Is there a way to implement a two-part authentication process? For example, after username and password are authenticated, some users would be logged in while others could be directed to a second login page to complete their login by providing, perhaps, a security question answer or two factor code?

    With very little Symfony experience so far I was hoping there was a "correct" way that I am just not aware of. My thought was to setup username/password authentication as described in this tutorial. Upon success, if logic determines another step is required, set a session variable. Then create another guard that looks for that session variable and prompt the user for the second part of the login process. I'm just not sure how to go about having this guard use a different page/form and how to restrict users from doing anything else on the site until that second part of the login is complete.

    Thanks again for the tutorials!

  • 2018-12-27 Diego Aguiar

    Hey Ovidiu Crihana

    Welcome to the Symfony world!

    1) First, you only need to define an entry point when you have more than one Guard Authenticator. In your case, I would prefer to have only one (open to public) login form and then protect the whole /admin/ URL namespace, so only admins can go to /admin/dashboard for example. If you really need two login forms, then you will have to create another Guard Authenticator, and then override the start() method to manage redirects properly. I recommend you to watch this chapter: https://symfonycasts.com/sc...

    2) This question is totally related to OOP and it's a big topic but in short, you would prefer to inject dependencies via the constructor when you want to use the same object's instance for all the time been of a dependent object, in other words, if you have a class (let's call it $a) that has a constructor argument of a MarkdownParser service, you know everytime that you use $a->render(), it will always use the same MarkdownParser instance.
    But, if instead the render method receives a MarkdownParser as an argument, then, everytime you call it, you (as a client) will have to provide a MarkdownParser instance, which can be a different instance and even a different type, in case that MarkdownParser is an Interface.

    3) Well, it depends on how complex or easy it is to use that library, usually I would create a service class that knows how to use the library, and just use it everywhere I need it.

    Cheers!

  • 2018-12-27 Ovidiu Crihana

    Hello, I just finished this course and the previous ones about s4 and I wanted to ask a few questions if you would kindly reply. Coming from a non-framework background, I still struggle a bit wrapping my head around Symfony. So here are my questions:

    1. When we built the login form, we chose an entry point for where the form is located and how to respond on the submit. The problem is that you log normal users and admin with the same form. How would I go about building multiple login forms? I need a login form for the normal users(clients) and another one for the admins, basically the urls are /profile/login and /admin/login

    2. I am a bit confused about when I need to pass arguments in the __construct method of a class and when I don't need to. What I mean is, sometimes you just type hint an object in the method declaration and have it available, but other times you passed an object via the __construct method. Is there like a rule where I know what is available to me and what is not so that I need to inject it via the __construct ? Because I only worked with normal php, I always instantiate a class with new ClassName() and then inject it to other classes when needed.

    3. Finally, how would you recommend using third-parties libraries that have nothing to do with symfony? For example, I often use http://image.intervention.io/ for image manipulation when I need to upload and resize images. How exactly would I use this library in my controller which handles the upload form, via __construct or there is a better way?

    Thank you again for these amazing courses and I apologize if my questions are too overwhelming.

  • 2018-11-26 Hermen

    So sorry, I meant Doctrine Extensions which, the last time I checked, was no longer maintained. It seems it is now maintained again...

    Looking forward to this update, working my way through the project now.

  • 2018-11-12 Victor Bocharsky

    Hey Hermen,

    What Symfony Extensions are you talking about? We'll base this screencast on our Stellar project that we're developing in every Symfony 4 screencast.

    Cheers!

  • 2018-10-31 Hermen

    Hey Victor,

    Translations would be awesome, but... will you guys bring in the Symfony Extensions which are no longer maintained or will you start from scratch?

  • 2018-10-22 Victor Bocharsky

    Hey Vadim,

    We're sorry, it were weekends. Just answered both your emails. Thank you for your patience!

    Cheers!

  • 2018-10-20 Вадим

    Hey, wrote a letter on your mail (through Contact Us section), still no answer (sir.****@y*.**)

  • 2018-10-18 weaverryan

    Hey Alex Bibiano González!

    Thank you! But... no - we don't :/. It's secretly on our TODO list to create a bundle that does ONLY this part, but it's not finished yet. And using FOSUserBundle just for the forgot password is overkill. I did find this gist today - https://gist.github.com/flo... - which contains some code (haven't tested it) for a manual reset password. It's really not *too* difficult (mostly a "chore") and that code clearly explains the steps needed. I hope it helps :).

    Cheers!

  • 2018-10-17 Alex Bibiano González

    Very good course. Do you have plans to add a chapter about adding a “forgotten password” feature to this serie?

  • 2018-10-10 Victor Bocharsky

    Hey Nicholas,

    Thank you! Good luck with catching up ;)

    Cheers!

  • 2018-10-10 Nicholas Njogu

    feels great catching up finally, Great casts Knp/

  • 2018-09-03 Victor Bocharsky

    Hey Jimmy,

    We completely released ReactJS tutorial finally, but now we have an intermediate small one - Contributing to Symfony: https://knpuniversity.com/s... . So the next should be this one. Thank you for your patience!

    Cheers!

  • 2018-09-02 Jimmy Silva

    Yes when will this one be completed?

  • 2018-08-08 Victor Bocharsky

    Hey Mike,

    We're going to release these 2 course next, but I can't guarantee we'll finish them in 2 months.. and it depends on do you need to finish your project in 2 months or you just need to create it in 2 months :) But yeah, we have plans to finish these 2 courses in 2 months I think.

    The other question is, do you want to use LTS version (that is 3.4) of Symfony for your project or do you want to use the newest 4.1? See this roadmap: http://symfony.com/roadmap . Probably starting project with LTS version is a good idea, and since it's 3.4 - you can totally follow those courses from Symfony 3 track. But please, use the Symfony Flex that supports Symfony 3.4 - it would be much easier to migrate to the Symfony 4 later. If you're about to use 4.1 - you can still follow those course from Symfony 3 track, you just need to do it in Symfony 4 way with Symfony Flex, the rest of the code is almost the same, but if you faced some problem following those tutorials on Symfony 4 - just ask your questions below videos and we'll try to help you.

    So, the original idea won't be changed too much for those new SF Auth & SF Forms, we'll just show how to do the same we did in Symfony 3 track but in Symfony 4 way usign Flex and some new features, so I don't think code will change too much.

    Cheers!

  • 2018-08-07 Mike

    I need to create a new project within the next 2 months. I need SF Auth & SF Forms, do you recommend to use techniques from your old SF3 courses to handle this or should I use the new SF official docs for this? It would be great if i could code by example the SF auth system without rewriting the code when this course is out!

    Any suggestions?

  • 2018-08-01 Victor Bocharsky

    Hey Mike!

    Here's our upcoming tutorials list: https://knpuniversity.com/c... - as you can see this one should be next for now. We need about 2-3 weeks to completely release ReactJS course, and, in the best case, I think this tutorial will be started releasing in a month. But that's just a wild guess for now, we'll have more precise dates after ReactJS. Thank you for your patience!

    Cheers!

  • 2018-07-31 Mike

    I see that there are still react tutorial published, is there a new ETA? Cant wait to see this new high quality tutorial from you Ryan!

  • 2018-07-17 Victor Bocharsky

    Hey Knayz,

    We covered services and service container a lot in the new Symfony 4 Fundamentals: https://knpuniversity.com/s... - check it out if you haven't seen it yet. More pro version of it would be have too, but we don't have any certain plans for it yet, so I don't tell you much about when it may be released. First of all, it will be Security, Forms, and probably Translations... what is the next - we will see :)

    Cheers!

  • 2018-07-16 Knayz

    And what about Level up with services and the container?

  • 2018-07-16 Knayz

    Victor Bocharsky,
    thank you for the reply!

  • 2018-07-16 Victor Bocharsky

    Hey Knayz,

    Yes, we're moving this way as you can see, so the next are screencasts about Security and Forms.

    Cheers!

  • 2018-07-14 Knayz

    Do you have plans to repeat all courses about SF3 for SF4?

  • 2018-07-09 weaverryan

    I agree! I'm finishing the React tutorial this week - then straight to security! Weeeee!!!!

  • 2018-07-09 kribo

    sounds good and is long over due... cannot wait to get started.

  • 2018-06-19 Felipe Martins

    I will be waiting for that.

  • 2018-06-19 mouerr

    will be a great tutorial, we are waiting :D

  • 2018-06-18 Victor Bocharsky

    Hey Arek,

    We're going to start releasing ReactJS tutorial this week, and then this one will be next I think. Thanks for your patience.

    You can track upcoming screencasts on this page: https://knpuniversity.com/c...

    Cheers!

  • 2018-06-16 Arek Mateusiak

    can't wait for this one :-) when it will be available? :-)