Buy

Yeaaaa! You've done it! You've made it to the tutorial where we get to build a security system with Symfony. This stuff is cool. Seriously, these days, the topic of security is gigantic! Just think about authentication: you might need to build a traditional login form, or a token-based API authentication system, or two-factor authentication or authentication across an API to a Single Sign-On server or something I've never even dreamed of before! For authorization, there are roles, access controls and more.

Woh. So we're going to write some seriously fun code in this tutorial. And it will be especially fun, because there are some new cool toys in Symfony's security system that make it nicer than ever to work with.

Coding Along!

As always, to become a true Symfony security geek... and to obtain the blueprint to the Deathstar, you should definitely code along with me. Download the course code from this page. When you unzip it, you'll find a start/ directory that has the same code that you see here. Follow the README.md file for all the important setup details.

Oh, and if you've been coding along with me in the Symfony series so far, um, you're amazing! But also, be sure to download the new code: I made a few changes since the last tutorial, including upgrading to Symfony 4.1 and improving our fixture system. More on that later.

Anyways, the last setup step will be to open a terminal, move into the project and run:

php bin/console server:run

to start the built in web server. Ok: head back to your browser and open our app by going to http://localhost:8000.

Hello The SpaceBar! Our awesome intergalactic real news site that helps connect alien species across this side of the Milky Way.

Installing Security & Upgrading MakerBundle

Our first goal in this tutorial is to create an authentication system. In other words: a way for the user to login. No matter how you want your users to authenticate - whether it's a login form, API authentication or something crazier - the first step is always the same: brew some coffee or tea. The second step is also always the same: create a User class.

To do this, we're going to use a brand-spanking new feature! Woo! Find your terminal and run:

composer update symfony/maker-bundle

Version 1.7 of MakerBundle comes with a new command that will make our life much easier. Yep, there it is: 1.7. The new command is called make:user - try it:

php bin/console make:user

Ah! It explodes! Of course! Remember: in Symfony 4, our project starts small. If you need a feature, you need to install it. Run:

composer require security

Ah, check it out: this library has a recipe! When Composer finishes... find out what it did by running:

git status

A new config file! Check it out: config/packages/security.yaml. This file is super important. We'll start talking about it soon.

Creating the User Class with make:user

Before we run make:user again, add all the changed files to git and commit with a message about upgrading MakerBundle & adding security:

git add .
git commit -m "Upgraded MakerBundle and added security"

I'm doing this because I want to see exactly what the make:user command does.

Ok already, let's try it!

php bin/console make:user

Call the class User. Second question:

Do you want to store user data in the database

For most apps, this is an easy yes... because most apps store user data in a local database table. But, what if your user data is stored on some other server, like an LDAP server or a single sign-on server? Well, even in those cases, if you want to store any extra information about your users in a local database table, you should still answer yes. Answer "no" only if you don't need to store any user information to your database.

So, "yes" for us! Next: choose one property on your user that will be its unique display name. This can be anything - it's usually an email or username. We'll talk about how it's used later. Choose email.

And, the last question: is our app responsible for checking the user's password? In some apps - like a pure API with only token authentication, users might not even have a password. And even if your users will be able to login with a password, only answer yes if this app will be responsible for directly checking the user's password. If you actually send the password to a third-party server and it checks if it's valid, choose no.

Remember when I mentioned how complex & different modern authentication systems can be? That's why this command exists: to help walk us through exactly want we need.

I'm going to choose "No" for now. We will add a password later, but we'll keep things extra simple to start.

And... we're done! Awesome! This created a User entity, a Doctrine UserRepository for it, and updated the security.yaml file.

Let's check out these changes next!

Leave a comment!

  • 2018-09-17 Victor Bocharsky

    Hey Vlad,

    Not sure :) Didn't that example fit you?

    Cheers!

  • 2018-09-15 Vlad

    Any other options?

  • 2018-09-14 Victor Bocharsky

    Hey Vlad,

    Here's how simulate HTTP authentication in a functional test, see docs: https://symfony.com/doc/cur...

    Cheers!

  • 2018-09-13 Vlad

    How do I simulate user authentication in order to test authenticated actions using PHPUnit? I know how to do that for JWT authentication, but not regular user authentication. Please help!

  • 2018-09-11 Victor Bocharsky

    Hey Direktorius,

    We're going to release a new video every day! Working on it right now.

    Cheers!

  • 2018-09-11 Direktorius

    Very excited for new tutorials.

    When can we expect the followup videos?

  • 2018-09-10 weaverryan

    Hey St├ęphane!

    Ah, thanks for noting that! I had a little "pointer" on the wrong commit for generating the "start" code for this tutorial. I just fixed that and it should be reflected in just a few minutes. The start code you downloaded will also be missing a few CSS files and some fixtures changes. All will be better now. Sorry about that - but thanks again for pointing it out!

    Cheers!

  • 2018-09-10 St├ęphane

    Hello
    Thank for this new tuto.
    When I install the application with start folder, the version of Sf4 is 4.0.14 not 4.1 like you say in video ? It's normal ?

  • 2018-09-10 weaverryan

    Yo Peter Kosak!

    > Will we touch account lockout. Example if someone/bot will try to submit 1000 times your login form the account should be locked for 15 minutes. After 3 incorrect passwords, another 3 would be 30mins then for a day so is there any easy pre-build function for this?

    This was NOT planned. But, it's interesting. Mostly, this would probably be accomplished by adding a few extra fields to your User entity so you could track number of failed attempts, and when the last attempt was. Then, probably inside a checkCredentials() method that we'll learn about in a few days, I'd check that and fail if you have one of those conditions. For the "submit the login form 1000" times, that's a bit harder. You could store a counter in the session very easily - but probably the "attacker" is using a programmatic client with no session. So, you'd probably need a new table to tracks logins by "IP".

    Anyways, when you learn about the "authenticator", you'll see that it's very easy to add custom logic and add "exit" points.

    > Second question is more general but I was thinking about it when I saw lesson 3 of this course. JSON field.

    VERY good question. It comes down to the complexity of your role system. For example, here on KnpUniversity, we have 2 roles: ROLE_USER and ROLE_ADMIN. And, there are 4 of us with ROLE_ADMIN. It's really simple, so the roles field works GREAT. But, you're right that if you want to have a lot of control over roles - listing who has what role, even adding a description in the database for what a role does - then a relationship setup will be better. This is a very valid point. It's not a matter of performance - just choosing how much complexity your app needs.

    If you *did* want this setup, then yea, you could have a `Role` entity or even a Group entity that has an json array of roles (or a Group entity that is ManyToMany to a Role entity) and then a User can be ManyToMany to Group. You can see that this can scale up to a lot of different levels of complexity. The cool thing is that Symfony only cares that your User has a getRoles() method that returns a string of roles. If you have a really complex setup, your getRoles() method would ultimately just be looping over its related Role entity objects (or looping over its related Group objects... and *their* Role objects) to create this array of strings. Performance is not really a problem because getRoles() is only called on login and then stored in the session (though, there is a pull request - https://github.com/symfony/... - to change this behavior).

    I hope that clarifies a bit. Great question! :D

    Cheers!

  • 2018-09-10 Peter Kosak

    Ryan, first of all thank you soooo much for this course I've been waiting for like the other one regarding forms.

    Anyway I have 2 questions that comes to my mind.

    First one:
    Will we touch account lockout. Example if someone/bot will try to submit 1000 times your login form the account should be locked for 15 minutes. After 3 incorrect passwords, another 3 would be 30mins then for a day so is there any easy pre-build function for this?

    Second question is more general but I was thinking about it when I saw lesson 3 of this course. JSON field.
    This is soo powerfull field these days that I think will drive people into wrong database schema setup in the future. (instead of creating linked tables for manytomanythey will store it in one entity) I am will be probably one of them. We/You are going to be storing roles in DB as JSON value. Before json we would have ManyToMany reletionship between roles and user. So the question is; is it actually good practise to store them as a JSON. What if I want to list all the users where ROLE is "admin" and not "user"? Why we/you/symfony is not using this elsewhere for ManyToMany relationship but usually we have to create this manytomany relationship (2 entities)? Whats the difference between following relationships Student & Course vs User & Roles (why we dont use JSON to store the courses in student class?).

    Is it just inconsistency or does it have some logical answer? I think only the performace will be the answer so the next question is: is it actually better approach to store Roles in separate table/entity?