Skip to content

Instantly share code, notes, and snippets.

@mrl22
Last active April 10, 2026 13:23
Show Gist options
  • Select an option

  • Save mrl22/97d56399b9acc2df6b71a615eb3b604e to your computer and use it in GitHub Desktop.

Select an option

Save mrl22/97d56399b9acc2df6b71a615eb3b604e to your computer and use it in GitHub Desktop.
AI Task: Laravel Passwordless Magic Link Authentication Implementation

AI Task: Laravel Passwordless Magic Link Authentication Implementation

This plan outlines the steps for an AI to implement a passwordless "magic link" authentication system in a Laravel application, replacing traditional password-based login.


Phase 1: Database & Model Setup

  1. Create LoginToken Migration:
    • Table name: login_tokens.
    • Fields:
      • string('email')->index(): The user's email address.
      • string('token', 64)->index(): A SHA-256 hash of the random token.
      • timestamp('consumed_at')->nullable(): To ensure single-use.
      • timestamp('expires_at'): Set to 30 minutes from creation.
      • timestamps(): Standard Laravel timestamps.
  2. Create LoginToken Model:
    • Add $fillable for email, token, consumed_at, and expires_at.
    • Cast consumed_at and expires_at to datetime.
  3. Run Migration: Execute php artisan migrate.

Phase 2: Notification & Logic

  1. Create PasswordlessLoginNotification:
    • php artisan make:notification PasswordlessLoginNotification.
    • Constructor should accept the $url.
    • toMail method should return a mail message with a "Login" button pointing to the magic link URL.
    • Mail message should contain wording "If you did not create a login request, please ignore this email.".
  2. Update Login Form:
    • UI: Remove the password field from the login blade.
    • Validation: Validate email format only. Do NOT use exists:users,email to prevent enumeration.
    • Rate Limiting: Implement a throttle (e.g., 5 attempts per 10 minutes) on the email/IP.
    • Invalidation: Always delete old tokens for the email before generating a new one.
    • Token Generation:
      • Create a random 40-character token using Str::random(40).
      • Store a SHA-256 hash of the token (hash('sha256', $token)) in login_tokens.
    • URL Generation:
      • Generate a temporary signed route to the confirmation page: URL::temporarySignedRoute('passwordless.confirm', now()->addMinutes(30), ['email' => $email, 'token' => $token]).
    • Feedback: Always show the same success message: "If an account exists, we've sent a link."

Phase 3: Routing & Authentication

  1. Define Routes in routes/auth.php:
    • Add a GET route /verify-login/{token} named passwordless.confirm.
    • Apply signed and guest middleware.
  2. Implement Confirmation Page:
    • Purpose: Prevent email prefetching/scanners from auto-consuming the token.
    • Mount Verification:
      • Hash the raw token from the URL.
      • Verify the token exists and is valid.
    • Atomic Consumption:
      • On "Continue Login" click, perform an atomic update: LoginToken::where(...)->whereNull('consumed_at')->update(['consumed_at' => now()]).
      • If the update fails (returns 0), abort with 403 (token already used or invalid).
    • Authentication:
      • Retrieve User, log in with Auth::login($user, $remember).
      • Call Session::regenerate().
      • Mark as verified: $user->markEmailAsVerified().

Phase 4: System Cleanup (Remove Password Dependencies)

  1. UI Cleanup:
    • Remove password fields from register.blade.php.
    • Remove password fields from user management (add/edit user).
    • Remove "Update Password" section from the profile page.
  2. Backend Logic Updates:
    • Update Register component and UserController to generate a secure random password (Hash::make(Str::password())) when creating users to satisfy DB constraints.
    • Ensure password fields are ignored during user updates.
  3. Authentication Cleanup:
    • Remove CanResetPassword trait and interface from the User model.
    • Remove password reset routes (forgot-password, reset-password).
    • Delete the password_reset_tokens table via migration.
    • Remove verified middleware from routes (since magic link login verifies the user).
    • Delete VerifyEmailController and related UI components.

Phase 5: Verification & Styling

  1. Formatting: Run vendor/bin/pint to ensure code style compliance.
  2. Testing:
    • Verify a user can request a link.
    • Verify the link expires after 30 minutes.
    • Verify the link cannot be used twice.
    • Verify tampering with the URL results in a 403 error.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment