Skip to main content
Device tracking associates each token issuance with a persisted device record in your database. It powers refresh-token rotation — each device row stores a hashed rotation digest that must match the presented refresh token, enabling replay detection and per-device revocation. When a device record is resolved on the bearer path, the package debounces last_logged_in_at writes to avoid hot-spot database pressure on high-traffic APIs. Device tracking is opt-in. If you only need short-lived access tokens without refresh (the common pattern for M2M APIs), you can skip the migration and the HasDevices contract entirely. See access-only mode below.

Config block

config/authentication.php

Fields

string
The fully-qualified class name of the Eloquent model used for device persistence. Sourced from AUTHENTICATION_DEVICE_MODEL. The default is the package’s shipped Device model. Swap this to a custom class when you need additional columns, relationships, or behaviour — the custom model must implement the EloquentDevice contract.
.env
string
default:"devices"
The database table that stores device records. Sourced from AUTHENTICATION_DEVICE_TABLE. Override this when your schema uses a different table name. The shipped Device model reads this value lazily on each instantiation, so runtime config swaps (such as in multi-tenancy scenarios or tests) take effect immediately.
.env
string
default:"refresh_key"
The column on the device table that stores the hashed refresh rotation digest. Sourced from AUTHENTICATION_DEVICE_REFRESH_KEY_COLUMN. The package reads and writes this column during refresh-token rotation; it must exist in the migration and be nullable (it is null before a refresh token has been issued for a device).
.env
number
default:"60"
The minimum number of seconds between last_logged_in_at writes for a given device on the bearer path. When a bearer request resolves a persisted device, the package updates last_logged_in_at only if the stored value is older than this threshold. Set to 0 to write on every request (not recommended on high-traffic APIs). Set to a larger value to reduce write frequency further.
.env

The shipped Device model

The package provides SineMacula\Laravel\Authentication\Models\Device as the default device adapter. Key characteristics:
  • UUID v7 primary key — generated on insert via Laravel’s HasUuids concern, so device IDs are time-ordered and globally unique without a sequence dependency.
  • Polymorphic authenticatable relation — a morphTo that can point at any identity model, so one devices table serves multiple authenticatable types in the same app.
  • Not final — you can subclass it to add columns or relationships while retaining the built-in EloquentDevice behaviour.
The model’s fillable attributes map directly to the columns created by the published migration:

Publishing and running the migration

This creates the devices table with the schema above. If you change device.table, update the migration’s Schema::create call before running it.

Using a custom device model

To use a custom device model, create a class that implements SineMacula\Laravel\Authentication\Contracts\EloquentDevice and point device.model at it:
config/authentication.php (or .env)
The simplest path is to subclass the shipped model:
app/Models/CustomDevice.php
If you need a fully independent implementation, implement EloquentDevice directly. The contract requires the persistence accessors the package needs for rotation and last-seen tracking — refer to the contract interface for the required method signatures.

Enabling device tracking on your identity model

To opt into device tracking and refresh-token rotation, implement HasDevices on your identity model and define the devices() morph-many relationship:
app/Models/User.php
Without HasDevices, the guard skips device resolution and refresh is unavailable.

Access-only mode

Device tracking and refresh-token rotation are entirely optional. If your use case only requires short-lived access tokens — common for M2M APIs, internal service calls, and simple backends — you can skip the devices table and the HasDevices contract.To run in access-only mode:
  1. Publish only the config: php artisan vendor:publish --tag=authentication-config. Skip the authentication-migrations tag.
  2. Do not implement HasDevices on your identity model.
  3. Issue access tokens with null as the device argument: Auth::jwt('api')->issueAccessToken($identity, $principal, null).
  4. Do not call $guard->refresh(...). Clients re-authenticate when their access token expires.
In this mode the package never touches a devices table. Auth::device() returns null, but the full contextual surface (Auth::identity(), Auth::principal(), Auth::tenant(), Auth::type()) still works normally. You can add the migration and refresh flow later — it is additive, not a rewrite.