Skip to content

Two-factor authentication (2fa)

Sharp provides a two-factor authentication (2fa) system, out of the box. You can configure it like this:

php
class SharpServiceProvider extends SharpAppServiceProvider
{
    protected function configureSharp(SharpConfigBuilder $config): void
    {
        $config
            ->enable2faByNotification()
            // or ->enable2faByTotp()
            // or ->enable2faCustom()
            // [...]
    }
}

With this configuration, Sharp will display a second screen to the user, after a successful password based login, asking for a 6-digit code. This code will be provided to the user depending on the configuration:

  • enable2faByNotification(): a notification is sent to the user (email by default, but you can tweak this, see below)
  • enable2faByTotp(): the user must use a 2fa authenticator app (like Google or Microsoft Authenticator) to generate a code
  • enable2faCustom(): in this case you must provide your own 2fa handler, see below.

Handling the 2fa code via a notification

WARNING

To be able to receive notifications, your User model must use the Illuminate\Notifications\Notifiable trait.

With this option, Sharp will send a notification to the user, containing the 6-digit code. By default, this notification is sent by email. You can override this behavior by providing your own handler class which must extend Code16\Sharp\Auth\TwoFactor\Sharp2faNotificationHandler:

php
class SharpServiceProvider extends SharpAppServiceProvider
{
    protected function configureSharp(SharpConfigBuilder $config): void
    {
        $config
            ->enable2faCustom(\App\Sharp\My2faNotificationHandler::class)
            // [...]
    }
}
php
class My2faNotificationHandler extends Sharp2faNotificationHandler
{
    protected function getNotification(int $code): Notification
    {
        return new My2faDefaultNotification($code);
    }
}

Handling the 2fa code via a TOTP authenticator app

WARNING

This implies a bit more work to implement, but this method is more secure than the notification handler. The out-of-the-box implementation implies that you leverage Eloquent.

With this option, Sharp will ask the user to register the app in a 2fa authenticator (like Google or Microsoft Authenticator). The user will have to provide a 6-digit code generated by the app to Sharp, in order to be authenticated.

First, require two packages needed for this feature:

bash
composer require pragmarx/google2fa-laravel
composer require bacon/bacon-qr-code

Then, you'll need to configure the totp handler:

php
class SharpServiceProvider extends SharpAppServiceProvider
{
    protected function configureSharp(SharpConfigBuilder $config): void
    {
        $config
            ->enable2faByTotp()
            // [...]
    }
}

Add three columns in the users table to store the 2fa secret, 2fa recovery codes and 2fa confirmation timestamp. Here’s a migration example:

php
return new class extends Migration
{
    public function up(): void
    {
        Schema::table('users', function (Blueprint $table) {
            $table->text('two_factor_secret')
                ->after('password')
                ->nullable();

            $table->text('two_factor_recovery_codes')
                ->after('two_factor_secret')
                ->nullable();

            $table->timestamp('two_factor_confirmed_at')
                ->after('two_factor_recovery_codes')
                ->nullable();
        });
    }
};

After that, you must provide a way for your users to register the app in their 2fa authenticator. Sharp can help a lot with that, by extending two built-in Commands; one for activating and one for deactivating 2fa. The idea is to add these commands in a "profile" SingleShow, or in some related Entity List.

php
class Activate2faCommand extends SingleInstanceWizardCommand
{
    use Code16\Sharp\Auth\TwoFactor\Commands\Activate2faViaTotpWizardCommandTrait;
}
php
class Deactivate2faCommand extends SingleInstanceCommand
{
    use Code16\Sharp\Auth\TwoFactor\Commands\Deactivate2FaViaTotpSingleInstanceCommandTrait;
    // or Code16\Sharp\Auth\TwoFactor\Commands\Deactivate2FaViaTotpEntityCommandTrait
}

The first command is a wizard which will guide the user through the registration process; the second one is to deactivate the 2fa. Both require to enter a password. You can tweak these commands and provide your own implementation if needed.

Finally, if you need more control, you can provide your own handler class via -⁠>enable2faCustom(), which must extend Code16\Sharp\Auth\TwoFactor\Sharp2faTotpHandler.

Enabling 2fa for some users only

Providing your own handler implementation, you can override the isEnabledFor method to enable 2fa for some users only:

php
class My2faNotificationHandler extends Sharp2faNotificationHandler // or Sharp2faTotpHandler
{
    public function isEnabledFor($user): bool
    {
        return $user->hasGroup('sharp');
    }
}

Customize the 2fa form

You can also change the default help text display above the 2fa form in the handler:

php
class My2faNotificationHandler extends Sharp2faNotificationHandler // or Sharp2faTotpHandler
{
    public function formHelpText(): string
    {
        return sprintf(
            'You code was sent via SMS to your phone number ending in %s',
            substr(User::find($this->userId())->phone, -4)
        );
    }
}

Released under the MIT License.