Flag of Ukraine
SymfonyCasts stands united with the people of Ukraine

Symfony 3 Security: Beautiful Authentication, Powerful Authorization

1:22:39

What you'll be learning

// composer.json
{
    "require": {
        "php": ">=5.5.9",
        "symfony/symfony": "3.1.*", // v3.1.4
        "doctrine/orm": "^2.5", // v2.7.2
        "doctrine/doctrine-bundle": "^1.6", // 1.6.4
        "doctrine/doctrine-cache-bundle": "^1.2", // 1.3.0
        "symfony/swiftmailer-bundle": "^2.3", // v2.3.11
        "symfony/monolog-bundle": "^2.8", // 2.11.1
        "symfony/polyfill-apcu": "^1.0", // v1.2.0
        "sensio/distribution-bundle": "^5.0", // v5.0.22
        "sensio/framework-extra-bundle": "^3.0.2", // v3.0.16
        "incenteev/composer-parameter-handler": "^2.0", // v2.1.2
        "composer/package-versions-deprecated": "^1.11", // 1.11.99
        "knplabs/knp-markdown-bundle": "^1.4", // 1.4.2
        "doctrine/doctrine-migrations-bundle": "^1.1" // 1.1.1
    },
    "require-dev": {
        "sensio/generator-bundle": "^3.0", // v3.0.7
        "symfony/phpunit-bridge": "^3.0", // v3.1.3
        "nelmio/alice": "^2.1", // 2.1.4
        "doctrine/doctrine-fixtures-bundle": "^2.3" // 2.3.0
    }
}

Ah, security! Quick run!

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 system using Json web tokens (not as scary as they sound!). Then, we'll turn to authorization, with roles, voters and other good stuff:

  • Security & Firewall Fundamentals
  • Creating a custom login form
  • Making a User (ooOOOoo)
  • 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)
  • Registration Form
  • Manual authentication

... and how to create a back door into your site. Just kidding! Let's make some secure sites!


Your Guides

Ryan Weaver Leanna Pelham

Buy Access

Join the Conversation?

79
Login or Register to join the conversation

oh at last! cant wait to get started.

4 Reply

lets hope the rest comes soon enough!

Reply
Default user avatar

Hey, Ryan. Thanks for great tutorials. Helped very much to master Symfony! Watched them on one breath! Can't wait for Security tutorial to be released. Keep going on!

3 Reply
Tt S. Avatar

Hi Ryan and Leanna, I just wanted to say thank you for all the amazing tuts, I'm just wondering will there be any tuts regarding to Symfonys' Performance Tuning in the future?

3 Reply

Hey Chen!

Ah, thank you! I think this a great topic - more and more people are asking for something like this. Which parts are most important to you? Tuning the bottom-line speed of each request? Caching? Something else?

I'm adding it to our list right now :).

Cheers!

1 Reply
Tt S. Avatar

Hi Ryan, thanks for the reply. I think I have got some real world examples for you and this might be (may not be) seem like a challenge... But we'll be so happy if you accept this challenge; BTW, will it be possible to experimenting them on Symfony3 framework as well? Many thanks and the best regards! :-)

Push it to the limits - Symfony2 for High Performance needs:
http://symfony.com/blog/pus...

Handling 1 Billion requests a week with Symfony2:
http://labs.octivi.com/hand...

The Easy Way Of Building A Growing Startup Architecture Using HAProxy, PHP, Redis And MySQL To Handle 1 Billion Requests A Week:
http://highscalability.com/...

1 Reply

These are awesome resources - thanks for sharing them :). And yes, we would definitely do this on Symfony 3!

1 Reply
Default user avatar
Default user avatar Julian | posted 5 years ago | edited

Hi Guys! I'm using the FOSUserBundle and I have two Problems while logging out.

  1. I have set up a MainController and I want to have a different view for ppl who are logged in. So I let my MainController do this:


/**
     * @Route("/")
     */
    public function loginAction()
    {
        if (!$this->get('security.authorization_checker')->isGranted('IS_AUTHENTICATED_FULLY'))
        {
            return $this->render('MainBundle:Default:log_index.html.twig');
        }
        else
        {
            return $this->render('MainBundle:Default:show.html.twig');
        }

    }

It kinda works BUT when I log out there is the default login page rendered and not my own log_index.html what I want to render when a user logs out.

  1. Problem is using the firewall correctly
    When I want my firewall to handle logouts like that:
    
    

logout:

       path:   /logout
       target: /log_index
       anonymous:    true

Symfony throws Excetions like I'm using the wrong option there..

Maybe you can help me.

Cheers!
1 Reply
Victor Avatar Victor | SFCASTS | Julian | posted 5 years ago | edited

Hey, Julian!

What about the second problem, it's difficult to understand from your message without proper formatting, but I was tricky and was able to see what's the problem :) You placed anonymous key under logout but should under your firewall name like:


your_firewall_name:
    logout:
        path:   /logout
        target: /log_index
    anonymous:    true

Please, fix it first and let me know if your first issue still exist.

Cheers!

1 Reply
Default user avatar

Thanks a lot dude! It was definitly my own YML mistake. I just have still a routingproblem. When my auth_token is set Annonymous my project is still rendering the default login form. When I have authorization he renders the show.html I just want to have rendered there ;). My dream is to render the log_index.html if a user logsout or is at the Annonymous-Status.

1 Reply
Victor Avatar Victor | SFCASTS | Julian | posted 5 years ago | edited

Hey, do you consider isGranted('IS_AUTHENTICATED_REMEMBERED')? It returns true for all logged in users, even if they are logged in because of a "remember me cookie". (Notice that I don't use negation (! sign) in if statement):


    public function loginAction()
    {
        if ($this->get('security.authorization_checker')->isGranted('IS_AUTHENTICATED_REMEMBERED')) {
            return $this->render('MainBundle:Default:show.html.twig');
        }

        return $this->render('MainBundle:Default:log_index.html.twig');
    }

Please, let me know if it helps you!

Reply
Default user avatar

This works the same way as the Controller before. There is still a default render to /login but I will check out now how to customize the Routes after a logout. Thank you so much and I'm also thankfull for any further advices!

Reply

Ah, OK!

If you have not found any solution yet, could you explain me again what exactly don't work as you want in this code? I'll try to help you with it too.

Cheers!

Reply
Victor Avatar Victor | SFCASTS | Julian | posted 5 years ago | edited

BTW, you could check full security reference here http://symfony.com/doc/current/reference/configuration/security.html#full-default-configuration, or just use Symfony console command:

bin/console config:dump-reference security```

1 Reply
Paweł C. Avatar
Paweł C. Avatar Paweł C. | posted 5 years ago

Hi, Where should I store files uploaded by users to prevent them to be available by everyone? I mean, if I store files in web/ directory then everyone have access to them by writing proper url

Reply

Hey Paweł C.

In that case you could use AWS S3 service for data storage, or, if you are storing files locally, then, you may want to store them out of the web directory and then create a controller's action that will *do* that "serving" logic (There is where you can check roles)

Cheers!

Reply
Mamadou Avatar
Mamadou Avatar Mamadou | posted 5 years ago

Hey, Ryan. Thanks for this great tutorials. I just wanted to know if it is compatible with symfony 4 or should I wait for a specific tutorial. Thanks bro and good job.

Reply

Hey Mamadou,

Thanks for your kind words! You can wait for this new course based on Symfony 4 and our brand Stellar project, we're really want to release it soon, but I have no estimations yet. So, if you impatient, you can start with this one - the code is almost the same, well, except that you need to do it in a Symfony 4 way with Symfony Flex, you know, installation process and file structure are a bit different in Symfony 4. If you find any issues with Symfony 4 - let us know in comments section and we'll help you.

Cheers!

Reply
Default user avatar
Default user avatar Yoni L. | posted 5 years ago

Hi Ryan and Leanna,
Did you plan a tutorial for translation service? I spent so much time to find usefull and up-to-date infos on the official doc, it could be usefull.

Thanks

Reply

Hi Yoni!

We don't have an active plan to talk about translations, but it's a good topic! I would love to know what specific pieces were most difficult for you - we could use that to plan the tutorial!

Cheers!

Reply
Default user avatar

Ok, to begin, most of the tutorials available describe how to use translator from scratch [http://symfony.com/doc/2.8/...], but the translator service is already active and the name is "translator.default" for injection.

- The fact that symfony can manage and create the file they are always talking about in that tutorials (xml, po, array...) with the console.

I know that we can use anything to render view but we can suppose that we use the excellent twig part.
I use {%trans%} in twig, how to generate easily the translation file in fr_FR and en_GB for example and where is it created?
I was force to google that (or Bing or yahoo...) to finaly understand: It's so easy...
- the translation folders which is a very sensitive point of 3 lines in the doc

If i was looking for a tutorial, I would like to have:
- quick overview of the global process
- (how to) the front end translation (twig),
- (how to) the back end translation (for api, ajax, error thrown...).
- the awesome translation file generation and the console debuging.
- ice on the cake for fun: custom translator with twig filter.

Often when already have some text to translate, and the official doc is beginning at the opposite of that.
Do we have a sugestion page on the KnPU site with vote?

Reply

Thank you so much for this! And you also highlighted some problems with the official docs that we can fix :).

I have added this to our internal screencast idea list. There's no public place to vote for ideas currently. Well, no "official" place - often times people click the "Ideas or Feedback" button on the bottom right of the page and add the idea there. Other people vote on those. We've been thinking about adding a proper spot on the site - it would be great to crowdsource what people are having most trouble with!

Cheers!

Reply

Oh well anxiously waiting for this tutorial series to be released! I learnt a lot from Starting in Symfony 3 series, cant wait to get to this new part!

Reply

This will be released the week after next (so about 9 days) - but parts of it may be released next week :)

2 Reply

Thats amazing! indeed would be awesome to be able to start even without the screencast, with the scripts maybe?

Reply

We'll have a script content without code blocks first. Code blocks will be added a bit later, after screencasts.

1 Reply

Nice Ryan!
I'm looking forward to watching that serie in the next.. :)

Reply
Default user avatar
Default user avatar Yuriy Stenin | posted 5 years ago

Hi, thanks for your great courses. I've been inspired your courses and creates own simple app for blogging http://demo-app.dev.htmlcms.me... I Am beginner in symfony

Reply
Default user avatar

Looking forward to this tutorial

Reply
Default user avatar
Default user avatar Damir Rama | posted 5 years ago

Hi Ryan, can you tell me when this tutorials come out? Thx for the work. It's greeeat!

Reply

I have a few other tutorials scheduled before this one, so it won't be in April :/. BUT, *very* soon, we'll be releasing the new Symfony REST course about security, and I think you might be able to use some good things from it :). http://knpuniversity.com/sc...

If you hit the "Notify me" button at the top - we'll send you a one-time email when this course comes out.

Cheers!

1 Reply
Default user avatar
Default user avatar Léo Li | posted 5 years ago

LOOKING FORWARD TO THIS

Reply
Default user avatar
Default user avatar Raphael Schubert | posted 5 years ago

Any scheduled date to this tutorial??

Reply

So glad you asked :). Yes, definitely June, hopefully early. I've already coded it up and will start recording soon (just need to get forms and Symfony REST part 5 out the door first).

Reply
Default user avatar
Default user avatar Javier Mendez | posted 5 years ago

do you have a class that takes this Authentication to JWT? if we already implemented this type of authentication and then decide to stop using sessions and we want to use tokens...

Reply

Hey Javier!

Are you looking for this? http://knpuniversity.com/sc... Or something else?

Let me know, and cheers!

Reply
Default user avatar
Default user avatar Gunnar Polte | posted 5 years ago

Hey Ryan,
it was a pleasure to follow your tutorial.

I would like to know one more things.
- How to create a password forgotten functionality?

Could you please give some advice.
Thank you.

Reply

Hey Gunnar,

Good question! You can take a look at FOSUserBundle which already has this functionality implemented out of the box, you just need to enable it. If you do not want to install an entire bundle and you've already implemented login/register features by yourself, well, you can grab some already written logic from FOSUserBundle to implement password resetting by yourself. But the concept is simple: when user request a password reset for his email - you need to generate a unique ID generally called "token", e.g. an md5 hash for that user. It's a good idea to add some kind of TTL for this token to make it token not active after a while for security reasons. This token should be sent to user's email as a link, and when user click this link - if token is valid and not expired - you show a password reset form, i.e. a new password field, or 2 fields: "new password" and "confirm new password". When user enter a new password - encrypt and store it in DB, also clearing the password resetting token for security reasons.

Cheers!

Reply

Hi Ryan, I watch videos and everything working fine, but I have problem I want to redirect user by roles after login. Is there some easy solution?

Reply

Hey sasa1007

I'm not sure if this is the best way todo it but at least it will give you the flexibility that you require.
If you make use of the "always redirect to the default page" system, then you can implement a custom route action that will work as a central entry point, where you can implement all the logic required to decide the proper redirection
https://symfony.com/doc/cur...

Cheers!

Reply
Default user avatar
Default user avatar Lee Ravenberg | posted 5 years ago

Followed the entire course and learned a lot! Authentication is awesome. When I finished the last video I really went like 'doh.. are we done already?'. So I went on to build a Password Reset form by making a LoginHash entity. But I ran into a problem when storing the new password into the database. If anyone is curious, I made a Stackoverflow post here: http://stackoverflow.com/qu...

Reply

Yo Lee!

Haha, sorry to disappoint ;). Once you've got authenticators down, you're a pro. Well, there's *always* more to security :).

I just left a comment on your SO - it's definitely something minor: based on your post, I think you understand things really well. I actually *always* call persist() - even though I don't need to - so I'm less familiar with what might ultimately be causing it.

Cheers!

1 Reply
Default user avatar
Default user avatar Lee Ravenberg | weaverryan | posted 5 years ago

Thanks Ryan! I really appreciate you taking the time to look at it.

I finally decided to remove the doctrine Password Hash Listener and encode the passwords at an other place. This works fine. I am still curious about what the cause of the issue is though. Doctrine scares me.

Reply

That's probably not a bad idea - I try to avoid Doctrine listeners if I can, and instead do work in services, and call those services when needed. Doctrine is great, but Doctrine listeners are a weird place, where you're already right in the middle of saving something. Not a bad policy to avoid :).

Cheers!

Reply
Vladimir Z. Avatar
Vladimir Z. Avatar Vladimir Z. | posted 5 years ago

Hi Ryan,
Is there any way to get a completion certificate?
This course is perfect for renewal of one of my certifications as part of continuing education, but I need to provide a completion certificate with the course name and my full name on it.
Thank you!

Reply

Hey Vlad!

This is something we're actually working on right now (and your feedback about what you need on the certificate is very useful!). But, we'd be happy to make a certificate for you before then so you have it. Just send us an email (hello@unviersity.com) with the details (e.g. your full name).

Happy this topic hit on your continuing ed requirements - that's awesome :).

Cheers!

Reply
Vladimir Z. Avatar

Thank you, Ryan, that's great! I'll shoot you an email, once I finish the course!

Reply
Default user avatar
Default user avatar fredrsf | posted 5 years ago

Hello everyone, how can i do automatically login after registration, if registration form data came from ajax request?

Reply

Hey fredrsf ,

Did you see the latest chapter in this course: <a href="https://knpuniversity.com/screencast/symfony-security/automatic-login-handling&quot;&gt; Automatically Login after Registration</a>. It explain how to do it, except that you should wrap $this->get('security.authentication.guard_handler')->authenticateUserAndHandleSuccess(...) call with additional condition:

if ($request->isXmlHttpRequest()) {
    return $this->get('security.authentication.guard_handler')
        ->authenticateUserAndHandleSuccess(...);
}

If you'll send an AJAX request - isXmlHttpRequest() returns true in this case. And don't forget to handle that case when you have a normal (not AJAX) request.

Cheers!

Reply
Cat in space

"Houston: no signs of life"
Start the conversation!

userVoice