Skip to main content
In 2D mode, the model that logs in is also the model that acts on every request. There is no separate principal or tenant — Auth::identity() and Auth::principal() both resolve to the same object. This is the right starting point for most apps: you can grow into 3D later without re-platforming your guard configuration.
2D is the correct choice for single-tenant APIs, M2M service accounts, and any app where users act only as themselves. If your users need to switch between tenant-scoped roles or memberships, see 3D multi-tenant setup instead.
1

Add the Identity and Principal contracts to your user model

Your user model must implement both Identity and Principal. The Authenticatable trait is a stateless drop-in for Laravel’s own Illuminate\Auth\Authenticatable — it suppresses the remember-token accessors that have no meaning in a sessionless package. The ActsAsPrincipal trait provides default implementations of the Principal contract methods (getPrincipalIdentifier(), getIdentity(), getTenant(), isActive()).
Point auth.providers.users.model at AppUser::class and the guard will use it for every bearer-token lookup.
2

Register the guard in config/auth.php

Register a guard with driver: jwt and wire it to your user provider. No extra service provider registration is needed — the package auto-registers its guard drivers.
Protect routes with the standard Laravel middleware: auth:api.
3

Optionally add HasDevices for refresh-token rotation

Device tracking and refresh-token rotation are opt-in. If you need them, publish and run the devices migration, then implement HasDevices on your model with a morphMany relation:
Without HasDevices, the guard operates in access-only mode: Auth::device() returns null and $guard->refresh() is unavailable. You can add device tracking later — it is purely additive.
4

Issue an access token at login

After validating credentials, call issueAccessToken() on the guard-scoped JWT service. Pass the identity, the principal, and — if you are using device tracking — the device record. Pass null for the device in access-only mode.
For access-only mode without device tracking, pass null as the third argument and skip issueRefreshToken:
5

Verify the contextual accessors

Once a request arrives with a valid bearer token, the guard rehydrates both identity and principal from the database and binds them. In 2D mode both accessors return the same model instance.
If you call Auth::identity() === Auth::principal() in 2D mode, the result is true — they are the same object.