---
title: "Login methods — Auth for Laravel | Roundly"
description: "Password, magic link, email code and passwordless passkey login — what each does, how it is throttled and what every login goes through."
url: https://roundly-consulting.com/open-source/docs/auth-for-laravel/login-methods
language: en
---

[All packages](https://roundly-consulting.com/open-source.md)

[Auth for Laravel](https://roundly-consulting.com/open-source/docs/auth-for-laravel.md)

# Login methods

Four primary methods, each switchable per guard. At least one must be enabled:

```php
'guards' => [
    'users' => [
        'model' => App\Models\User::class,
        'login' => [
            'password' => true,
            'magic_link' => true,
            'email_otp' => true,
            'passkey' => true,               // passwordless
            'reveal_account_state' => true,  // disabled/unverified codes only after a verified first factor
        ],
        'magic_link' => ['ttl' => 900, 'same_device' => false],
        'email_otp' => ['ttl' => 600, 'length' => 6, 'max_attempts' => 5],
    ],
],
```

```php
$guard   = Authentication::guard('users');
$context = $guard->contextFrom($request);

// Password
$result = $guard->attempt(new PasswordCredentials('ada@example.com', $password), $context);

// Magic link: the request never reveals whether the address exists
$guard->requestMagicLink('ada@example.com', $context);
$result = $guard->consumeMagicLink($token, $context);

// Email code
$guard->requestEmailOtp('ada@example.com', $context);
$result = $guard->verifyEmailOtp('ada@example.com', $code, $context);

// Passwordless passkey (discoverable credentials)
$options = $guard->passkeyLoginOptions($context);             // for navigator.credentials.get()
$result  = $guard->loginWithPasskey($assertion, $context);    // passkeys' AuthenticationResponseData
```

## Password

The login, login\_ip and login\_account throttles are checked before any hashing. Unknown or passwordless accounts are checked against a dummy hash of the same cost, so response time does not reveal whether an account exists. A failure hits all three buckets, bumps the opt-in lockout counter and answers invalid\_credentials. A hard-locked account answers exactly like a wrong password — a dummy check, the attempt stays counted, invalid\_credentials — so a lock confirms neither the password nor that the account exists; locked\_until is read again once the password is hashed, so a lock written meanwhile refuses that login the same way, a correct password included. A correct password gets its attempt back only once the login is challenged or completes: one the login then refuses with the uniform invalid\_credentials (a disabled or unverified account without reveal\_account\_state, a uniform risk denial, an unavailable step-up) stays counted like a wrong one, so the throttle never tells the right guess apart.

## Magic link

A real, enabled account receives a 64-character link with the secret in the URL fragment; the request always answers 202 sent. On consume, the link’s address must still be the account’s. With magic\_link.same\_device, the requesting device’s fingerprint is checked before the claim, so a mismatch does not burn the link.

## Email code

Same request semantics as the magic link. Verification runs through the three login buckets (a code guess is a login attempt), never reveals attempts\_left and binds the code to its address.

## Passkey

Options are discoverable — no identifier, no allowCredentials. With passkeys.satisfies\_mfa they require user verification, enforced for that ceremony. The ceremony id must come back with the assertion, another guard’s credential is refused, and every failure is a uniform invalid\_credentials.

## What every login goes through

- Possession side effects — a magic-link or email-code login verifies the address (verification.verify\_on\_email\_login).
- Account state — disabled answers account\_disabled (or invalid\_credentials with reveal\_account\_state off); locked answers too\_many\_attempts on a link, code or passkey login (a password login has already answered invalid\_credentials); unverified under required\_for\_login answers email\_not\_verified.
- Password rehash.
- Locale fill when the account has none, and new-device detection.
- Risk — the configured reaction can notify, require a second factor or deny, whatever the login method.
- Required steps — a step-up with no factor to step up with is denied. Only past this point are the lockout counter reset and the login bucket cleared, so a uniform refusal before it leaves both exactly as a wrong password would.
- Either a challenge, or the success tail: tokens, last\_login\_at, activity, LoginSucceeded and TokensIssued.

## Authentication method references

Access tokens carry amr (RFC 8176). A completed challenge adds its factors and mfa:

| Primary method | amr |
| --- | --- |
| Password | pwd |
| Magic link | email |
| Email code | otp |
| Passkey | hwk, user (+ mfa when user verification was required) |
| Invitation | email (none for an unverified chosen address) |
| Password reset | email |
| Registration | pwd or none |

## Show your open-source love

This package is free and MIT-licensed. If it saves you time, a one-off donation or a Patreon membership keeps it maintained, tested and documented.

[More ways to support, including crypto](https://roundly-consulting.com/support-us.md)

By donating, you agree to our [donation terms](https://roundly-consulting.com/donation-terms.md).

[Support our open source work (opens in a new tab)](https://donate.stripe.com/dRmeVe8FX5PF1Qd9pXcEw00) [Join us on Patreon (opens in a new tab)](https://www.patreon.com/cw/roundly)

## Want this built into your product?

We integrate our packages into custom Laravel and AI builds. Tell us what you're working on and we'll reply within 48 hours.

[Get a quote in 48 hours](https://roundly-consulting.com/contact.md) [Browse all packages](https://roundly-consulting.com/open-source/docs/auth-for-laravel.md)
