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

# Users

> The user identity data model — profiles, credentials, sessions, and roles — that auth resolves and permissions consume.

The `@ooneex/user` package defines the identity contract for the framework. It ships **only types and enums** — no runtime, no storage, no logic. `IUser` describes who a request belongs to: email, roles, profile fields, two-factor state, and references to sessions, accounts, and verifications. Auth resolves a user from a token and attaches it to the request context; permission checks and controllers read it from there. Because it is a pure contract, every layer agrees on the same shape without depending on a database or auth provider.

## Why this package

* **One identity shape.** Every part of the stack — [auth](/security/auth), [roles](/security/roles), [permissions](/security/permissions), controllers — speaks the same `IUser` interface, so a user resolved in one place is usable everywhere.
* **Provider-agnostic.** The model is independent of how you authenticate. A Clerk, JWT, or credentials backend maps its own user into `IUser`; downstream code never sees the provider.
* **Zero runtime.** Pure `type`/`interface`/`enum` definitions. Importing it adds nothing to your bundle and forces no storage decisions.
* **Audit-aware.** Sessions, accounts, verifications, and profile updates each have a dedicated interface with timestamps and status, so security trails are modeled, not improvised.
* **Roles are first-class.** `roles` is a required field of uppercase strings, the same convention roles and route guards expect.

## How it works

A user does not arrive with the request — it is *resolved*. Auth middleware reads the bearer token, validates it, maps the result into an `IUser`, and sets `context.user`. From that point the same object travels through the pipeline.

| Stage             | What happens to the user                                                                               |                                      |
| ----------------- | ------------------------------------------------------------------------------------------------------ | ------------------------------------ |
| Request arrives   | `context.user` is `null` — no identity has been established yet.                                       |                                      |
| Auth middleware   | Validates the token, builds an `IUser`, and assigns `context.user`. A guest route may leave it `null`. |                                      |
| Permission checks | `setUserPermissions(context)` reads `context.user` and its `roles` to grant abilities.                 |                                      |
| Controller        | Reads `context.user` (typed \`IUser                                                                    | null\`) to scope data to the caller. |

In `@ooneex/controller`, the context types the user as nullable so guest-accessible routes are handled explicitly:

```typescript theme={null}
export type ContextType<T extends ContextConfigType = ContextConfigType> = {
  // ...
  user: IUser | null;
  permission?: IPermission;
};
```

## The `IUser` interface

`IUser` extends a shared `IBase` (lifecycle and moderation fields) and adds identity, profile, verification, and security fields. `email` and `roles` are required; everything else is optional.

| Field               | Type                  | Meaning                                                                                         |
| ------------------- | --------------------- | ----------------------------------------------------------------------------------------------- |
| `email`             | `string`              | Primary email address. Required.                                                                |
| `roles`             | `Uppercase<string>[]` | Roles granted to the user (e.g. `["ROLE_USER"]`). Required; consumed by roles and route guards. |
| `externalId`        | `string`              | Identifier from an external auth provider (e.g. a Clerk user id).                               |
| `name`              | `string`              | Full display name.                                                                              |
| `firstName`         | `string`              | Given name.                                                                                     |
| `lastName`          | `string`              | Family name.                                                                                    |
| `username`          | `string`              | Unique handle.                                                                                  |
| `avatar`            | `string`              | URL of the profile picture.                                                                     |
| `bio`               | `string`              | Short biography / about text.                                                                   |
| `phone`             | `string`              | Phone number.                                                                                   |
| `birthDate`         | `Date`                | Date of birth.                                                                                  |
| `timezone`          | `string`              | Preferred timezone.                                                                             |
| `isEmailVerified`   | `boolean`             | Whether the email has been verified.                                                            |
| `isPhoneVerified`   | `boolean`             | Whether the phone has been verified.                                                            |
| `lastActiveAt`      | `Date`                | Last activity timestamp.                                                                        |
| `emailVerifiedAt`   | `Date`                | When the email was verified.                                                                    |
| `phoneVerifiedAt`   | `Date`                | When the phone was verified.                                                                    |
| `lastLoginAt`       | `Date`                | Last successful login.                                                                          |
| `passwordChangedAt` | `Date`                | When the password was last changed.                                                             |
| `twoFactorEnabled`  | `boolean`             | Whether 2FA is enabled.                                                                         |
| `twoFactorSecret`   | `string`              | Secret backing the 2FA flow. Store encrypted, never expose.                                     |
| `recoveryTokens`    | `string[]`            | One-time recovery codes.                                                                        |
| `sessions`          | `ISession[]`          | Active and historical sessions for the user.                                                    |
| `accounts`          | `IAccount[]`          | Linked authentication accounts (OAuth, credentials, WebAuthn).                                  |
| `verifications`     | `IVerification[]`     | Pending or completed verification records.                                                      |

### Inherited `IBase` fields

| Field                                     | Type                          | Meaning                                             |
| ----------------------------------------- | ----------------------------- | --------------------------------------------------- |
| `id`                                      | `string`                      | Unique identifier. Required.                        |
| `isLocked` / `lockedAt`                   | `boolean` / `Date`            | Whether the account is locked, and when.            |
| `isBanned` / `bannedAt` / `banReason`     | `boolean` / `Date` / `string` | Ban flag, timestamp, and reason.                    |
| `isBlocked` / `blockedAt` / `blockReason` | `boolean` / `Date` / `string` | Block flag, timestamp, and reason.                  |
| `isPublic`                                | `boolean`                     | Whether the record is publicly visible.             |
| `createdAt` / `updatedAt` / `deletedAt`   | `Date`                        | Lifecycle timestamps (soft-delete via `deletedAt`). |
| `language`                                | `LocaleType`                  | Preferred locale.                                   |

## Related identity interfaces

`IUser` references three companion interfaces, each also extending `IBase`.

* **`ISession`** — an authentication session: `token`, optional `refreshToken`, device and location metadata (`userAgent`, `ipAddress`, `deviceType`, `browser`, `operatingSystem`, `location`), `isActive`, `expiresAt`, and revocation fields (`revokedAt`, `revokedReason`). See [JWT](/security/jwt) for how tokens are minted and validated.
* **`IAccount`** — a linked credential or provider: `type` (an `EAccountType`), a hashed `password` for credentials accounts, OAuth fields (`provider`, `providerAccountId`, `accessToken`, `refreshToken`, `scope`, `idToken`), and provider profile data.
* **`IVerification`** — a verification challenge: `token`, `type` (an `EVerificationType`), optional `code`, `isUsed`, `expiresAt`, and `attemptsCount` / `maxAttempts` for rate limiting.

A separate **`IUserProfileUpdate`** interface audits profile edits, recording `changedFields`, `previousValues` / `newValues`, a `status` (`EProfileUpdateStatus`), and an optional linked `verification`.

## Enums

The package exports three string enums, each with a matching string-literal union type (`AccountType`, `VerificationType`, `ProfileUpdateStatusType`) for use where a plain string is preferred.

| Enum                   | Members                                                                                                                                             | Used by                     |
| ---------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------- |
| `EAccountType`         | `OAUTH` `"oauth"`, `EMAIL` `"email"`, `CREDENTIALS` `"credentials"`, `WEBAUTHN` `"webauthn"`                                                        | `IAccount.type`             |
| `EVerificationType`    | `EMAIL` `"email"`, `PHONE` `"phone"`, `PASSWORD_RESET` `"password_reset"`, `TWO_FACTOR` `"two_factor"`, `ACCOUNT_ACTIVATION` `"account_activation"` | `IVerification.type`        |
| `EProfileUpdateStatus` | `PENDING` `"pending"`, `COMPLETED` `"completed"`, `FAILED` `"failed"`, `REVERTED` `"reverted"`                                                      | `IUserProfileUpdate.status` |

## Resolving a user in auth middleware

An auth middleware validates the incoming token, maps the provider's user into an `IUser`, and assigns it to `context.user`. Optional fields are only set when present, so the resolved user stays minimal:

```typescript theme={null}
import type { IUser } from "@ooneex/user";

const user: IUser = {
  id: providerUser.privateMetadata?.externalId as string,
  externalId: providerUser.id,
  email: primaryEmail.emailAddress,
  roles: (providerUser.privateMetadata?.roles as Uppercase<string>[]) ?? ["ROLE_USER"],
};

if (providerUser.firstName) user.firstName = providerUser.firstName;
if (providerUser.lastName) user.lastName = providerUser.lastName;
if (providerUser.imageUrl) user.avatar = providerUser.imageUrl;

context.user = user; // resolved once; reused for the rest of the request
```

See [Auth](/security/auth) for the full middleware contract and guest-route handling.

## Using the resolved user

Downstream code reads `context.user` rather than re-validating the token. Because it is `IUser | null`, always handle the guest case.

In a controller, scope data to the caller:

```typescript theme={null}
import type { ContextType } from "@ooneex/controller";

public async index(context: ContextType) {
  const user = context.user;

  if (!user) {
    return context.response.exception("Authentication required", { status: 401 });
  }

  return context.response.json({ email: user.email, roles: user.roles });
}
```

In a permission class, `setUserPermissions` reads the user and its `roles` to grant abilities, which `can` / `cannot` then check:

```typescript theme={null}
public setUserPermissions(context: ContextType): this {
  const user = context.user;

  if (user?.roles.includes("ROLE_ADMIN")) {
    this.allow().to("manage", "all");
  } else if (user) {
    this.allow().to("read", "Article");
  }

  return this;
}
```

See [Permissions](/security/permissions) for the full `IPermission` contract and [Roles](/security/roles) for how `roles` strings are defined and compared.

## Best practices

* **Never store plaintext passwords.** `IAccount.password` is the *hashed* credential; hash on write and compare hashes on login. Treat `twoFactorSecret` and `recoveryTokens` as secrets too — encrypt at rest, never serialize to clients.
* **Resolve once, reuse everywhere.** Validate the token and build `IUser` in auth middleware, set `context.user`, and read it downstream. Don't re-decode tokens in controllers or permissions.
* **Keep the identity model minimal.** Populate only the fields you have. Optional fields exist for richer profiles, not as a checklist to fill.
* **Always handle `null`.** The context types the user as `IUser | null`; branch on it explicitly so guest routes are intentional, not accidental.
* **Expose a safe view.** When returning a user over the wire, project to the public fields — never send credentials, secrets, sessions, or verification tokens.
* **Trust `roles`, not ad-hoc flags.** Authorize against the `roles` array via roles and permissions rather than inventing per-controller checks.
