OTPz for Laravel

repository·main·Indexed 43 days ago

https://github.com/benbjurstrom/otpz

A Laravel package providing secure, first-factor one-time password (OTP) authentication via email. It eliminates traditional passwords by allowing users to sign in using only their email address. Includes built-in support for React (Inertia.js), Vue (Inertia.js), and Livewire (Volt) stacks, as well as integration options for Laravel Fortify.

Tokens
5.1K
Snippets
17
Records
19
Agent score
55%

What's inside OTPz

  1. Install OTPz for Laravel

    main

    To install OTPz, use Composer to require the package, publish the migrations, and run them. You must then update your User model to implement the Otpable interface and use the HasOtps trait.

    composer require benbjurstrom/otpz
    
    php artisan vendor:publish --tag="otpz-migrations"
    php artisan migrate
    // app/Models/User.php
    namespace App\Models;
    
    use BenBjurstrom\Otpz\Models\Concerns\HasOtps;
    use BenBjurstrom\Otpz\Models\Concerns\Otpable;
    // ...
    
    class User extends Authenticatable implements Otpable
    {
        use HasFactory, Notifiable, HasOtps;
    
        // ...
    }
  2. Set up OTPz with Livewire (Volt)

    main

    To use OTPz with Livewire/Volt, publish the Livewire components and add the routes using Volt::route.

    Note: These components use Flux UI components and layout components from the Laravel Livewire starter kit. Adjust markup and styling if not using the starter kit.

    php artisan vendor:publish --tag="otpz-livewire"
    // routes/web.php
    use App\Http\Controllers\Auth\PostOtpController;
    use Livewire\Volt\Volt;
    
    Route::middleware('guest')->group(function () {
        Volt::route('otpz', 'auth.otpz-login')
            ->name('otpz.index');
    
        Volt::route('otpz/{id}', 'auth.otpz-verify')
            ->middleware('signed')
            ->name('otpz.show');
    
        Route::post('otpz/{id}', PostOtpController::class)
            ->middleware('signed')
            ->name('otpz.verify');
    });
  3. Customize OTPz email templates

    main

    To customize the styling of OTP emails, publish the package's views to your application's resources directory using the otpz-views tag.

    Once published, you can modify the files in resources/views/vendor/otpz/. You can switch between a custom styled template and Laravel's default notification styling by updating the template key in config/otpz.php.

    php artisan vendor:publish --tag="otpz-views"
    // In config/otpz.php
    'template' => 'otpz::mail.notification', // Use Laravel's default styling
  4. Implement custom user resolution

    main

    By default, OTPz automatically creates new users if the provided email address does not exist in the database. To change this behavior (for example, to prevent registration and only allow existing users to log in), implement a custom user resolver class.

    Your resolver must implement a handle(string $email) method that returns an instance of BenBjurstrom\Otpz\Models\Concerns\Otpable.

    After creating your resolver, register its fully qualified class name in config/otpz.php under the user_resolver key.

    namespace App\
    Actions;
    
    use App\
    Models\
    User;
    use BenBjurstrom\
    Otpz\
    Models\
    Concerns\
    Otpable;
    use Illuminate\
    Validation\
    ValidationException;
    
    class MyUserResolver
    {
        public function handle(string $email): Otpable
        {
            $user = User::where('email', $email)->first();
    
            if($user){
                return $user;
            }
    
            throw ValidationException::withMessages([
                'email' => 'No user found with that email address.',
            ]);
        }
    }
    // In config/otpz.php
    'user_resolver' => App\
    Actions\
    MyUserResolver::class,
  5. Replace Fortify Login with OTPz

    main

    If you are using Laravel Fortify, you can replace the standard username/password login with the OTPz email entry flow by updating the loginView in your FortifyServiceProvider.

    // For React
    Fortify::loginView(fn (Request $request) => Inertia::render('auth/otpz-login', []));
    
    // For Vue
    Fortify::loginView(fn (Request $request) => Inertia::render('auth/OtpzLogin', []));
    
    // For Livewire
    // Fortify::loginView(fn () => view('livewire.auth.login'));
    // Then in routes/web.php:
    // Volt::route('login', 'auth.otpz-login')->name('login');
  6. Set up OTPz with React (Inertia.js)

    main

    To use OTPz with a React/Inertia.js stack, publish the React components and add the necessary routes to routes/web.php.

    Note: These components expect shadcn/ui components (Button, Input, Label, Checkbox), an AuthLayout, and wayfinder for route generation. If you are not using the official Laravel React starter kit, you will need to adjust these imports.

    php artisan vendor:publish --tag="otpz-react"
    // routes/web.php
    use App\Http\Controllers\Auth\OtpzController;
    
    Route::middleware('guest')->group(function () {
        Route::get('otpz', [OtpzController::class, 'index'])
            ->name('otpz.index');
        Route::post('otpz', [OtpzController::class, 'store'])
            ->name('otpz.store');
        Route::get('otpz/{id}', [OtpzController::class, 'show'])
            ->name('otpz.show')
            ->middleware('signed');
        Route::post('otpz/{id}', [OtpzController::class, 'verify'])
            ->name('otpz.verify')
            ->middleware('signed');
    });
  7. Set up OTPz with Vue (Inertia.js)

    main

    To use OTPz with a Vue/Inertia.js stack, publish the Vue components and add the necessary routes to routes/web.php.

    Note: These components expect an AuthLayout and wayfinder for route generation. If you are not using the official Laravel Vue starter kit, you will need to adjust these imports.

    php artisan vendor:publish --tag="otpz-vue"
    // routes/web.php
    use App\Http\Controllers\Auth\OtpzController;
    
    Route::middleware('guest')->group(function () {
        Route::get('otpz', [OtpzController::class, 'index'])
            ->name('otpz.index');
        Route::post('otpz', [OtpzController::class, 'store'])
            ->name('otpz.store');
        Route::get('otpz/{id}', [OtpzController::class, 'show'])
            ->name('otpz.show')
            ->middleware('signed');
        Route::post('otpz/{id}', [OtpzController::class, 'verify'])
            ->name('otpz.verify')
            ->middleware('signed');
    });
  8. Configure OTPz settings

    main

    You can customize the behavior of OTPz by publishing the configuration file. Key settings include:

    • expiration: Time in minutes before an OTP expires (default: 5).
    • limits: Multi-tier rate limiting array (e.g., ['limit' => 1, 'minutes' => 1]).
    • models.authenticatable: The model class used for authentication.
    • mailable: The Mailable class used to send the OTP.
    • template: The email template used.
    • user_resolver: The class responsible for finding or creating users by email.
    php artisan vendor:publish --tag="otpz-config"
  9. Publish OTPZ components for React, Vue, or Livewire

    main

    The otpz package provides pre-built UI components and controllers for different frontend stacks. You can publish these stubs into your application's resource and controller directories using the --tag flag with the vendor:publish command.

    Depending on your frontend framework, use one of the following tags:

    • React (Inertia.js): Use the otpz-react tag to publish React components and the OtpzController.
    • Vue (Inertia.js): Use the otpz-vue tag to publish Vue components and the OtpzController.
    • Livewire (Volt): Use the otpz-livewire tag to publish Livewire Volt components and the PostOtpController.
    # For React (Inertia.js)
    php artisan vendor:publish --tag=otpz-react
    
    # For Vue (Inertia.js)
    php artisan vendor:publish --tag=otpz-vue
    
    # For Livewire (Volt)
    php artisan vendor:publish --tag=otpz-livewire
  10. Customize the OTP email template

    main

    The OtpzMail class uses a configurable markdown template to render the OTP email. By default, it uses the otpz::mail.otpz view. You can override this by setting the template key in your otpz configuration file.

    When the email is rendered, the following variables are available to your template:

    • email: The email address of the user associated with the OTP.
    • code: The OTP code, automatically formatted with a hyphen after the 5th character (e.g., 123456 becomes 12345-6) for improved readability.
    // config/otpz.php
    return [
        'template' => 'your-custom-namespace::mail.custom-otp',
    ];
  11. Configure the authenticatable model for OTPZ

    main

    The package requires you to specify which Eloquent model acts as the authenticatable entity for One-Time Passwords. This is configured via the otpz.models.authenticatable key in your Laravel configuration files.

    To be valid, the class specified must:

    1. Be a string representing a class name.
    2. Implement the BenBjurstrom\Otpz\Models\Concerns\Otpable interface.
    3. Extend Illuminate\Database\Eloquent\Model.

    If the configured model does not meet these requirements, the package will throw an InvalidAuthenticatableModel exception.

    // Example configuration in config/otpz.php
    return [
        'models' => [
            'authenticatable' => App\Models\User::class,
        ],
    ];