Advanced

HasPasskeys Trait

Eloquent trait that adds a polymorphic passkey relationship and automatic deletion to your model.

The HasPasskeys trait is provided by the package to wire up the polymorphic Eloquent relationship between any model (typically User) and the Passkey model.

Setup

Add the trait to your User model:

app/Models/User.php
use Xefi\LaravelPasskey\Traits\HasPasskeys;

class User extends Authenticatable
{
    use HasPasskeys;
}

Relationship

The trait exposes a single passkeys() relationship method:

public function passkeys(): MorphMany
{
    return $this->morphMany(Passkey::class, 'passkeeable');
}

This is a polymorphic relationship — the passkeys table stores the owner's class and ID via passkeeable_type and passkeeable_id. This means you can attach passkey to any model, not just User.

// Get all passkey for a user
$passkeys = $user->passkeys;

// Count passkey
$count = $user->passkeys()->count();

// Check if user has any passkey
$hasPasskeys = $user->passkeys()->exists();

// Delete all passkey for a user
$user->passkeys()->delete();

Customization methods

The trait provides two methods you can override in your model to control how user data is exposed during passkey operations.

getPasskeyDisplayName()

Returns the display name used during passkey registration and included in /login responses.

public function getPasskeyDisplayName(): string
{
    return $this->name ?? $this->email ?? (string) $this->getKey();
}

Override it in your model if the display name comes from a different field:

app/Models/User.php
public function getPasskeyDisplayName(): string
{
    return $this->display_name ?? $this->username;
}

getPasskeyEmail()

Returns the email used during passkey registration and included in /login responses.

public function getPasskeyEmail(): string
{
    return $this->email ?? '';
}

Override it if your model stores the email under a different attribute:

app/Models/User.php
public function getPasskeyEmail(): string
{
    return $this->email_address;
}

Cascade deletion

The trait registers a deleting model event via bootHasPasskeys(). When the owning model is deleted, all its associated passkey are automatically deleted:

protected static function bootHasPasskeys(): void
{
    static::deleting(function ($model) {
        $model->passkeys()->delete();
    });
}
Cascade deletion is handled in PHP by the model event, not by a database-level CASCADE constraint. Make sure you delete models through Eloquent (not raw SQL) for this to trigger.