New features in my two-factor authentication bundle

In the last days SchebTwoFactorBundle has received some major updates. The current version is v0.3.0. I want to give you a brief overview of what has changed and how to use those features.

The biggest change so far was the refactoring of the authentication layer. I’ve removed a bunch of duplicate code and implemented an abstract interface for the two-factor authentication, which can be extended with any kind of authentication method. This enables the users of the bundle to implement their own authentication methods quite easily. Take a look at the documentation how it works.

Besides this I’ve added a “trusted computer” feature, that has been suggested by a contributor. An optional checkbox is shown in the authentication form, which makes it possible to flag your machine as “trusted”. Then the whole two-factor process will be skipped once you’ve completed the authentication process. The feature supports multiple computers and multiple users on the same machine.

I hope those features are useful and they help to secure your projects with an two-factor mechanism that’s easy to implement.

Say Hello to SchebTwoFactorBundle

I’ve recently done some posts about how to integrate two-factor authentication and Google Authenticator into Symfony2. I thought to myself “Why not make a bundle from it?” and that’s what i did: Say hello to SchebTwoFactorBundle. Now it’s dead simple to add two-factor authentication to your own website. Add the bundle via Composer and enable the authentication method you want.

Currently it supports both the email and the Google Authenticator method I’ve blogged about. It already has some features for customization, but I’m thinking about making it more flexible. Maybe some generic two-factor implementation, which allows you to plug in different kinds of two-factor modules.


Google Authenticator in Symfony2

Before you start reading:
If you just want to add two-factor authentication to your project, you should take a look at scheb/two-factor-bundle. If you’re interested in technical details, keep on reading 🙂

This is the follow up to previous post about two-factor authentication in Symfony2. As promised I also want to show you how to integrate Google Authenticator into your project. If you haven’t read my first post, I’d suggest doing it now, because it explains the principle more in detail. The following example code is widely identical to SonataUserBundle‘s integration.

To get started, you’ll have to install the Sonata Google Authenticator package. If you’re using composer (I guess so), you can simply execute:

Read more

Two-Factor Authentication in Symfony2

Before you start reading:
If you just want to add two-factor authentication to your project, you should take a look at scheb/two-factor-bundle. If you’re interested in technical details, keep on reading 🙂

For a project of mine I wanted to have some extra security because it contains critical features, only authorized people should have access to in any case. So I did some research if it’s possible to implement two-factor authentication in Symfony2. Sadly I didn’t find any good how-tos about that topic. Then I’ve found out that SonataUserBundle has Google Authenticator as an optional feature, so I did some reverse enginering to figure out how they did it.

This is how you implement two-factor authentication into Symfony2’s security layer. The following example will send out a random code to the user’s email address. I will do another post for Google Authenticator soon.

Read more

Password constraints in FOSUserBundle

I’ve recently tried to modify the password constraints of FOSUserBundle. To my surprise I’ve discovered that this is a little bit tricky.

Before you start I would suggest taking a look at validation.xml in the config directory of FOSUserBundle There you’ll find all the pre-defined constraints. As you can see, there is a class named FOS\UserBundle\Form\Model\ChangePassword. This is the data class, which is used in the change password form instead of the actual entity. So you have to change the password constraints on the User as well as on the ChangePassword class.

I have a bundle, which is extending FOSUserBundle, so I thought it is straight forward: Create a validation.xml which is overwriting the original one and put my own constraints in there. Unfortunatley that doesn’t work, instead my own constraints will simply be added to the default ones. So how to get rid of them? The trick is to define your own validation group.

This is how my configuration for the ChangePassword class looks like. The same goes for the User class.

By default FOSUserBundle is using the ChangePassword group to validate the change password form. With some extra lines in config.yml you can tell it to use a different one:

Now FOSUserBundle is using the MyChangePassword validation group for validation. This is also working for other forms like registration or the user profile. Take a look at the class FOS\UserBundle\DependencyInjection\Configuration and search for validation_groups to find out more about the configuration.

Default Values for Parameters

Sometimes it makes sense to have a default value for parameters. This can be done by creating a new file parameters_default.yml (can be any name you like) and adding it to config.yml just before parameters.yml.

Now you can overwrite the default values in parameters.yml on demand but you’re not longer forced to define those parameters.

Using Parameters in Routes

You can use parameters from parameters.yml in routes. There are serveral use-cases when this may be useful.

Let’s say you want to localize your routes but you want to store the list of available locales only once. Then you could define a parameter in config.yml:

Now you can use the parameter in your routing.yml:

Another use-case is routes based on a hostname. Instead of writing the hostname directly into routing.yml you could use a parameter, as seen in the official documentation.

As fas as I know parameters only work for routes defined in YAML, XML or PHP. If it also works for annotation routes, please let me know.

Importing Routes from a Controller

This is how you import routes from a specific controller (only works if you used annotations):

This is very useful if you have multiple controllers in single bundle but you can’t import the whole Controller directory at once because some routes have to be treated in a special way, e.g. you want a different prefix for every controller.

For reference, this is the original documentation of that feature.

PHPUnit: contains vs. stringContains

Note to myself: If I ever see that error again when running PHPUnit tests

please remember that $this->contains() is not the same as $this->stringContains(). The first one is a constraint for arrays, the second one is for strings.

SonataNotificationBundle does not consume messages

Today I was playing around with SonataNotificationBundle. I simply followed the installation instructions, but I encountered some problems.

First problem: You have to manually add SonataEasyExtendsBundle as a dependency. For some reason it is not automatically added by Composer. I guess they’ve just forgot it in the composer.json, so maybe this problem is already fixed by the time you read this.

After the bundle was working I’ve implemented a new component to dispatch some messages. Everything was working fine, I could even see the message in the database (used Doctrine for the message queue). To complete that test scenario I implemented a consumer for my message type and registered it as a tagged service. I started the consumer job as described in the documentation …

… but nothing happend!

Second problem: The messages did not process. Some debugging in the code showed me that it only fetched messages with type default from the database. This was strange, because the console told me that my message consumer is registered and there was no default type at all. I did some research on this problem and found a very useful post of someone having the exact same problem. There was as a working configuration in the thread so a gave it a try – and it worked! The trick was to configure at least one queue. Your configuration should look similar to this:

The catchall is just a name for the queue, it can be anything. It’s more important to make it the default queue, which is done with default: true.