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

# 2D vs 3D: Choosing Your Adoption Mode

> Compare 2D mode where Identity equals Principal, and 3D mode where a separate Principal and Tenant model support multi-tenant apps.

Laravel Authentication supports two adoption modes that describe how your domain maps onto the Identity–Principal–Device model. Choosing the right mode is less about the package and more about whether your app needs to distinguish "who logged in" from "who this request is acting as." Both modes use the same guards, the same facade, and the same middleware — the difference is entirely in how you implement your Eloquent models.

## 2D mode: identity is the principal

In 2D mode, one model implements both `Identity` and `Principal`. The logged-in user *is* the actor on whose behalf every request runs. There is no separate membership, role model, or tenant record — the user themselves is the scope of each action.

`Auth::identity()` and `Auth::principal()` return the **same object** in this mode. `Auth::tenant()` returns `null` because the principal's `getTenant()` returns `null`. `Auth::type()` likewise returns `null`.

**When to use 2D mode:**

* Simple single-tenant applications where every user has the same scope
* Machine-to-machine APIs where the authenticating service account is also the actor
* Internal tools without multi-tenant isolation requirements
* Early-stage products where you want to get auth working now and grow the model later

## 3D mode: identity, principal, and tenant are separate

In 3D mode, three separate models carry the three concerns:

* An **Identity** model represents the human (or service account) — it implements `Identity` and `HasPrincipals`
* A **Principal** model represents the tenant-scoped actor — typically a membership or role row — and implements `Principal`
* A **Tenant** model represents the isolation boundary and implements `Tenant`

`Auth::identity()` returns the human. `Auth::principal()` returns the membership they are currently acting as (pinned by the `pid` claim in the access token). `Auth::tenant()` returns the tenant that membership belongs to.

**When to use 3D mode:**

* Multi-tenant SaaS where users belong to one or more workspaces, organizations, or teams
* Apps where a single login should be able to switch between different tenant contexts
* Platforms with per-tenant roles or permissions that differ from the user's global account
* Any domain where "acting on behalf of" is a first-class concept

## Side-by-side comparison

<Tabs>
  <Tab title="2D mode">
    One model handles everything. Point `auth.providers.users.model` at it and you're done.

    ```php theme={null}
    use Illuminate\Foundation\Auth\User;
    use SineMacula\Laravel\Authentication\Contracts\Identity;
    use SineMacula\Laravel\Authentication\Contracts\Principal;
    use SineMacula\Laravel\Authentication\Traits\ActsAsPrincipal;
    use SineMacula\Laravel\Authentication\Traits\Authenticatable;

    class AppUser extends User implements Identity, Principal
    {
        use Authenticatable, ActsAsPrincipal;
    }
    ```

    At runtime:

    ```php theme={null}
    Auth::identity();   // AppUser instance
    Auth::principal();  // same AppUser instance
    Auth::tenant();     // null
    Auth::type();       // null
    ```
  </Tab>

  <Tab title="3D mode">
    Three separate models. The identity implements `HasPrincipals`; the principal implements `Principal` and belongs to a tenant; the tenant implements `Tenant`.

    ```php theme={null}
    // The human
    class AppIdentity extends User implements Identity, HasPrincipals
    {
        use Authenticatable;

        public function principals(): HasMany
        {
            return $this->hasMany(AppMembership::class, 'identity_id');
        }

        public function resolveDefaultPrincipal(): ?PrincipalContract
        {
            return $this->principals()->where('is_active', true)->first();
        }
    }

    // The tenant-scoped actor
    class AppMembership extends Model implements PrincipalContract
    {
        use ActsAsPrincipal;

        public function tenant(): BelongsTo
        {
            return $this->belongsTo(AppTenant::class);
        }
    }

    // The isolation boundary
    class AppTenant extends Model implements TenantContract
    {
        use ActsAsTenant;
    }
    ```

    At runtime:

    ```php theme={null}
    Auth::identity();   // AppIdentity instance (the human)
    Auth::principal();  // AppMembership instance (pinned by pid claim)
    Auth::tenant();     // AppTenant instance
    Auth::type();       // null unless AppTenant also implements HasType
    ```
  </Tab>
</Tabs>

## Starting with 2D and growing into 3D

The two modes are additive — you do not need to re-platform to move from 2D to 3D. The guards change nothing; only your model implementations change. Here is a safe migration path:

<Steps>
  <Step title="Start with 2D">
    Implement `Identity` and `Principal` on your existing user model. Issue tokens, wire middleware, ship.
  </Step>

  <Step title="Add a membership model">
    When you need per-tenant roles, create an `AppMembership` model that implements `Principal`. Keep your user model implementing `Identity` but add `HasPrincipals` to it. The guards pick up the new contract automatically.
  </Step>

  <Step title="Add a tenant model">
    Create an `AppTenant` model that implements `Tenant`. Wire `AppMembership::tenant()` to return it. `Auth::tenant()` now works without any change to your guards or middleware.
  </Step>

  <Step title="Optionally add HasType">
    If you need to branch on tenant category (e.g. `'staff'` vs `'customer'`), add `HasType` to `AppTenant` and implement `getType()`. `Auth::type()` starts returning the value immediately.
  </Step>
</Steps>

<Note>
  Existing access tokens issued in 2D mode continue to work during and after migration. The `pid` claim in a 2D token contains the identity's own identifier. When you switch to 3D, new tokens carry a membership identifier as `pid`. Old tokens will fail to resolve a `Principal` if the old `pid` no longer matches a membership row — issue a fresh token to users after migration.
</Note>

<CardGroup cols={2}>
  <Card title="2D setup guide" icon="user" href="/guides/2d-setup">
    Step-by-step walkthrough for wiring up a single-model identity that acts as its own principal.
  </Card>

  <Card title="3D setup guide" icon="users" href="/guides/3d-setup">
    Step-by-step walkthrough for separating identity, membership, and tenant into three models.
  </Card>
</CardGroup>
