PHPackages                             andydefer/laravel-totp - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Authentication &amp; Authorization](/categories/authentication)
4. /
5. andydefer/laravel-totp

ActiveLibrary[Authentication &amp; Authorization](/categories/authentication)

andydefer/laravel-totp
======================

Laravel TOTP package for two-factor authentication with polymorphic support, recovery codes and QR Code generation.

v0.1.0(yesterday)00MITPHPPHP ^8.2

Since Jun 20Pushed yesterdayCompare

[ Source](https://github.com/andydefer/laravel-totp)[ Packagist](https://packagist.org/packages/andydefer/laravel-totp)[ RSS](/packages/andydefer-laravel-totp/feed)WikiDiscussions main Synced today

READMEChangelogDependencies (12)Versions (2)Used By (0)

Laravel TOTP
============

[](#laravel-totp)

> Package de double authentification TOTP (RFC 6238) pour Laravel avec support polymorphique

[![Latest Version](https://camo.githubusercontent.com/bde83a31aa29de813dd60adb81855961691e13d20e315d9e75c7a22d4a9d869a/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f616e647964656665722f6c61726176656c2d746f74702e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/andydefer/laravel-totp)[![Total Downloads](https://camo.githubusercontent.com/db23529f4821c984a37d86290b8ece92ead80e9b9e4b2849d4b1107746203089/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f64742f616e647964656665722f6c61726176656c2d746f74702e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/andydefer/laravel-totp)[![PHP Version](https://camo.githubusercontent.com/9dc2a2089df6443157df95d0bbb066e2999ca0999458f42a7778b88dbfe64aec/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f7068702d762f616e647964656665722f6c61726176656c2d746f74702e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/andydefer/laravel-totp)[![License](https://camo.githubusercontent.com/f16ea3000b3d55a6e34377402211c835ab434e5c1071c11ec05f8e33acba12f3/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f6c2f616e647964656665722f6c61726176656c2d746f74702e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/andydefer/laravel-totp)

Un package Laravel pour la double authentification (2FA) avec TOTP (RFC 6238), support polymorphique, codes de récupération et génération de QR Code.

---

📋 Table des matières
--------------------

[](#-table-des-matières)

- [Fonctionnalités](#fonctionnalit%C3%A9s)
- [Prérequis](#pr%C3%A9requis)
- [Installation](#installation)
- [Configuration](#configuration)
- [Structure du package](#structure-du-package)
- [Utilisation](#utilisation)
    - [Configurer TOTP](#configurer-totp)
    - [Activer TOTP après vérification](#activer-totp-apr%C3%A8s-v%C3%A9rification)
    - [Vérifier un code TOTP](#v%C3%A9rifier-un-code-totp)
    - [Vérifier un code de récupération](#v%C3%A9rifier-un-code-de-r%C3%A9cup%C3%A9ration)
    - [Désactiver TOTP](#d%C3%A9sactiver-totp)
    - [Régénérer les codes de récupération](#r%C3%A9g%C3%A9n%C3%A9rer-les-codes-de-r%C3%A9cup%C3%A9ration)
    - [Vérifier l'état de TOTP](#v%C3%A9rifier-l%C3%A9tat-de-totp)
- [Référence de l'API](#r%C3%A9f%C3%A9rence-de-lapi)
    - [TotpService](#totpservice)
    - [TotpGenerator](#totpgenerator)
    - [QrCodeGenerator](#qrcodegenerator)
- [Value Objects](#value-objects)
- [Structure de la base de données](#structure-de-la-base-de-donn%C3%A9es)
- [Workflow complet](#workflow-complet)
- [Tests](#tests)
- [Journal des modifications](#journal-des-modifications)
- [Contribuer](#contribuer)
- [Licence](#licence)

---

✨ Fonctionnalités
-----------------

[](#-fonctionnalités)

- ✅ **Polymorphisme** - Attachez TOTP à n'importe quel modèle (User, Admin, etc.) **sans que le modèle n'ait à déclarer de relation**
- ✅ **TOTP Standard** - RFC 6238 compatible (SHA1, 6 chiffres, période de 30s)
- ✅ **Génération de secrets** - Secrets Base32 (32 caractères)
- ✅ **Génération de QR Code** - Compatible Google Authenticator, Authy, Microsoft Authenticator
- ✅ **Codes de récupération** - 10 codes de secours de 8 caractères (hashés en SHA256)
- ✅ **Fenêtre de tolérance** - Tolérance de +/- 1 période (30s) pour les latences réseau
- ✅ **Activation/Désactivation** - Gestion complète du cycle de vie TOTP
- ✅ **Vérification** - Vérification des codes avec comparaison sécurisée (`hash_equals`)
- ✅ **Support des métadonnées** - Stockez des données supplémentaires au format JSON
- ✅ **Suppression douce** - SoftDeletes pour une suppression sécurisée
- ✅ **Tests complets** - Couverture complète des tests d'intégration (27 tests)

---

🚀 Prérequis
-----------

[](#-prérequis)

- PHP 8.2 ou supérieur
- Laravel 12.0, 13.0, 14.0 ou 15.0
- Extension GD (pour la génération de QR Code)

---

📦 Installation
--------------

[](#-installation)

Installez le package via Composer :

```
composer require andydefer/laravel-totp
```

### Publier les migrations

[](#publier-les-migrations)

```
php artisan vendor:publish --tag=Totp-migrations
```

### Exécuter les migrations

[](#exécuter-les-migrations)

```
php artisan migrate
```

---

⚙️ Configuration
----------------

[](#️-configuration)

Le package est automatiquement découvert par Laravel. Aucune configuration supplémentaire n'est requise.

Si vous devez personnaliser le Service Provider, ajoutez-le manuellement dans `config/app.php` :

```
'providers' => [
    // ...
    AndyDefer\LaravelTotp\TotpServiceProvider::class,
],
```

### Dépendances optionnelles

[](#dépendances-optionnelles)

Pour la génération de QR Code, le package utilise `endroid/qr-code`. Assurez-vous que l'extension GD est activée :

```
# Vérifier que GD est installé
php -m | grep gd

# Sur Ubuntu/Debian
sudo apt-get install php-gd

# Sur macOS avec Homebrew
brew install php-gd
```

---

🏗️ Structure du package
-----------------------

[](#️-structure-du-package)

```
laravel-totp/
├── src/
│   ├── TotpServiceProvider.php
│   ├── Models/
│   │   └── TotpSecret.php          # Seul modèle avec relations polymorphiques
│   ├── Services/
│   │   ├── TotpService.php         # Service principal
│   │   ├── TotpGenerator.php       # Génération TOTP
│   │   └── QrCodeGenerator.php     # Génération QR Code
│   ├── ValueObjects/
│   │   └── TotpSecretVO.php
│   └── Exceptions/
│       └── TotpException.php
├── database/
│   └── migrations/
│       └── create_totp_secrets_table.php
└── tests/
    ├── Integration/
    │   └── Services/
    │       └── TotpServiceIntegrationTest.php
    ├── Fixtures/
    └── IntegrationTestCase.php

```

### ⚠️ Important : Aucune relation dans les modèles consommateurs

[](#️-important--aucune-relation-dans-les-modèles-consommateurs)

Contrairement à d'autres packages, **le modèle consommateur (User, Admin, etc.) n'a pas besoin de déclarer de relation**. Seul le modèle `TotpSecret` contient les relations polymorphiques.

```
// ✅ Le modèle User reste propre - AUCUNE RELATION !
class User extends Model
{
    protected $fillable = ['name', 'email', 'password'];
    // AUCUNE méthode totpSecret() ici !
}
```

---

📖 Utilisation
-------------

[](#-utilisation)

### Configurer TOTP

[](#configurer-totp)

```
use AndyDefer\LaravelTotp\Services\TotpService;

class TwoFactorController extends Controller
{
    public function __construct(
        private readonly TotpService $totpService,
    ) {}

    public function setup(Request $request)
    {
        $user = $request->user();

        $setup = $this->totpService->setup($user);

        return response()->json([
            'secret' => $setup['secret'],
            'qr_code' => base64_encode($setup['qr_code']),
            'qr_code_uri' => $setup['qr_code_uri'],
            'recovery_codes' => $setup['recovery_codes'],
        ]);
    }
}
```

**Réponse :**

```
{
    "secret": "JBSWY3DPEHPK3PXP",
    "qr_code": "iVBORw0KGgoAAAANSUhEUgAA...",
    "qr_code_uri": "otpauth://totp/Laravel:john@example.com?secret=JBSWY3DPEHPK3PXP&issuer=Laravel&algorithm=SHA1&digits=6&period=30",
    "recovery_codes": [
        "X7K9M2P4", "R3T8W6Q1", "F5N2V9L3",
        "C1H7A4E8", "Y6B0U3S9", "D2J5R8M1",
        "W4P7K0T6", "G9E3F1V8", "L2N5Q7C4", "S8U1B6X9"
    ]
}
```

### Activer TOTP après vérification

[](#activer-totp-après-vérification)

```
public function enable(Request $request)
{
    $user = $request->user();

    $request->validate([
        'code' => 'required|string|size:6',
    ]);

    try {
        $enabled = $this->totpService->verifyAndEnable(
            authenticatable: $user,
            code: $request->code,
            window: 1, // Tolérance de +/- 1 période (30s)
        );

        if ($enabled) {
            return response()->json([
                'message' => 'TOTP enabled successfully',
                'verified_at' => now()->toDateTimeString(),
            ]);
        }

        return response()->json([
            'message' => 'Invalid TOTP code',
        ], 400);
    } catch (TotpException $e) {
        return response()->json([
            'message' => $e->getMessage(),
        ], 400);
    }
}
```

### Vérifier un code TOTP

[](#vérifier-un-code-totp)

```
public function verifyLogin(Request $request)
{
    $user = User::where('email', $request->email)->firstOrFail();

    $request->validate([
        'code' => 'required|string|size:6',
    ]);

    try {
        $valid = $this->totpService->verify(
            authenticatable: $user,
            code: $request->code,
            window: 1,
        );

        if ($valid) {
            auth()->login($user);

            return response()->json([
                'message' => 'Login successful',
            ]);
        }

        return response()->json([
            'message' => 'Invalid TOTP code',
        ], 400);
    } catch (TotpException $e) {
        return response()->json([
            'message' => $e->getMessage(),
        ], 400);
    }
}
```

### Vérifier un code de récupération

[](#vérifier-un-code-de-récupération)

```
public function recover(Request $request)
{
    $user = User::where('email', $request->email)->firstOrFail();

    $request->validate([
        'recovery_code' => 'required|string',
    ]);

    try {
        $valid = $this->totpService->verifyRecoveryCode(
            authenticatable: $user,
            code: $request->recovery_code,
        );

        if ($valid) {
            auth()->login($user);

            return response()->json([
                'message' => 'Recovery successful',
            ]);
        }

        return response()->json([
            'message' => 'Invalid recovery code',
        ], 400);
    } catch (TotpException $e) {
        return response()->json([
            'message' => $e->getMessage(),
        ], 400);
    }
}
```

### Désactiver TOTP

[](#désactiver-totp)

```
public function disable(Request $request)
{
    $user = $request->user();

    try {
        $this->totpService->disable($user);

        return response()->json([
            'message' => 'TOTP disabled successfully',
        ]);
    } catch (TotpException $e) {
        return response()->json([
            'message' => $e->getMessage(),
        ], 400);
    }
}
```

### Régénérer les codes de récupération

[](#régénérer-les-codes-de-récupération)

```
public function regenerateRecoveryCodes(Request $request)
{
    $user = $request->user();

    try {
        $recoveryCodes = $this->totpService->regenerateRecoveryCodes($user);

        return response()->json([
            'recovery_codes' => $recoveryCodes->toArray(),
        ]);
    } catch (TotpException $e) {
        return response()->json([
            'message' => $e->getMessage(),
        ], 400);
    }
}
```

### Vérifier l'état de TOTP

[](#vérifier-létat-de-totp)

```
public function status(Request $request)
{
    $user = $request->user();

    return response()->json([
        'enabled' => $this->totpService->isEnabled($user),
        'verified' => $this->totpService->isVerified($user),
        'remaining_recovery_codes' => count(
            $this->totpService->getRemainingRecoveryCodes($user)
        ),
    ]);
}
```

---

📚 Référence de l'API
--------------------

[](#-référence-de-lapi)

### TotpService

[](#totpservice)

MéthodeDescriptionRetourneException`setup(Model $authenticatable)`Configurer TOTP (génère secret, QR Code, codes récupération)`array{secret, qr_code, qr_code_uri, recovery_codes}`-`verifyAndEnable(Model $authenticatable, string $code, int $window = 1)`Vérifier le code et activer TOTP`bool``TotpException``enable(Model $authenticatable, string $secret, array $recoveryCodes)`Activer TOTP avec un secret existant`TotpSecret`-`disable(Model $authenticatable)`Désactiver TOTP`bool``TotpException``verify(Model $authenticatable, string $code, int $window = 1)`Vérifier un code TOTP`bool``TotpException``verifyRecoveryCode(Model $authenticatable, string $code)`Vérifier un code de récupération`bool``TotpException``markAsVerified(Model $authenticatable)`Marquer TOTP comme vérifié`void`-`isEnabled(Model $authenticatable)`Vérifier si TOTP est activé`bool`-`isVerified(Model $authenticatable)`Vérifier si TOTP est vérifié`bool`-`getRemainingRecoveryCodes(Model $authenticatable)`Récupérer les codes de récupération restants`StringTypedCollection`-`regenerateRecoveryCodes(Model $authenticatable)`Régénérer les codes de récupération`StringTypedCollection`-`getSecret(Model $authenticatable)`Récupérer le secret`?string`-### TotpGenerator

[](#totpgenerator)

MéthodeDescriptionRetourne`generateSecret(int $length = 32)`Générer un secret Base32`string``generateCode(string $secret, ?int $timestamp = null, int $digits = 6, int $period = 30)`Générer un code TOTP`string``generateCodesWithWindow(string $secret, int $digits = 6, int $period = 30, int $window = 1, ?int $timestamp = null)`Générer les codes avec fenêtre de tolérance`array``generateRecoveryCodes(int $count = 10, int $length = 8)`Générer des codes de récupération`array``hashRecoveryCodes(array $codes)`Hasher les codes de récupération (SHA256)`array``verifyRecoveryCode(string $code, array $hashedCodes)`Vérifier un code de récupération`bool`### QrCodeGenerator

[](#qrcodegenerator)

MéthodeDescriptionRetourne`generate(string $account, string $secret, string $issuer, int $digits = 6, int $period = 30, string $algorithm = 'SHA1')`Générer un QR Code PNG`string``generateFromUri(string $uri)`Générer un QR Code à partir d'une URI`string``generateDataUri(string $uri)`Générer une Data URI du QR Code`string``buildUri(string $account, string $secret, string $issuer, int $digits = 6, int $period = 30, string $algorithm = 'SHA1')`Construire l'URI TOTP`string`---

🎯 Value Objects
---------------

[](#-value-objects)

Le package supporte les Value Objects suivants :

Value ObjectDescriptionExemple`TotpSecretVO`Secret TOTP avec métadonnées`new TotpSecretVO('JBSWY3...', true, [...], $verifiedAt)``DateTimeVO`Date/heure`new DateTimeVO('2024-01-01 12:00:00')``StrictDataObject`Métadonnées typées`StrictDataObject::from(['ip' => '127.0.0.1'])`### Accesseurs dans le modèle TotpSecret

[](#accesseurs-dans-le-modèle-totpsecret)

```
$totpSecret = TotpSecret::find(1);

// Accès sous forme de Value Objects
$secretVO = $totpSecret->getSecret();         // TotpSecretVO
$recoveryCodes = $totpSecret->getRecoveryCodes(); // array
$verifiedAt = $totpSecret->getVerifiedAt();   // DateTimeVO
$createdAt = $totpSecret->getCreatedAt();     // DateTimeVO
$updatedAt = $totpSecret->getUpdatedAt();     // DateTimeVO
$metadata = $totpSecret->getMetadata();       // StrictDataObject

// Méthodes utilitaires
$totpSecret->isEnabled();     // bool
$totpSecret->isVerified();    // bool
$totpSecret->countRecoveryCodes();  // int
$totpSecret->hasRecoveryCodes();    // bool

// Relation polymorphique
$authenticatable = $totpSecret->authenticatable;  // User, Admin, etc.
```

---

📝 Structure de la base de données
---------------------------------

[](#-structure-de-la-base-de-données)

```
CREATE TABLE totp_secrets (
    id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
    authenticatable_type VARCHAR(255) NOT NULL,  -- Type de l'identifiant (morph)
    authenticatable_id BIGINT UNSIGNED NOT NULL, -- ID de l'identifiant
    secret VARCHAR(255) NOT NULL UNIQUE,         -- Secret Base32
    is_enabled BOOLEAN DEFAULT FALSE,            -- TOTP activé ?
    recovery_codes JSON NULL,                    -- Codes de récupération hashés
    verified_at TIMESTAMP NULL,                  -- Date de première vérification
    metadata JSON NULL,                          -- Métadonnées
    created_at TIMESTAMP NULL,
    updated_at TIMESTAMP NULL,
    deleted_at TIMESTAMP NULL,

    INDEX idx_authenticatable (authenticatable_type, authenticatable_id),
    INDEX idx_secret (secret),
    INDEX idx_is_enabled (is_enabled)
);
```

### Structure du champ `recovery_codes` (JSON)

[](#structure-du-champ-recovery_codes-json)

```
[
    "5e884898da28047151d0e56f8dc6292773603d0d6aabbdd62a11ef721d1542d8",
    "b109f3bbbc244eb82441917ed06d618b9008dd09b3befd1b5e07394c706a8bb1",
    "..."
]
```

Les codes de récupération sont hashés en SHA256 avant stockage.

---

🔧 Exceptions possibles
----------------------

[](#-exceptions-possibles)

ExceptionMessageQuand ?`TotpException::totpNotEnabled()`TOTP is not enabled for this user.`verify()` appelé mais TOTP pas activé`TotpException::secretNotFound()`TOTP secret not found.`verify()` appelé mais secret inexistant`TotpException::invalidCode()`Invalid TOTP code.Code invalide`TotpException::invalidRecoveryCode()`Invalid recovery code.Code de récupération invalide`TotpException::alreadyEnabled()`TOTP is already enabled for this user.Activation déjà faite`TotpException::alreadyDisabled()`TOTP is already disabled for this user.Désactivation déjà faite`TotpException::maxAttemptsExceeded()`Maximum TOTP attempts exceeded.Trop de tentatives`TotpException::setupFailed()`TOTP setup failed.Échec de la configuration---

👨‍💻 Auteur
----------

[](#‍-auteur)

**Andy Kani**

- GitHub: [@andydefer](https://github.com/andydefer)
- Email:

---

⭐ Support
---------

[](#-support)

Si vous trouvez ce package utile, n'hésitez pas à lui donner une ⭐ sur GitHub !

---

🙏 Remerciements
---------------

[](#-remerciements)

- Framework Laravel
- Tous les contributeurs et utilisateurs de ce package

---

**Construit avec ❤️ pour la communauté Laravel**
------------------------------------------------

[](#construit-avec-️-pour-la-communauté-laravel)

###  Health Score

36

—

LowBetter than 80% of packages

Maintenance100

Actively maintained with recent releases

Popularity0

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity35

Early-stage or recently created project

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Unknown

Total

1

Last Release

1d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/2170ec3fbad9eb4b002661ab4f58b1cc374eae4293b92904c6a74bc2818bd570?d=identicon)[andydefer](/maintainers/andydefer)

---

Top Contributors

[![andydefer](https://avatars.githubusercontent.com/u/124321745?v=4)](https://github.com/andydefer "andydefer (1 commits)")

###  Code Quality

TestsPHPUnit

Static AnalysisPHPStan, Psalm, Rector

Code StyleLaravel Pint

Type Coverage Yes

### Embed Badge

![Health badge](/badges/andydefer-laravel-totp/health.svg)

```
[![Health](https://phpackages.com/badges/andydefer-laravel-totp/health.svg)](https://phpackages.com/packages/andydefer-laravel-totp)
```

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
