> ## Documentation Index
> Fetch the complete documentation index at: https://docs.sinemacula.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Identity, Principal, Device, and Tenant Explained

> Learn what Identity, Principal, Device, and Tenant mean in Laravel Authentication and why separating them improves multi-tenant security.

Most auth packages collapse "who you are" and "who you're acting as" into a single user model. That works fine for simple apps, but it creates real problems once you add multi-tenancy, per-tenant roles, or M2M tokens that act on behalf of a specific workspace membership. Laravel Authentication solves this by giving each concern its own contract: **Identity**, **Principal**, **Device**, **Tenant**, and the optional **Type** label that sits on a tenant.

## Why the single-model approach falls short

When one model does everything, you end up with row-level checks scattered across your codebase, fat `User` models that know about every feature, and authorization logic that has to re-derive "which workspace is this request for?" from request context instead of reading it off the authenticated state. Worse, a token that grants access to *any* of a user's tenants becomes a wider blast radius than necessary — a compromised credential can act across every workspace the user belongs to.

Separating identity from principal gives you a natural enforcement point: the access token is minted for a specific principal (e.g. a tenant membership), the guard rehydrates that exact principal on every request, and a token for one membership simply cannot be used to act as another.

## The five concepts

| Concept       | What it represents                                                                                                    | Contract    |
| ------------- | --------------------------------------------------------------------------------------------------------------------- | ----------- |
| **Identity**  | Who logged in — the person or service account behind the request                                                      | `Identity`  |
| **Principal** | Who the request is acting as — often the same as the identity, but in 3D mode it's a tenant-scoped membership or role | `Principal` |
| **Device**    | Which client obtained the login — a browser, mobile app, or CLI session that holds the refresh token                  | `Device`    |
| **Tenant**    | The isolation boundary the principal acts within — a company, workspace, or project                                   | `Tenant`    |
| **Type**      | An optional categorical label on the tenant, e.g. `"staff"` vs `"customer"`                                           | `HasType`   |

Each concept maps to a PHP interface in the `SineMacula\Laravel\Authentication\Contracts` namespace. Your Eloquent models implement whichever contracts match your domain.

## Contracts at a glance

**`Identity`** extends Laravel's `Authenticatable`. Any model that implements it can be loaded by the guard's identity provider and rehydrated from a bearer token's `sub` claim.

**`Principal`** is the acting entity. It exposes three core methods:

* `getPrincipalIdentifier()` — the stable ID embedded as the `pid` claim in every access token
* `getIdentity()` — the identity that owns this principal (in 2D mode this returns `$this`)
* `getTenant()` — the tenant the principal acts within, or `null` if none
* `isActive()` — lets the guard reject inactive memberships without a separate active-state check

**`Device`** is the client record that anchors refresh-token rotation. It carries the hashed refresh key, a `revoked_at` timestamp, last-login metadata, and the operating system captured at registration. The guard reads `Device::getDeviceIdentifier()` to confirm a `did` claim matches a real row.

**`Tenant`** is the isolation boundary. It exposes `getTenantIdentifier()` so the package can identify it without knowing anything about your domain model.

**`HasType`** is an optional capability contract that can be added to any `Tenant` implementation. When present, `Auth::type()` reads its `getType()` return value. When absent, `Auth::type()` returns `null`.

Two additional capability contracts extend what an `Identity` model can do:

* **`HasDevices`** — exposes a `devices()` Eloquent relation builder; required for device tracking and refresh-token rotation
* **`HasPrincipals`** — exposes a `principals()` relation and `resolveDefaultPrincipal()`; required for 3D mode where identity and principal are separate models

## Runtime accessors

All five values are readable through the standard `Auth` facade after a successful authentication. Import the package facade to get the contextual methods alongside the standard Laravel ones:

```php theme={null}
use SineMacula\Laravel\Authentication\Facades\Auth;

Auth::check();       // bool — same as Laravel
Auth::user();        // Identity|null — same as Laravel

Auth::identity();    // Identity|null   — the authenticated subject
Auth::principal();   // Principal|null  — the acting principal
Auth::device();      // Device|null     — the issuing device
Auth::tenant();      // Tenant|null     — the tenant the principal acts within
Auth::type();        // string|null     — the tenant's type string, or null
```

These are not new globals — they read from the active guard's contextual state, which the guard populates automatically when it resolves a bearer token or validates Basic credentials.

### When `Auth::type()` returns `null`

`Auth::type()` returns `null` in two situations:

1. The request has no authenticated principal (the guard has no bound state).
2. The resolved tenant does not implement `HasType`.

If your tenant model has different tiers but you have not yet added `HasType`, the method safely returns `null` rather than throwing. You can add `HasType` incrementally once you need it.

<Note>
  `Auth::tenant()` and `Auth::type()` are only non-null in 3D mode where a separate `Tenant` model exists. In 2D mode, where one model implements both `Identity` and `Principal`, `getTenant()` on the principal returns `null` and both accessors return `null` accordingly.
</Note>

## How the pieces fit together

The authentication lifecycle follows a single directed path on every request:

<Steps>
  <Step title="Identity logs in">
    The guard reads the bearer token or Basic credentials, looks up the identity via the configured provider, and confirms it is active (if the model implements `CanBeActive`).
  </Step>

  <Step title="Principal is resolved">
    The principal resolver maps the identity to its acting principal. In 3D mode this uses the `pid` claim embedded in the token; in 2D mode the identity *is* the principal.
  </Step>

  <Step title="Device is rehydrated">
    When the token carries a `did` claim and the identity implements `HasDevices`, the guard loads the device row and binds it. If the `did` is present but resolves to nothing, the guard rejects the request — it never silently drops to "no device".
  </Step>

  <Step title="Tenant scopes the principal">
    `Auth::tenant()` reads `principal->getTenant()`. The guard itself does not load the tenant; your principal model exposes it, so you control the query shape (eager-loaded, lazy, joined — whatever fits your schema).
  </Step>
</Steps>

<Tip>
  If tenant resolution is on your hot path, return the tenant relation already hydrated from `resolveDefaultPrincipal()` or `resolveHintedPrincipal()`. That way `Auth::tenant()` hits no extra queries.
</Tip>
