skywalker-labs/passwordless

Seamless Passwordless Authentication for Laravel. Integrate OTP (One-Time Password) Login and 2FA into your default authentication flow with zero-conf middleware and ready-to-use UI.

Maintainers

Package info

github.com/skywalker-labs/passwordless

pkg:composer/skywalker-labs/passwordless

Fund package maintenance!

ermradulsharma

Statistics

Installs: 0

Dependents: 0

Suggesters: 0

Stars: 5

Open Issues: 0

v2.1.0 2026-03-10 12:51 UTC

This package is auto-updated.

Last update: 2026-03-10 13:08:25 UTC


README

Version Total Downloads PHPStan Level 9 PHP Version License Security Policy

Elegant passwordless authentication for Laravel. Drop-in OTP login, 2FA enforcement, magic links, and backup codes โ€” all built on Skywalker Toolkit with action-oriented architecture, contract-based design, and Extreme Strictness (PHPStan Level 9 + Strict Rules) compliance.

โœจ Features

Feature Detail
OTP Login Generate & verify time-limited one-time passwords
Hashed Storage OTPs and backup codes stored with Hash::make() โ€” never plain-text
Magic Login Links Signed, temporary URLs for one-click authentication
Backup Codes Hashed emergency recovery codes
Multi-Channel Email, SMS (Twilio), Slack, and Log channels
Event-Driven OtpGenerated, OtpVerified, OtpFailed events for full extensibility
Middleware Gate otp.verified middleware with infinite-loop protection
Rate Limiting Built-in per-identifier request throttling on all routes
Action Architecture Each operation is a dedicated Action class (SRP)
Extreme Strictness 100% compliance with PHPStan Level 9 + Strict & Deprecation rules
Zero-Trust Auth Risk-based trust scoring (TrustEngine) integrated into the core flow
Strict Types declare(strict_types=1) and explicit type comparisons everywhere

๐Ÿ“ฆ Installation

composer require skywalker-labs/passwordless

Requires: PHP โ‰ฅ 8.2, Laravel โ‰ฅ 11.0

๐Ÿ› ๏ธ Setup

1. Add the HasOtp Trait to Your User Model

use Skywalker\Otp\Concerns\HasOtp;

class User extends Authenticatable
{
    use HasOtp;
}

The trait provides sendOtp(): string and verifyOtp(string $token): bool methods.

2. Publish Config & Migrations

php artisan vendor:publish --tag=passwordless-config
php artisan vendor:publish --tag=passwordless-migrations
php artisan migrate

3. Configure (config/passwordless.php)

return [
    'length'     => 6,            // OTP digit length
    'expiry'     => 10,           // Minutes until OTP expires
    'driver'     => 'cache',      // 'cache' or 'database'
    'channel'    => 'mail',       // 'mail' | 'log' | 'sms' | 'slack'
    'middleware' => ['web', 'throttle:6,1'],

    'services'   => [
        'twilio' => [
            'sid'   => env('TWILIO_SID'),
            'token' => env('TWILIO_AUTH_TOKEN'),
            'from'  => env('TWILIO_FROM'),
        ],
        'slack'  => [
            'webhook_url' => env('SLACK_WEBHOOK_URL'),
        ],
    ],
];

๐ŸŽฏ Usage

Using the Facade

use Skywalker\Otp\Facades\Otp;

// Send an OTP to an email or phone
$otp = Otp::generate('user@example.com');

// Verify the submitted OTP
try {
    Otp::verify('user@example.com', $request->otp);
} catch (\Skywalker\Otp\Exceptions\InvalidOtpException $e) {
    // Invalid or expired OTP
}

Dependency Injection (Recommended)

Inject the contract for testable, SOLID-compliant code:

use Skywalker\Otp\Domain\Contracts\OtpService;

public function __construct(private readonly OtpService $otp) {}

public function send(string $identifier): void
{
    $this->otp->generate($identifier);
}

Magic Login Links

// Generate a signed link valid for 15 minutes (configurable via 'expiry')
$link = Otp::generateMagicLink('user@example.com');
// โ†’ https://your-app.com/magic-login?identifier=...&signature=...

// Route: GET /magic-login  โ†’ passwordless.magic-login
// Validated automatically by hasValidSignature() in the controller

Backup Codes

// Generate 8 hashed recovery codes (stored securely in DB)
$codes = Otp::generateBackupCodes('user@example.com');
// Returns: ['AbcD123fGh', 'xYz987Qrst', ...]  โ† shown once, stored hashed

// Verify & consume a backup code (uses Hash::check internally)
$ok = Otp::verifyBackupCode('user@example.com', $submittedCode);

Custom OTP Generator

use Skywalker\Otp\Facades\Otp;

// Use a custom generator at runtime (e.g. alphanumeric, UUID-style)
Otp::useGenerator(fn() => strtoupper(substr(md5(microtime()), 0, 6)));

Listen to Events

// In your EventServiceProvider or a Listener
use Skywalker\Otp\Events\OtpVerified;
use Skywalker\Otp\Events\OtpGenerated;
use Skywalker\Otp\Events\OtpFailed;

protected $listen = [
    OtpVerified::class  => [LogSuccessfulLogin::class],
    OtpGenerated::class => [NotifySecurityTeam::class],
    OtpFailed::class    => [AlertOnRepeatedFailures::class],
];

Middleware Gate

Add the otp.verified middleware to any route to enforce OTP verification before access:

Route::middleware(['auth', 'otp.verified'])->group(function () {
    Route::get('/dashboard', DashboardController::class);
});

The middleware automatically:

  • Skips users without the HasOtp trait
  • Allows access once otp_verified is set in the session
  • Excludes OTP verify routes to prevent redirect loops

๐Ÿ—๏ธ Architecture

The package follows a strict Action-Oriented + Domain-Driven architecture:

src/
โ”œโ”€โ”€ Actions/                    โ† One class per operation (SRP)
โ”‚   โ”œโ”€โ”€ GenerateOtp             โ† Hash::make() + event dispatch
โ”‚   โ”œโ”€โ”€ VerifyOtp               โ† Hash::check() + timing-safe
โ”‚   โ”œโ”€โ”€ GenerateBackupCodes     โ† Hash::make() per code
โ”‚   โ”œโ”€โ”€ VerifyBackupCode        โ† Hash::check() against stored hashes
โ”‚   โ””โ”€โ”€ GenerateMagicLink       โ† Signed URL generation
โ”œโ”€โ”€ Concerns/
โ”‚   โ””โ”€โ”€ HasOtp                  โ† User model trait (sendOtp / verifyOtp)
โ”œโ”€โ”€ Domain/
โ”‚   โ”œโ”€โ”€ Contracts/              โ† OtpStore, OtpSender, OtpService interfaces
โ”‚   โ””โ”€โ”€ ValueObjects/
โ”‚       โ””โ”€โ”€ OtpToken            โ† Immutable (readonly) value object
โ”œโ”€โ”€ Events/
โ”‚   โ”œโ”€โ”€ OtpGenerated, OtpFailed, OtpVerified
โ”œโ”€โ”€ Exceptions/
โ”‚   โ”œโ”€โ”€ OtpException            โ† Extends PackageException (toolkit)
โ”‚   โ”œโ”€โ”€ InvalidOtpException
โ”‚   โ””โ”€โ”€ OtpDeliveryFailedException
โ”œโ”€โ”€ Facades/
โ”‚   โ””โ”€โ”€ Otp                     โ† Static access via Laravel facade
โ”œโ”€โ”€ Http/
โ”‚   โ”œโ”€โ”€ Controllers/OtpAuthController  โ† Injects OtpService contract
โ”‚   โ””โ”€โ”€ Middleware/EnsureOtpVerified
โ”œโ”€โ”€ Infrastructure/
โ”‚   โ”œโ”€โ”€ Delivery/NotificationSender    โ† Multi-channel sender
โ”‚   โ””โ”€โ”€ Persistence/
โ”‚       โ”œโ”€โ”€ CacheOtpStore              โ† driver=cache
โ”‚       โ””โ”€โ”€ DatabaseOtpStore           โ† driver=database
โ”œโ”€โ”€ Services/
โ”‚   โ””โ”€โ”€ OtpService              โ† Orchestrator, delegates to Actions
โ””โ”€โ”€ OtpServiceProvider.php      โ† Bindings, routes, events, middleware

Toolkit foundation:

Our Class Extends
All 5 Action classes Skywalker\Support\Foundation\Action
OtpToken Skywalker\Support\Foundation\ValueObject
OtpException Skywalker\Support\Exceptions\PackageException
OtpService Skywalker\Support\Foundation\Service
OtpServiceProvider Skywalker\Support\Providers\PackageServiceProvider

๐Ÿงช Testing & Analysis

# Run tests
composer test

# Static analysis (PHPStan Level 9)
composer analyse

# Mutation testing (Infection)
vendor/bin/infection

# Format code (Laravel Pint / PSR-12)
composer format

๐Ÿ”’ Security & Quality

  • Extreme Strictness โ€” 100% compliance with PHPStan Level 9 + Strict & Deprecation rules
  • Zero-Trust Security โ€” Integrated TrustEngine for risk-based analysis
  • Hashed OTPs โ€” stored with Hash::make(), verified with Hash::check()
  • Hashed Backup Codes โ€” same approach as OTPs
  • Signed Magic Links โ€” protection against link tampering
  • Rate Limiting โ€” strict per-identifier request throttling
  • Strict Logic โ€” no implicit type coercion, only explicit boolean comparisons

๐Ÿ”’ Zero-Trust Security

This package integrates the Skywalker Trust Engine to provide risk-based authentication:

  1. Trust Scoring: Every authentication attempt calculates a "Trust Score" (0.0 to 1.0) based on IP, behavior, and environment.
  2. Adaptive 2FA: If a user has a very high trust score (e.g. > 0.8), the otp.verified middleware can be configured to bypass the OTP check (bypass_high_trust).
  3. Hijack Protection: If a session's trust score drops significantly mid-session, the user is automatically prompted for re-verification.
  4. Session Rotation: Sessions are regenerated upon successful OTP or Magic Link verification to prevent fixation attacks.

๐Ÿ›ฃ๏ธ Available Routes

Method URI Name Auth
POST /otp/send otp.send Public
POST /otp/verify otp.verify Public
GET /otp/verify otp.verify.view auth
POST /otp/verify-submit otp.verify.submit auth
POST /otp/resend otp.resend auth
GET /magic-login passwordless.magic-login Signed URL

๐Ÿ“„ License

The MIT License (MIT). Please see License File.