Recently I had the opportunity to work a bit with the Symfony security component. Let me tell you something: it is amazing. Ok, I know, it may not be the most easy to understand implementation in the world, but it is really flexible and allows you to do almost everything. Once you get it, you’ll love it.
I had to implement an auto login mechanism to let users authenticate via a query string parameter and I want to share with you what I’ve learned.
First of all, let’s face it: having an auto login link is not the best way to preserve your user’s privacy, I know.
I would like to put aside any security concern (pretty weird for a post about security layer uh?), to concentrate just on the Symfony security component.
I’m not even showing any code in this post, but the concept you’ll read here are valid for every custom authentication provider you’ll need.
Symfony security layer is based on two steps:
In this post we’re talking about authentication, and we’ll leave the authorization for another post.
Authorization is quite simple, once you have the big picture in mind and you get used to the terminology. Three main actors are involved in the authentication process:
- A Token
- A Listener
- An Authentication Provider
The token is probably the most confusing piece, but its role is fundamental and afterall its job is quite simple. Think of the token as a simple container: the token retains the user’s credentials present in the request and, after the authentication, the authenticated user itself.
The security system uses the token to move these information through the involved components.
The listener is the glue between the request and the authentication provider.
Its job is to retrieve the user’s authentication parameters (usually from the request), create a token and try to authenticate it through the authentication manager (which will delegate the authentication process to a set of compatible authentication providers). If the authentication manager returns an authenticated token, the listener stores it in the security context.
The authentication provider
The authentication provider is the real boss here. It verifies the user’s authentication information stored in the token and returns an authenticated token which contains the authenticated user.
As you can have many different tokens and many different authentication providers, authentication manager must know which provider is capable of handling a particular token. Here comes to play the AuthenticationProviderInterface which contains an authenticate and a support method. The support method will belp the authentication provider to decide which provider is able to authenticate a particular token.
The big picture
How does these three objects work together and how can the security system know anything about these?
You read in the documentation (you read the doc, right?) that security is a two steps process: authentication and authorization. Authentication is managed by a Firewall. When the firewall is activated, it calls the listeners (we talked about them above): any of the configured listeners could be able to authenticate a user.
Unfortunately tying firewall and listeners together is not so easy, because you can have different firewalls to secure different parts of your application, depending on your configuration.
To solve this you need to implement a special factory class, that implements SecurityFactoryInterface that will create the needed DIC entries.
This is pretty much all you need to know to implement your custom authentication provider.
I haven’t written anything about my implementation of the auto login feature. The reaons is the implementation has nothing magic in it, it’s quite simple and it doesn’t add so much value to the explanation.
If you’re looking for code, there is a great cookbook about how to implement a custom authentication provider. You can use that as a good starting point for your implementation. While learning how all these classes work together, I found very helpful to look at the code and in particular at the RememberMeListener.
That said, if you’d like to read about my implementation, I can share it in another post about this topic, or maybe plan a screencast about it.