PHPackages                             torqie/laravel-passwordless - 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. torqie/laravel-passwordless

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

torqie/laravel-passwordless
===========================

Passwordless authentication for Laravel with magic links and login codes

v1.2.0(3mo ago)062[3 PRs](https://github.com/torqie/laravel-passwordless/pulls)MITPHPPHP ^8.4CI passing

Since Mar 23Pushed 2mo agoCompare

[ Source](https://github.com/torqie/laravel-passwordless)[ Packagist](https://packagist.org/packages/torqie/laravel-passwordless)[ Docs](https://github.com/torqie/laravel-passwordless)[ GitHub Sponsors](https://github.com/torqie)[ RSS](/packages/torqie-laravel-passwordless/feed)WikiDiscussions main Synced 3w ago

READMEChangelog (3)Dependencies (26)Versions (8)Used By (0)

laravel-passwordless
====================

[](#laravel-passwordless)

[![Latest Version on Packagist](https://camo.githubusercontent.com/bb72a92a580bbf346897f0c3dfdcb7c641957c3bccc3906d27692f82860a1241/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f746f727169652f6c61726176656c2d70617373776f72646c6573732e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/torqie/laravel-passwordless)[![GitHub Tests Action Status](https://camo.githubusercontent.com/2897b4b95f1f1e4c3c9f3974de0ae24f95bf997f674963773ff0cff87e3c1555/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f616374696f6e732f776f726b666c6f772f7374617475732f746f727169652f6c61726176656c2d70617373776f72646c6573732f72756e2d74657374732e796d6c3f6272616e63683d6d61696e266c6162656c3d7465737473267374796c653d666c61742d737175617265)](https://github.com/torqie/laravel-passwordless/actions?query=workflow%3Arun-tests+branch%3Amain)[![GitHub Code Style Action Status](https://camo.githubusercontent.com/64b65b6b3835d163bb818511ff895549e5950204d28453cbc778fe190dda0791/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f616374696f6e732f776f726b666c6f772f7374617475732f746f727169652f6c61726176656c2d70617373776f72646c6573732f6669782d7068702d636f64652d7374796c652d6973737565732e796d6c3f6272616e63683d6d61696e266c6162656c3d636f64652532307374796c65267374796c653d666c61742d737175617265)](https://github.com/torqie/laravel-passwordless/actions?query=workflow%3A%22Fix+PHP+code+style+issues%22+branch%3Amain)[![Total Downloads](https://camo.githubusercontent.com/5ed3736ca6ff8b7f2368689835a7faa461a4a0f7774334c42f1321e3b53778d2/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f64742f746f727169652f6c61726176656c2d70617373776f72646c6573732e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/torqie/laravel-passwordless)

Passwordless authentication for Laravel via **magic links** and **login codes** (OTP). No passwords, no complexity — just click a link or type a code.

- 🔗 **Magic links** — a signed, time-limited URL sent by email; click to log in instantly
- 🔢 **Login codes** — a short numeric/alphanumeric code sent by email; enter it on a verify form
- 🔄 Use one flow, the other, or both at the same time
- 🎛 Fully customisable — swap views, swap action classes, configure everything via `config/passwordless.php`

---

Requirements
------------

[](#requirements)

DependencyVersionPHP8.4+Laravel11 or 12---

Installation
------------

[](#installation)

Install via Composer:

```
composer require torqie/laravel-passwordless
```

Then run the interactive install wizard:

```
php artisan passwordless:install
```

The wizard walks you through every setup step and writes to your `.env` automatically:

```
── Step 1: Configuration
   Publish config file? (config/passwordless.php) [yes]

── Step 2: Migrations
   Publish migrations? [yes]
   Run migrations now? [yes]

── Step 3: Authentication Flows
   Which flow(s)? Both / Magic links only / Login codes only

── Step 4: Frontend Framework
   Detected: vue (from package.json). Use it? [yes]
   (or choose: Blade / Vue / React / Svelte)

── Step 5: View Setup
   Blade  → optionally publish views for customisation
   Inertia → publish component stubs + writes PASSWORDLESS_INERTIA=true

── Step 6: Session Options
   Enable remember me? [no]

```

Use `--force` to overwrite any already-published files when re-running:

```
php artisan passwordless:install --force
```

### Manual installation (optional)

[](#manual-installation-optional)

If you prefer to run each step yourself:

```
# Publish config
php artisan vendor:publish --tag="passwordless-config"

# Publish and run migrations
php artisan vendor:publish --tag="passwordless-migrations"
php artisan migrate

# Publish Blade views (optional)
php artisan vendor:publish --tag="laravel-passwordless-views"

# Scaffold Inertia components for a specific framework (optional)
php artisan passwordless:install-inertia --framework=vue
```

---

Setup
-----

[](#setup)

### 1. Add the trait to your User model

[](#1-add-the-trait-to-your-user-model)

```
use Torqie\LaravelPasswordless\Traits\HasPasswordlessAuth;

class User extends Authenticatable
{
    use HasPasswordlessAuth;
    // ...
}
```

### 2. Configure your user model (if not `App\Models\User`)

[](#2-configure-your-user-model-if-not-appmodelsuser)

```
// config/passwordless.php
'user_model' => App\Models\User::class,
```

Or via `.env`:

```
PASSWORDLESS_USER_MODEL=App\Models\User
```

### 3. Routes are registered automatically

[](#3-routes-are-registered-automatically)

The package registers all routes under the configured prefix (default: `/auth`). No additional route registration is needed.

---

Usage
-----

[](#usage)

### Using the built-in routes

[](#using-the-built-in-routes)

The package ships with ready-made routes and controllers for both flows. After installation you get these routes out of the box:

MethodURINameDescription`GET``/auth/magic-link``passwordless.magic-link.request`Show email form`POST``/auth/magic-link``passwordless.magic-link.send`Send magic link email`GET``/auth/magic-link/{token}``passwordless.magic-link.authenticate`Authenticate via clicked link`GET``/auth/code``passwordless.login-code.request`Show email form`POST``/auth/code``passwordless.login-code.send`Send login code email`GET``/auth/code/verify``passwordless.login-code.verify`Show code entry form`POST``/auth/code/verify``passwordless.login-code.authenticate`Authenticate via submitted codeLink to either flow from your login page:

```
Sign in with a magic link
Sign in with a code
```

### Using the fluent API (programmatic)

[](#using-the-fluent-api-programmatic)

Use the `LaravelPasswordless` facade when you need to trigger a magic link or login code from your own code (e.g. inside a controller, job, or listener):

```
use Torqie\LaravelPasswordless\Facades\LaravelPasswordless;

// Send a magic link — returns the signed URL
$url = LaravelPasswordless::for($user)->sendMagicLink();

// Send a login code — returns the plain-text code
$code = LaravelPasswordless::for($user)->sendLoginCode();
```

`sendMagicLink()` and `sendLoginCode()` both:

1. Generate and persist a hashed token
2. Send the appropriate notification to the user
3. Fire the corresponding event (`MagicLinkSent` / `LoginCodeSent`)

### Using the trait helpers directly

[](#using-the-trait-helpers-directly)

The `HasPasswordlessAuth` trait exposes helpers on your User model:

```
// All tokens associated with the user
$user->passwordlessTokens;

// Only valid (non-expired, non-used) tokens
$user->validPasswordlessTokens;

// Invalidate all unused tokens (optional: scope to one type)
$user->invalidatePasswordlessTokens();
$user->invalidatePasswordlessTokens('magic_link');
$user->invalidatePasswordlessTokens('login_code');
```

---

Configuration
-------------

[](#configuration)

All options live in `config/passwordless.php`.

### `type`

[](#type)

Which flow(s) to make available. This is informational for your own UI — the package does not restrict routes by this setting.

```
'type' => 'both', // 'magic_link' | 'login_code' | 'both'
```

`.env` keyDefault`PASSWORDLESS_TYPE``both`---

### `ttl`

[](#ttl)

How many minutes a magic link or login code remains valid after being issued.

```
'ttl' => 15,
```

`.env` keyDefault`PASSWORDLESS_TTL``15`---

### `code`

[](#code)

Settings specific to the login code (OTP) flow.

```
'code' => [
    'length'  => 6,            // Number of characters in the code
    'charset' => '0123456789', // Characters to draw from
],
```

`.env` keyDefault`PASSWORDLESS_CODE_LENGTH``6``PASSWORDLESS_CODE_CHARSET``0123456789`You can make codes alphanumeric:

```
PASSWORDLESS_CODE_CHARSET=ABCDEFGHJKLMNPQRSTUVWXYZ23456789
PASSWORDLESS_CODE_LENGTH=8
```

---

### `guard`

[](#guard)

The authentication guard used when logging the user in.

```
'guard' => 'web',
```

`.env` keyDefault`PASSWORDLESS_GUARD``web`---

### `user_model`

[](#user_model)

The fully-qualified class name of the authenticatable model to look up by email.

```
'user_model' => \App\Models\User::class,
```

`.env` keyDefault`PASSWORDLESS_USER_MODEL``App\Models\User`---

### `redirects`

[](#redirects)

Where to send the user after a successful login or when a token is invalid/expired.

```
'redirects' => [
    'after_login'   => '/dashboard',
    'invalid_token' => '/login',
],
```

`.env` keyDefault`PASSWORDLESS_REDIRECT_AFTER_LOGIN``/dashboard``PASSWORDLESS_REDIRECT_INVALID``/login`---

### `routes`

[](#routes)

Prefix and middleware applied to all passwordless routes.

```
'routes' => [
    'prefix'     => 'auth',
    'middleware' => ['web'],
],
```

`.env` keyDefault`PASSWORDLESS_ROUTE_PREFIX``auth`Change the prefix to mount the routes under `/login`:

```
'routes' => [
    'prefix'     => 'login',
    'middleware' => ['web'],
],
```

---

### `views`

[](#views)

Override any view by pointing to your own. Set a key to a view string to use it instead of the package default. Leave as `null` to use the package default.

```
'views' => [
    'magic_link_request' => null,  // GET /auth/magic-link
    'magic_link_sent'    => null,  // after POST /auth/magic-link
    'magic_link_email'   => null,  // email notification
    'login_code_request' => null,  // GET /auth/code
    'login_code_verify'  => null,  // GET /auth/code/verify
    'login_code_email'   => null,  // email notification
],
```

Example — use your own Blade view for the magic link email:

```
'views' => [
    'magic_link_email' => 'emails.auth.magic-link',
],
```

Your view receives these variables:

FlowVariableTypeDescriptionMagic link`$url``string`The fully-signed magic link URLMagic link`$expiresMins``int`TTL in minutesLogin code`$code``string`The plain-text OTP codeLogin code`$expiresMins``int`TTL in minutesAlternatively, publish the built-in views and edit them in your project:

```
php artisan vendor:publish --tag="laravel-passwordless-views"
# → resources/views/vendor/laravel-passwordless/
```

---

### `inertia` + `components`

[](#inertia--components)

If your app uses **Inertia.js**, enable this mode so the package renders your Inertia components instead of Blade views.

The quickest way to get started is the install command — it auto-detects your framework and scaffolds ready-to-use component stubs:

```
php artisan passwordless:install-inertia
```

Then enable Inertia mode in `config/passwordless.php`:

```
'inertia' => true,

'components' => [
    'magic_link_request' => 'Auth/MagicLinkRequest',
    'magic_link_sent'    => 'Auth/MagicLinkSent',
    'login_code_request' => 'Auth/LoginCodeRequest',
    'login_code_verify'  => 'Auth/LoginCodeVerify',
],
```

Or via `.env`:

```
PASSWORDLESS_INERTIA=true
```

When `inertia` is `true`, the controllers call `Inertia::render($component, $props)` instead of `view()`. The `components` keys take precedence over the `views` keys when Inertia is enabled.

**Props passed to each component:**

ComponentPropTypeDescription`login_code_verify``email``string`The pending email address from the sessionAll other components receive no additional props.

> **Note:** The `inertiajs/inertia-laravel` package is not a hard dependency of this package — it only needs to be installed in your app if you enable Inertia mode.

**Example Vue component for the login code request form:**

```

import { useForm } from '@inertiajs/vue3'

const form = useForm({ email: '' })
const submit = () => form.post(route('passwordless.login-code.send'))

        Send code
        {{ form.errors.email }}

```

**Example Vue component for the code verify form:**

```

import { useForm } from '@inertiajs/vue3'

defineProps({ email: String })

const form = useForm({ code: '' })
const submit = () => form.post(route('passwordless.login-code.authenticate'))

        Verify
        {{ form.errors.code }}

```

---

### `actions`

[](#actions)

Swap any action class with your own implementation. Your class must implement the corresponding contract from `Torqie\LaravelPasswordless\Contracts`.

```
'actions' => [
    'generate_magic_link'     => \App\Auth\MyGenerateMagicLinkAction::class,
    'authenticate_magic_link' => \Torqie\LaravelPasswordless\Actions\AuthenticateViaMagicLinkAction::class,
    'generate_login_code'     => \Torqie\LaravelPasswordless\Actions\GenerateLoginCodeAction::class,
    'authenticate_login_code' => \Torqie\LaravelPasswordless\Actions\AuthenticateViaLoginCodeAction::class,
],
```

---

Customisation
-------------

[](#customisation)

### Swapping an action class

[](#swapping-an-action-class)

1. Create a class that implements the relevant contract:

```
use Torqie\LaravelPasswordless\Contracts\GeneratesMagicLink;

class MyGenerateMagicLinkAction implements GeneratesMagicLink
{
    public function generate(\Illuminate\Contracts\Auth\Authenticatable $authenticatable): string
    {
        // your logic
    }
}
```

2. Register it in `config/passwordless.php`:

```
'actions' => [
    'generate_magic_link' => \App\Auth\MyGenerateMagicLinkAction::class,
],
```

### Listening to events

[](#listening-to-events)

The package dispatches three events you can listen to in your `EventServiceProvider` or using `#[Listen]` attributes:

EventFired whenProperties`MagicLinkSent`A magic link is generated and emailed`$authenticatable`, `$url``LoginCodeSent`A login code is generated and emailed`$authenticatable``UserAuthenticatedPasswordlessly`A user successfully logs in`$authenticatable`, `$type` (`magic_link` | `login_code`)```
use Torqie\LaravelPasswordless\Events\UserAuthenticatedPasswordlessly;

class LogPasswordlessLogin
{
    public function handle(UserAuthenticatedPasswordlessly $event): void
    {
        activity()
            ->causedBy($event->authenticatable)
            ->log("Logged in via {$event->type}");
    }
}
```

### Rate limiting

[](#rate-limiting)

Login code verification is rate-limited at **5 failed attempts per email address** using Laravel's `RateLimiter`. Exceeding the limit returns a `ValidationException` with a countdown message. The counter is cleared automatically on a successful authentication.

### The `passwordless.signed` middleware

[](#the-passwordlesssigned-middleware)

The magic link authenticate route is protected by the `passwordless.signed` middleware alias, which validates the signed URL signature and expiry. You can apply this middleware to your own routes if needed:

```
Route::get('/my-route/{token}', MyController::class)->middleware('passwordless.signed');
```

---

Artisan Commands
----------------

[](#artisan-commands)

### `passwordless:install`

[](#passwordlessinstall)

The primary setup wizard. Covers everything in one interactive session: publishing config, migrations, running migrations, choosing your auth flows, scaffolding views or Inertia components, and writing `.env` keys.

```
php artisan passwordless:install
```

Use `--force` to overwrite any already-published files when re-running:

```
php artisan passwordless:install --force
```

---

### `passwordless:install-inertia`

[](#passwordlessinstall-inertia)

Standalone command for adding Inertia component stubs to an already-installed app. Reads `package.json` to auto-detect your framework, or accepts `--framework` explicitly.

```
# Auto-detect from package.json
php artisan passwordless:install-inertia

# Explicit framework
php artisan passwordless:install-inertia --framework=vue
php artisan passwordless:install-inertia --framework=react
php artisan passwordless:install-inertia --framework=svelte

# Overwrite existing stubs
php artisan passwordless:install-inertia --force
```

**Published files (Vue example):**

```
resources/js/Pages/Auth/LoginCodeRequest.vue
resources/js/Pages/Auth/LoginCodeVerify.vue
resources/js/Pages/Auth/MagicLinkRequest.vue
resources/js/Pages/Auth/MagicLinkSent.vue

```

After running, the command prints the config snippet you need to add to `config/passwordless.php` to enable Inertia mode.

---

### `passwordless:purge`

[](#passwordlesspurge)

Remove expired and/or used tokens from the database. Run this periodically (e.g. via the scheduler) to keep the `passwordless_tokens` table clean.

```
# Purge everything expired or used (default)
php artisan passwordless:purge

# Only remove expired tokens
php artisan passwordless:purge --expired

# Only remove used (but not yet expired) tokens
php artisan passwordless:purge --used
```

Schedule it in `routes/console.php`:

```
Schedule::command('passwordless:purge')->daily();
```

---

Testing
-------

[](#testing)

```
composer test
```

The package ships with a full Pest test suite — 102 tests covering models, actions, controllers, notifications, events, the fluent API, and the purge command.

---

Changelog
---------

[](#changelog)

Please see [CHANGELOG](CHANGELOG.md) for more information on what has changed recently.

Contributing
------------

[](#contributing)

Please see [CONTRIBUTING](CONTRIBUTING.md) for details.

Security Vulnerabilities
------------------------

[](#security-vulnerabilities)

Please review [our security policy](../../security/policy) on how to report security vulnerabilities.

Credits
-------

[](#credits)

- [Terik Hone](https://github.com/torqie)
- [All Contributors](../../contributors)

License
-------

[](#license)

The MIT License (MIT). Please see [License File](LICENSE.md) for more information.

###  Health Score

43

—

FairBetter than 90% of packages

Maintenance85

Actively maintained with recent releases

Popularity12

Limited adoption so far

Community8

Small or concentrated contributor base

Maturity56

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 98% 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

Every ~0 days

Total

3

Last Release

95d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/1f7224866010dc91e574a2ed946cbbf309822d2655e31298c899de8cd6440389?d=identicon)[torqie](/maintainers/torqie)

---

Top Contributors

[![torqie](https://avatars.githubusercontent.com/u/6262398?v=4)](https://github.com/torqie "torqie (49 commits)")[![dependabot[bot]](https://avatars.githubusercontent.com/in/29110?v=4)](https://github.com/dependabot[bot] "dependabot[bot] (1 commits)")

---

Tags

laravellaravel-passwordlesstorqie

###  Code Quality

TestsPest

Static AnalysisPHPStan

Code StyleLaravel Pint

### Embed Badge

![Health badge](/badges/torqie-laravel-passwordless/health.svg)

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

###  Alternatives

[spatie/laravel-permission

Permission handling for Laravel 12 and up

12.9k98.0M1.3k](/packages/spatie-laravel-permission)[spatie/laravel-pdf

Create PDFs in Laravel apps

1.0k4.3M42](/packages/spatie-laravel-pdf)[spatie/laravel-passkeys

Use passkeys in your Laravel app

470755.5k32](/packages/spatie-laravel-passkeys)[rawilk/profile-filament-plugin

Profile &amp; MFA starter kit for filament.

3913.7k](/packages/rawilk-profile-filament-plugin)[jeffgreco13/filament-breezy

A custom package for Filament with login flow, profile and teams support.

1.0k1.9M53](/packages/jeffgreco13-filament-breezy)[spatie/laravel-health

Monitor the health of a Laravel application

87411.3M153](/packages/spatie-laravel-health)

PHPackages © 2026

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