A simple and durable FOSUserBundle alternative in minutes
Like most people in the Symfony world, when I started making apps, I was for a long time convinced FOSUserBundle was the only way to go or else I would have to implement everything by hand, and that thought scared the hell out of me.
TLDR;
I made a gist compiling the first few files needed to make a simple register / login / reset workflow, relieving your from installing FOSUserBundle and giving you 100% freedom to edit and customize without overriding anything and sticking purely on the Symfony components.
Recently on our main website in my company, after the 5th tutorial on how to use Guard or event the basic Symfony Authentication system, I decided that the famous bundle had done its time, and shall be put to death by composer remove
. I had read Damien Alexandre’s warning about this bundle and decided I would follow through.
The task was feeling daunting, as this is what it did for me:
- User entity and roles
- Login
- Register
- Password reset handling
- Email confirmation
- Templates in all various sorts
- Making me sleep at night thinking my app login was safe and easy
It turned out this was quite simple, readable and especially maintainable to do on my own. This article is a step-by-step guide into the process, so beware, I do not go into details, but I will point out to the documentation sections concerned.
User entity and roles
The (almost) only thing you need to know is that you want a User
entity that implements the UserInterface
from Symfony security component. That gives you some mandatory fields: email (or username), password, roles...and that's pretty much it. 3 doctrine fields. You can add the name later on if you wish or add it right now. You probably want to add a plainPassword
field to help you out manipulating forms, but you certainly don’t want it persisted !
Once your entity is set, let’s go for the configuration ! Our project is in 3.4, but the whole process is very similar in Symfony 4, I will try to update this guide but mainly this follows the Security section from the Symfony docs.
The security.yml
file
Following the documentation, you will have to define an encoder, a provider, a firewall and eventually some simple access_control rules like allowing anonymous users to reset their password. Some routes and controllers will be necessary to create, we will come to that just afterwards.
The login process
Once you have configured your firewall with the basic settings, you need to create some controllers and routes. You need a SecurityController
where you will have your login(AuthenticationUtils $authenticationUtils)
action returning your login template, which again is simply a form following certain naming conventions, the rest being handled by Symfony. A small template will be needed for your form to be usable.
The registration process
This time, Symfony does not really have your back as much as for the login process, but don’t worry all the tools are still there. You will need a RegistrationController
with a register()
action, and a RegistrationType
with the basic fields for your user, keeping in mind you will use the plainPasword
field defined in your entity with a RepeatedType
form field.
Once your form is ready, your basic {{ form(form) }}
template is done, and your submission is valid, several tools will help you finish things up:
UserPasswordEncoderInterface
: this will encode theplainPassword
into a securely storable hashed password.
That’s actually all you need. But it is very common to log the user in after registration so we will use theses:
TokenStorageInterface
: this is used to create a fresh newUsernamePasswordToken
and store it.SessionInterface
: this is just to ensure you can store the_security_main
serialized session key
And boom your user is now logged in.
Forgot your password ? We got you covered
Once you’ve done the registration process, this one is pretty easy and straightforward. The main difficulty here is that you will need to have emails enabled, an email template or simple message, and a new field in your User entity: passwordResetToken
. Then your new ResettingController
will have 2 actions: one for asking a reset token sent by email, and another to handle the actual password resetting with a new one. Your token will have to be set to null
after successful usage to ensure it's a one shot token.
You can of course imagine more complex logic with an expiry date for the token, but let's keep things simple for a starter pack. Simple hack: create a secure token with bin2hex(random_bytes(32))
on-the-fly. Your new password will be created using a simple ReaptedType
form and encoded like in your registration controller, and then you can - again - log in your user during the process, to keep everything smooth.
User roles
I won’t go into much details on this topic as it simply is a CRUD operation.
You might be thinking that it actually looks pretty simple and straight-forward, right ? Your read the above in a couple of minutes, that’s actually shorter than reading the customization section of the FOSUB after you spent an hour installing it.
This looks incomplete !
Yes, I missed out several important aspects that you will probably want at some point:
- Email address confirmation: this one is pretty easy as it’s the same as resetting a password, only the token is issued at the user instanciation, in another field.
- User enabling / disabling: this one is partly pure CRUD and partly another Symfony interface to implement on your entity:
AdvancedUserInterface
, or else making your own custom UserChecker - Event Dispatcher and Listeners: you will probably want a welcome email of some kind for your users, just dispatch a simple
Event
with the Symfony dispatcher in your controller. You are 100% in control of your workflow!
Do I really have to type all of this ?
No ! I just happen to have made a gist with all these files ready to copy and edit. If you liked this article, simply hit the clap button, once for every StackOverflow page concerning FOSUserBundle your visited since you started using it ?