Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Laravel Tokenize Login Laravel Package

imanghafoori/laravel-tokenize-login

Token-based, passwordless login for Laravel: generate secure, expiring tokens and signed URLs that authenticate users without passwords. Ideal for magic-link sign-in, one-time access, and API-style flows, with drop-in integration into Laravel's auth system and configurable token lifetimes.

View on GitHub
Deep Wiki
Context7

Getting Started

  1. Install and publish.

    composer require imanghafoori/laravel-tokenized-login
    php artisan vendor:publish --provider="Imanghafoori\\TokenizedLogin\\TokenizedLoginServiceProvider"
    

    Publish both the config (config/tokenized-login.php) and the migration. Run php artisan migrate so the token column/table exists before you wire the UI.

  2. Look here first, in this order:

    • config/tokenized-login.php — token lifetime, length, and the token_sender class key are the knobs you will actually touch.
    • vendor/imanghafoori/laravel-tokenized-login/src/ — the TokenSender contract and the trait are tiny; read them before customizing. Confirm exact method names (sendLoginToken, verifyLoginToken, etc.) in the source rather than trusting a blog post.
    • The package's routes/ file — this is where the "request token" and "verify token" endpoints live, if they ship enabled by default. If not, you write the controller yourself and the package only gives you the trait + contract.
  3. Bind your sender. The package does not send mail or SMS by itself; it delegates. Create app/TokenSenders/EmailTokenSender.php implementing the package contract, then bind it in AppServiceProvider::register() (or point the config key at the class, depending on which the published config exposes).

  4. Add the trait to your User model and make sure the columns the trait expects (token, token_created_at, or whatever the migration adds) are not in $guarded/are in $fillable as required.

  5. First end-to-end use case: a two-step login page. Step 1 collects the email, calls the send endpoint (or $user->sendLoginToken() directly), step 2 collects the token, calls verify, then Auth::login($user) on success and redirect. Get this working on a single guard before you touch anything else.

Implementation Patterns

Two-step controller, written by you. Skip the package's routes and own the flow end-to-end:

public function send(Request $request) {
    $request->validate(['email' => 'required|email']);
    $user = User::where('email', $request->email)->first();
    if ($user) { $user->sendLoginToken(); }
    return back()->with('status', 'If that account exists, a code is on its way.');
}

public function verify(Request $request) {
    $request->validate(['email' => 'required|email', 'token' => 'required|string']);
    $user = User::where('email', $request->email)->first();
    abort_unless($user && $user->verifyLoginToken($request->token), 422);
    Auth::login($user, $request->boolean('remember'));
    $request->session()->regenerate();
    return redirect()->intended('/dashboard');
}

Owning the controller means you control throttling, enumeration messages, and where the user lands — all things you will need to tune.

Swap the sender per channel. Implement EmailTokenSender and SmsTokenSender both against the same contract, then choose at runtime (config is static, so the cleaner pattern is to bind a CompositeTokenSender that inspects $user->phone/$user->email and dispatches). This keeps you on one trait method while supporting multiple login identifiers.

Multi-guard. The trait works on any authenticatable model, so Auth::guard('admin')->login($admin) after a successful verify is enough — you don't need separate endpoints per guard if the guard is inferred from the request's route group.

Rate limit outside the package. Add ->middleware('throttle:5,1') to the send route and throttle:10,1 to verify. The package predates the RateLimiter facade's current ergonomics and likely does not throttle by default; brute-forcing a 5-digit token is trivial without this.

Extend via events, not edits. If you need audit logging of login attempts, wrap the send/verify calls in your controller with your own event dispatch — do not fork the trait. The trait is a single file; keeping it pristine means composer update stays safe.

Testing. If your sender wraps Mail::to()->send() or a notification, Notification::fake() / Mail::fake() is enough. If it wraps an SMS SDK, bind a fake implementation of the contract in TestCase::setUp() so tests never hit the network. Assert both the happy path and that a re-used token is rejected.

Gotchas and Tips

  • Check the token column storage. Read the migration: if tokens are stored in plaintext, anyone with DB read access can log in as anyone. If that bothers you, override the trait's storage or hash on write and compare with hash_equals on verify. Either way, verify the comparison is timing-safe — == on a 5-digit code is a real weakness at scale.
  • Single-use is not guaranteed. Confirm the trait nulls the token after a successful verify. If it doesn't, add a $user->clearLoginToken() (or equivalent) call in your controller immediately after Auth::login().
  • Expiry units. token_lifetime is almost certainly in minutes, not seconds. Grep the trait for ->addMinutes / ->addSeconds before you set it to 300 thinking "5 minutes."
  • User enumeration. Never return "no such user" from the send endpoint. Always return the same generic message and the same HTTP status, or you've built a valid-email oracle for attackers.
  • Route collisions with your own /login. If the package registers routes, they may shadow or be shadowed by your app's auth routes. Run php artisan route:list | grep -i token and reconcile before debugging any 404/405.
  • Laravel version drift. Last release is Feb 2023, i.e. it targets Laravel 9/early 10. On Laravel 11+ you may need to loosen the illuminate/* constraint in your own composer.json or fork the repo; expect composer update to complain. Zero dependents means no one is filing these bugs upstream — pin the version and treat upgrades as a manual review.
  • Auth::login() before session regenerate. Call $request->session()->regenerate() on successful verify to prevent session fixation; the trait won't do this for you.
  • Debugging sends that silently fail. Wrap the sender invocation in try/catch and Log::error() the exception — if the package catches sender exceptions internally, a misconfigured mailer looks identical to a successful send, and you'll chase the wrong bug for an hour.
  • Trait method names can collide with your existing User methods (sendLoginToken, verifyLoginToken). Run php -l / a quick php artisan tinker to confirm; if they clash, alias with insteadof in your model.
  • Extension points, ranked by usefulness: (1) the sender contract — this is where 90% of real customization goes; (2) the published config; (3) wrapping the trait calls in your own service class so you can swap the package out later without touching controllers. Do not patch vendor/.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
ignibyte/aic-framework
glavweb/data-schema-bundle
glavweb/datagrid-bundle
tacticmedia/scim-server
uni-method/frame
neika-ai/sdk
adachsoft/php-code-style
pharaonic/laravel-notification-dependencies
beautinow/translug
dev-brainiac/bot-cloaked
octoberrain/html
octoberrain/extension
octoberrain/exception
octoberrain/contracts
adachsoft/api-version-bundle
cgv/localized-resource
noith/settings
kda/laravel-package
kda/filament-dynamic-navigation
bypersoft/signbox