---
title: "Account model — Auth for Laravel | Roundly"
description: "The Account contract, the HasAuthentication trait, the account columns, account lookups and the disable, lock and locale lifecycle."
url: https://roundly-consulting.com/open-source/docs/auth-for-laravel/account-model
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)

# Account model

Each guard’s model implements RoundlyConsulting\\Auth\\Contracts\\Account and uses the HasAuthentication trait. This is what php artisan authentication:guard clients generates (--no-two-factor / --no-passkeys drop the matching lines; a renamed authentication.columns.password / .email\_verified\_at replaces that name in $hidden and casts()):

```php
use Illuminate\Auth\Authenticatable as AuthenticatableConcern;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Notifications\Notifiable;
use RoundlyConsulting\Auth\Concerns\HasAuthentication;
use RoundlyConsulting\Auth\Contracts\Account;
use RoundlyConsulting\Passkeys\Concerns\InteractsWithPasskeys;
use RoundlyConsulting\Passkeys\Contracts\HasPasskeys;
use RoundlyConsulting\RefreshTokens\Traits\HasRefreshTokens;
use RoundlyConsulting\TwoFactor\Concerns\HasTwoFactorAuthentication;
use RoundlyConsulting\TwoFactor\Contracts\TwoFactorAuthenticatable;

final class Client extends Model implements Account, HasPasskeys, TwoFactorAuthenticatable
{
    use AuthenticatableConcern;
    use HasAuthentication;
    use HasFactory;
    use HasRefreshTokens;
    use HasTwoFactorAuthentication;
    use InteractsWithPasskeys;
    use Notifiable;
    use SoftDeletes;

    protected $table = 'clients';

    protected $guarded = [];

    protected $hidden = ['password'];

    protected function casts(): array
    {
        return [...$this->authenticationCasts(), ...$this->twoFactorCasts(), 'email_verified_at' => 'datetime'];
    }
}
```

TwoFactorAuthenticatable is required when the guard’s 2FA mode is not off, HasPasskeys when passkeys are on (or passkey login is enabled) — authentication:check verifies it.

## The Account contract

| Method | Returns |
| --- | --- |
| `accountEmail()` | ?string |
| `hasPassword()` | bool |
| `hasVerifiedEmail() / markEmailAsVerified()` | bool |
| `tokenVersion()` | int — the tv claim; bumped on invalidation |
| `isDisabled()` | bool |
| `isLocked()` | bool — locked\_until in the future |
| `accountLocale() / accountTimezone()` | ?string |

## The HasAuthentication trait

- Implements the contract over the configurable authentication.columns.\*.
- authenticationCasts() — spread into casts(): token\_version and failed\_login\_count as integers; password\_changed\_at, last\_login\_at, disabled\_at and locked\_until as immutable datetimes.
- accountEmailColumn(), lockedUntil() and preferredLocale() — Laravel localises the account’s notifications with the latter.
- Hides the password, token\_version, locked\_until, disabled\_reason and failed\_login\_count columns from serialisation under their configured authentication.columns.\* names — a renamed password column stays hidden even next to a stock $hidden = \['password'\].

## Account columns

The authenticationColumns() Blueprint macro adds token\_version, locale, timezone, password\_changed\_at, last\_login\_at, disabled\_at, disabled\_reason, locked\_until and failed\_login\_count (written only when lockout is on). The table also needs email (unique), email\_verified\_at and password (nullable for passwordless guards), plus two-factor’s and passkeys’ columns when used:

```php
Schema::create('clients', function (Blueprint $table) {
    $table->id();
    $table->string('name')->nullable();
    $table->string('email')->unique();
    $table->timestamp('email_verified_at')->nullable();
    $table->string('password')->nullable();
    $table->authenticationColumns();
    $table->twoFactorColumns();      // two-factor-for-laravel
    $table->passkeyUserHandle();     // passkeys-for-laravel
    $table->timestamps();
    $table->softDeletes();
});
```

## Static analysis

authenticationColumns(): void is declared on Blueprint for Larastan / PHPStan. A macro exists only once its service provider has run. Larastan boots an auto-discovering host app and usually finds it, but not when the provider isn’t loaded during analysis — package development under Testbench, dont-discover, a provider registered by hand. There, a migration calling the macro would fail with “Call to an undefined method”. The package ships a PHPStan extension (RoundlyConsulting\\Auth\\PHPStan\\AuthenticationMacrosExtension) that phpstan/extension-installer loads through extra.phpstan.includes — nothing to configure. Without the installer, include it yourself:

```yaml
includes:
    - vendor/roundly-consulting/auth-for-laravel/extension.neon
```

two-factor-for-laravel and passkeys-for-laravel declare twoFactorColumns() and passkeyUserHandle() the same way, each through its own extension.neon.

## Finding accounts

The AccountRepository is the only class that queries a guard’s model. Soft-deleted accounts never log in but keep their address taken:

```php
$accounts = Authentication::guard('users')->accounts();

$accounts->findForLogin('ada@example.com');   // tries identifier.columns in order
$accounts->findByEmail('ada@example.com');    // ?Account
$accounts->findByKey(42);                     // ?Account
$accounts->emailTaken('ada@example.com');     // includes soft-deleted accounts
```

## Account lifecycle

```php
use RoundlyConsulting\Auth\DataTransferObjects\LocaleData;
use RoundlyConsulting\Auth\Facades\Authentication;

$users = Authentication::guard('users');

$users->disable($user, 'chargeback fraud');   // every session ends; login and refresh refused
$users->enable($user);

$until = $users->lock($user, 3600);           // blocks new logins, sessions stay; the owner is notified
$users->unlock($user);

$users->updateLocale($user, new LocaleData(locale: 'sk', timezone: 'Europe/Bratislava'));
$users->logoutEverywhere($user);
```

- Disable — sets disabled\_at and disabled\_reason, invalidates everything (always all) and fires AccountDisabled. The account can no longer log in or refresh.
- Lock — locked\_until = now + seconds (default lockout.duration). Sessions stay alive; a lock only stops new logins, and the owner is notified as with the automatic lockout. Returns the unlock time.
- Locale — validates the locale against locale.supported and the timezone; fires LocaleUpdated.
- Logout everywhere — bumps the token version and revokes every session; also available as php artisan authentication:logout-everywhere {guard} {id}.

Pass the admin as actor: (any saved model) and it goes on AccountDisabled, AccountEnabled, AccountLocked and AccountUnlocked as actor, ready for an audit log; lock() also tells the owner an administrator locked the account. Disable, enable and unlock send no mail. The account itself counts as no actor; an unsaved model throws InvalidArgumentException before anything is written:

```php
Authentication::disable($user, 'chargeback fraud', actor: $admin);
Authentication::lock($user, seconds: 3600, actor: $admin);
Authentication::unlock($user, actor: $admin);
```

## 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)
