• Stars
    star
    8
  • Rank 2,099,232 (Top 42 %)
  • Language
    PHP
  • License
    MIT License
  • Created about 2 years ago
  • Updated 4 months ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Webauthn Two-Factor-Authentictication Plugin for scheb 2fa

Webauthn Two-Factor-Authentictication Plugin for scheb/2fa

This repository contains a plugin for scheb/2fa that adds support for Webauthn authenticators (like a Yubikey) as a second factor.

Feautures

  • Support of all webauthn authenticators as second factor
  • Supports multiple authenticators per user
  • Backward compatibility for existing registered U2F keys (from r/u2f-two-factor-bundle)

Requirements

  • Symfony 6
  • PHP 8.1 or later
  • webauthn/webauthn-lib 4.0 or later

If you want to use symfony 5.* and PHP 7.4, use the version 1.0.0 of this bundle.

Installation

  1. Install the bundle composer require jbtronics/2fa-webauthn
  2. Enable the bundle in your config/bundles.php (normally done by Symfony flex automatically)
  3. If you want to use the easy doctrine integration, add the web-authn symfony bundle: composer require web-auth/webauthn-symfony-bundle. You do not need to run the community recipe, as we just use the doctrine type definitons from the bundle. Add Webauthn\Bundle\WebauthnBundle::class => ['all' => true], to your config/bundles.php instead.

Setup and Usage

After following the Installation steps, do the follwing steps to setup the library:

  1. Add Jbtronics\TFAWebauthn\Model\TwoFactorInterface interface to your user entity:
use Jbtronics\TFAWebauthn\Model\TwoFactorInterface as WebauthnTwoFactorInterface;

class User implements WebauthnTwoFactorInterface
{
    /** 
     * @var Collection<int, PublicKeyCredentialSource>
     * @ORM\OneToMany(targetEntity="App\Entity\WebauthnKey", mappedBy="user", cascade={"REMOVE"}, orphanRemoval=true)
     */
    private $webauthnKeys;
    
    /**
     * Determines whether the user has 2FA using Webauthn enabled
     * @return bool True if the webauthn 2FA is enabled, false otherwise
     */
    public function isWebAuthnAuthenticatorEnabled(): bool
    {
        //Return true to enable webauthn 2FA
        return count($this->webauthnKeys) > 0;
    }
    
    /**
     * Returns a list of all legacy U2F keys, associated with this user
     * Return an empty array, if this user does not have any legacy U2F keys.
     * @return iterable<LegacyU2FKeyInterface>
     */
    public function getLegacyU2FKeys(): iterable
    {
        return []; //If you have no legacy U2F keys, return just an empty array
        //return $this->u2f_keys; //Otherwise return the legacy keys (see migration section below)
    }

    /**
     * Returns a list of all webauthn keys, associated with this user
     * @return iterable<PublicKeyCredentialSource>
     */
    public function getWebauthnKeys(): iterable
    {
        return $this->webauthnKeys;
    }

    /**
     * Returns the webauthn user entity that should be used for this user.
     * @return PublicKeyCredentialUserEntity
     */
    public function getWebAuthnUser(): PublicKeyCredentialUserEntity
    {
        //Return webauthn user definition for this user. As we just use it as an two-factor authentication, the values here are most likely not that important
        return new PublicKeyCredentialUserEntity(
            $this->getUsername(), // The Webauthn Name (like a username)
            $this->getID(), // A unique identifier for this user
            $this->getDisplayName() // The display name of this user (optional, otherwise null)
        );
    }
}
  1. Create a new entity for the webauthn keys. For simplicity we use the templates from the web-auth/webauthn-symfony-bundle (see here for more infos)
declare(strict_types=1);

namespace App\Entity;

use Doctrine\ORM\Mapping as ORM;
use Ramsey\Uuid\Uuid;
use Ramsey\Uuid\UuidInterface;
use Webauthn\PublicKeyCredentialSource as BasePublicKeyCredentialSource;
use Webauthn\TrustPath\TrustPath;

/**
 * @ORM\Table(name="webauthn_keys")
 * @ORM\Entity()
 */
class WebAuthnKey extends BasePublicKeyCredentialSource
{
    /**
     * @var string
     * @ORM\Id
     * @ORM\Column(type="integer")
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    private $id;
    
    /**
     * @ORM\ManyToOne(targetEntity="App\Entity\User", inversedBy="webauthnKeys")
     **/
    protected ?User $user = null;
    
    //You can declare additional fields too, if you want to store additional information about the key (like a name)
    private $name;


    public function getId(): string
    {
        return $this->id;
    }
    
    public static function fromRegistration(BasePublicKeyCredentialSource $registration): self
    {
        return new static(
            $registration->getPublicKeyCredentialId(),
            $registration->getType(),
            $registration->getTransports(),
            $registration->getAttestationType(),
            $registration->getTrustPath(),
            $registration->getAaguid(),
            $registration->getCredentialPublicKey(),
            $registration->getUserHandle(),
            $registration->getCounter(),
            $registration->getOtherUI()
        );
    }
}
  1. Include javascript frontend code into your project: For webauthn we need some javascript code to interact with the authenticators. Copy the file from src/Resources/assets/tfa_webauthn.js to your project and include it either by loading it via a <script> tag or by including it in your webpack using .addEntry().

  2. Add configuration file config/packages/jbtronics_2fa_webauthn.yaml:

tfa_webauthn:
  enabled: true
  
  # Optional configuration options:

  # timeout: 60000 # The timeout in millisceconds to allow the user to interact with the authenticator. Default: 60000
  # template: '' # The template to use for the login form
  
  # rpID: null # The relying party ID of your application. If null, the current host will be used. Default: null
  # U2FAppID: null # The U2F AppID of your application. If null, the current host will be used. Default: null
  
  # These settings are most likely not important for two-factor authentication:
  # rpName: 'My Application' # The relying party name of your application, Default: 'My Application'
  # rpIcon: null # The relying party icon of your application. Default: null
  1. Customize the login template: Similar to the base login template of the scheb/2fa bundle you will most likely need to override the login template of this bundle to integrate it into your design. Copy the template from Resources/views/Authentication/form.html.twig to your project and customize it to your needs. Configure the template setting in the bundle config to your new path.

Registration of new keys

In principle the login with exsting keys should work now, but you will most likely need some possibility to register new keys. To make this easy there is the Jbtronics\TFAWebauthn\Services\TFAWebauthnRegistrationHelper service to help you with this:

  1. Create a new controller, which will handle the registration, which should looks like this:
    use Jbtronics\TFAWebauthn\Services\TFAWebauthnRegistrationHelper;Ä
    
    class WebauthnKeyRegistrationController extends AbstractController
{
    /**
     * @Route("/webauthn/register", name="webauthn_register")
     */
    public function register(Request $request, TFAWebauthnRegistrationHelper $registrationHelper, EntityManagerInterface $em)
    {

        //If form was submitted, check the auth response
        if ($request->getMethod() === 'POST') {
            $webauthnResponse = $request->request->get('_auth_code');

            //Retrieve other data from the form, that you want to store with the key
            $keyName = $request->request->get('keyName');


            try {
                //Check the response
                $new_key = $registrationHelper->checkRegistrationResponse($webauthnResponse);
            } catch (Exception $exception) {
                // Handle errors...
            }
            
            //If we got here, the registration was successful. Now we can store the new key in the database
            
            //Convert our returned key into an database entity and persist it...
            $keyEntity = WebauthnKey::fromRegistration($new_key);
            $keyEntity->setName($keyName);
            $keyEntity->setUser($this->getUser());

            $em->persist($keyEntity);
            $em->flush();
            
            
            $this->addFlash('success', 'Key registered successfully');
            //We are finished here so return to another page
            return $this->redirectToRoute('homepage');
        }


        return $this->render(
            'webauthn_register.html.twig',
            [
                //Generate the registration request
                'registrationRequest' => $registrationHelper->generateRegistrationRequestAsJSON(),
            ]
        );
    }
}
  1. Create a template with a form, which will be used to register the new key. The form should look like this:
<form method="post" class="form" action="{{ path('webauthn_register') }}" data-webauthn-tfa-action="register" data-webauthn-tfa-data='{{ registrationRequest|raw }}'>
    <input type="text" name="keyName" id="keyName" placeholder="Shown key name"/>
                
    <button type="submit" class="btn btn-success">Add new Key</button>
        
    <input type="hidden" name="_auth_code" id="_auth_code" />
        
</form>

The data-webauthn-tfa-action attribute marks the form as webauthn registration form and is handled by the frontend code included above. If the form is submitted, the frontend code will catch that and start a registration process. The response is put it into the hidden input field with the id _auth_code and sent to our controller for parsing.

Migrate from r/u2f-two-factor-bundle

  1. Replace the R\U2FTwoFactorBundle\Model\U2F\TwoFactorKeyInterface interface of your U2FKey entity with Jbtronics\TFAWebauthn\Model\LegacyU2FKeyInterface and remove the fromRegistrationData() function (as we do not need it anymore).
  2. Replace the R\U2FTwoFactorBundle\Model\U2F\TwoFactorInterface interface of your user with Jbtronics\TFAWebauthn\Model\TwoFactorInterface, configure it (see above) and replace/rename your getU2FKeys() function to getLegacyU2FKeys().
  3. (Optional:) If your appID is not the same as your domain, configure it with the U2FAppID option. But this should normally not be needed
  4. Remove the old routes, templates and settings of the r/u2f-two-factor-bundle and remove it from your application
  5. Follow the setup steps above

License

This bundle is licensed under the MIT license. See LICENSE for details.

Credits

More Repositories

1

CrookedStyleSheets

Webpage tracking only using CSS (and no JS)
CSS
3,288
star
2

UE4-CheatSheet

An Cheat Sheet for Unreal Engine 4 C++ programming, licensed under CC BY-NC-SA 4.0
TeX
431
star
3

ESP32Console

Extensible UART console library for ESP32 with useful included commands.
C++
152
star
4

settings-bundle

A bundle to easily create typesafe, user-configurable settings for symfony applications
PHP
73
star
5

PCBruler

A PCB Ruler created in KiCAD
66
star
6

DS1054_BodePlotter

A Python program that plots Bode diagrams of a component using a Rigol DS1054Z and a JDS6600
Python
34
star
7

SDBatchToolsGUI

A Gui for the Substance Designer Tools
C#
24
star
8

WireDebugger

Debug AVRs with DebugWire via SerialPort
C#
21
star
9

AVR-ZIF-Programmer

An AVR Programmer based on USBasp which uses a ZIF-Socket for Programming.
KiCad Layout
10
star
10

bs-treeview

TreeView element for browsers without any dependencies
TypeScript
7
star
11

FM1216duino

An Arduino Library to controlo FM1216-Tuner over I2C (only Soft-I2C)
C
7
star
12

FTController

A Motor controller for Fischertechnik with many cool features.
HTML
5
star
13

Source2Strings

A repository containing extracted strings from existing Source2 games
Shell
5
star
14

Recolldroid

A android app for comfortable use of Recoll-WebUI on android.
Java
4
star
15

ESP07-Breakout

The KiCAD Project files for my ESP07-Breakout
KiCad Layout
4
star
16

Part-DB-android

Part-DB Barcode Scanner for Android
Java
3
star
17

StuRa-Finanzsoftware

A little web project to submit PaymentOrders via a web formula and manage them in a backend. Developed for the student council of the University of Jena.
PHP
3
star
18

GolonkaBBCodeParser

A copy of Golonka\BBCode after original repo got deleted (it's a dependency for Part-DB)
PHP
2
star
19

dompdf-font-loader-bundle

A symfony bundle to easily load custom fonts for dompdf (on cache warming)
PHP
2
star
20

Portal2HQMaterials

High Quality remake Materials for Portal 2
2
star
21

FRMorp

A tool for dumping firmware from SPMP8000 devices originally written by openschemes.com
C
1
star
22

WiFI-Sinus-Gen

The ESP8266 Code for my WLAN DDS Sinus Generator
Arduino
1
star
23

WiFi-Sinus-Generator-android

The Repository for the WiFi-Sinus-Generator Android App
Java
1
star