Skip to main content
The @talosjs/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, and 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.

What the contract covers

Every part of the stack (auth, roles, permissions, controllers) speaks the same IUser interface, so a user resolved in one place is usable everywhere. The model is independent of how you authenticate. A Clerk, JWT, or credentials backend maps its own user into IUser, and downstream code never sees the provider. The definitions are pure type, interface, and enum, so importing the package adds nothing to your bundle and forces no storage decisions. Sessions, accounts, verifications, and profile updates each have a dedicated interface with timestamps and status, so security trails are modeled rather than improvised. 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. In @talosjs/controller, the context types the user as nullable so guest-accessible routes are handled explicitly:

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.

Inherited IBase fields

IUser references three companion interfaces, each also extending IBase.
  • ISession is 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 for how tokens are minted and validated.
  • IAccount is 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 is 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.

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:
See 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 explicitly. In a controller, scope data to the caller:
In a permission class, setUserPermissions reads the user and its roles to grant abilities, which can / cannot then check:
See Permissions for the full IPermission contract and Roles for how roles strings are defined and compared.

Handling the user model safely

IAccount.password is the hashed credential, so hash on write and compare hashes on login. Treat twoFactorSecret and recoveryTokens as secrets too: encrypt them at rest and never serialize them to clients. Validate the token and build IUser once in auth middleware, set context.user, and read it downstream rather than re-decoding tokens in controllers or permissions. Populate only the fields you have. The optional fields exist for richer profiles, not as a checklist to fill. The context types the user as IUser | null, so branch on it explicitly to keep guest routes intentional. When returning a user over the wire, project to the public fields and never send credentials, secrets, sessions, or verification tokens. Authorize against the roles array through roles and permissions rather than inventing per-controller flags.