# How to Manage Logto Users: Complete API and Database Guide

> Learn to manage Logto users effectively using the comprehensive API and database guide. Explore user profiles, passwords, and custom data management with JWT authentication.

- Repository: [Logto/logto](https://github.com/logto-io/logto)
- Tags: api-reference
- Published: 2026-07-03

---

**To manage Logto users, interact with the `/api/me` endpoints authenticated via JWT tokens, which route through [`packages/core/src/routes-me/user.ts`](https://github.com/logto-io/logto/blob/main/packages/core/src/routes-me/user.ts) to perform operations like profile updates, password changes, and custom data management while enforcing security policies through the user library in [`packages/core/src/libraries/user.ts`](https://github.com/logto-io/logto/blob/main/packages/core/src/libraries/user.ts).**

Logto provides a comprehensive user management system built on PostgreSQL that handles everything from profile updates to password policies. As implemented in the `logto-io/logto` repository, user data resides in a dedicated **`users`** table defined in [`packages/schemas/tables/users.sql`](https://github.com/logto-io/logto/blob/main/packages/schemas/tables/users.sql), with all operations funneled through structured libraries and REST routes that enforce identifier uniqueness and security constraints.

## Understanding the Logto User Architecture

Logto organizes user management into four distinct layers that separate concerns between API exposure, business logic, utilities, and data validation. Each layer handles specific responsibilities to ensure secure and consistent user operations.

### Database Schema and Storage

User records are persisted in the **`users`** table, defined in [`packages/schemas/tables/users.sql`](https://github.com/logto-io/logto/blob/main/packages/schemas/tables/users.sql). This schema stores standard fields like `username`, `primaryEmail`, `primaryPhone`, and encrypted password hashes, alongside metadata such as `isSuspended` and custom data JSON blobs.

### Core Architectural Components

The platform implements a layered architecture with the following key components:

- **API Routes** ([`packages/core/src/routes-me/user.ts`](https://github.com/logto-io/logto/blob/main/packages/core/src/routes-me/user.ts)): Exposes public `/me` endpoints for the currently authenticated user, handling GET, PATCH, and password actions.
- **User Library** ([`packages/core/src/libraries/user.ts`](https://github.com/logto-io/logto/blob/main/packages/core/src/libraries/user.ts)): Central business logic for creating users, checking identifier collisions, retrieving roles, and verifying passwords.
- **Utilities** ([`packages/core/src/utils/user.ts`](https://github.com/logto-io/logto/blob/main/packages/core/src/utils/user.ts)): Helper functions for password hashing via `encryptUserPassword`, MFA conversion, and data sanitization.
- **Schemas** ([`packages/core/src/routes-me/user.ts`](https://github.com/logto-io/logto/blob/main/packages/core/src/routes-me/user.ts) guards and [`user.openapi.json`](https://github.com/logto-io/logto/blob/main/user.openapi.json)): Type-safe definitions of user fields and validation guards using Zod.

## Authentication and Request Flow

Every user operation follows a strict five-step pipeline to ensure security and data integrity.

### JWT Extraction and Guard Validation

The router extracts the `userId` from the JWT via `ctx.auth.id`. Requests then pass through `koaGuard` middleware, which validates bodies against Zod schemas using regex patterns like `usernameRegEx` and `emailRegEx` defined in the route guards.

### Business Logic and Policy Enforcement

Before database operations execute, the system performs three critical checks:

1. **Suspended Account Check**: `assertThat(!user.isSuspended, …)` blocks operations on suspended accounts.
2. **Identifier Collision**: `checkIdentifierCollision` prevents duplicate usernames, emails, phone numbers, or external identities.
3. **Password Policy**: `PasswordPolicyChecker` (via `checkPasswordPolicyForUser`) enforces configured password rules against new passwords.

All operations execute inside PostgreSQL transactions when needed—for example, during role assignment on user creation—to guarantee consistency.

## Managing User Profiles via REST API

The `/api/me` endpoints provide self-service user management capabilities for the currently authenticated user.

### Retrieve Current User Profile

To fetch the current user's profile, send an authenticated GET request to `/api/me`:

```typescript
const response = await fetch('/api/me', {
  headers: { Authorization: `Bearer ${accessToken}` },
});
const profile = await response.json();
// Returns: id, username, primaryEmail, name, avatar, hasPassword, ...

```

The implementation in [`packages/core/src/routes-me/user.ts`](https://github.com/logto-io/logto/blob/main/packages/core/src/routes-me/user.ts) returns a trimmed view using `pick(user, ...userInfoSelectFields)` to expose only safe fields.

### Update User Information

Update fields like username, email, name, or avatar via PATCH requests. Note that username updates are available in OSS only, while primary email updates are Cloud only:

```typescript
await fetch('/api/me', {
  method: 'PATCH',
  headers: {
    'Content-Type': 'application/json',
    Authorization: `Bearer ${accessToken}`,
  },
  body: JSON.stringify({
    username: 'new-name',          // OSS only
    primaryEmail: 'new@example.com', // Cloud only
    name: 'Jane Doe',
    avatar: 'https://example.com/avatar.png',
  }),
});

```

The route validates the payload using `router.patch('/', koaGuard({ body: … }).partial())`, performs collision checks via `checkIdentifierCollision`, and delegates to `updateUserById` for the database transaction.

## Handling Custom User Data

Custom data stores arbitrary JSON blobs associated with the user account. Retrieve custom data with a GET request:

```typescript
const { customData } = await (await fetch('/api/me/custom-data', {
  headers: { Authorization: `Bearer ${accessToken}` },
})).json();

```

Update custom data using a PATCH request:

```typescript
await fetch('/api/me/custom-data', {
  method: 'PATCH',
  headers: { 
    'Content-Type': 'application/json', 
    Authorization: `Bearer ${accessToken}` 
  },
  body: JSON.stringify({ theme: 'dark', language: 'en' }),
});

```

Both endpoints guard against suspended accounts and use `updateUserById` to persist the JSON blob.

## Secure Password Operations

Password management follows strict verification flows to prevent unauthorized changes.

### Verify User Password

Before sensitive actions, verify the current password against the stored hash:

```typescript
await fetch('/api/me/password/verify', {
  method: 'POST',
  headers: { 
    'Content-Type': 'application/json', 
    Authorization: `Bearer ${accessToken}` 
  },
  body: JSON.stringify({ password: 'current-pwd' }),
});

```

This calls `verifyUserPassword` which delegates to `userPasswordVerification.isPasswordValid` and updates the verification status accordingly.

### Change Password

To set a new password, send a POST request with the new value:

```typescript
await fetch('/api/me/password', {
  method: 'POST',
  headers: { 
    'Content-Type': 'application/json', 
    Authorization: `Bearer ${accessToken}` 
  },
  body: JSON.stringify({ password: 'NewStrong!123' }),
});

```

The implementation validates the new password against the active policy using `PasswordPolicyChecker`, then encrypts and stores it via `buildUserPasswordPayloadFromPassword` and `updateUserById`.

## Summary

- Logto stores user data in the **`users`** table defined in [`packages/schemas/tables/users.sql`](https://github.com/logto-io/logto/blob/main/packages/schemas/tables/users.sql).
- All user operations route through **[`packages/core/src/routes-me/user.ts`](https://github.com/logto-io/logto/blob/main/packages/core/src/routes-me/user.ts)** and delegate to **[`packages/core/src/libraries/user.ts`](https://github.com/logto-io/logto/blob/main/packages/core/src/libraries/user.ts)**.
- Authentication relies on JWT tokens with `userId` extracted from `ctx.auth.id`.
- Security checks include suspended account validation, identifier collision detection via `checkIdentifierCollision`, and password policy enforcement.
- The `/api/me` endpoints support retrieving profiles, updating identifiers, managing custom JSON data, and secure password operations using `verifyUserPassword` and `encryptUserPassword`.

## Frequently Asked Questions

### What database table stores Logto user information?

User data resides in the **`users`** table, defined in [`packages/schemas/tables/users.sql`](https://github.com/logto-io/logto/blob/main/packages/schemas/tables/users.sql). This table includes fields for identifiers (username, email, phone), encrypted passwords, suspension status, and custom data JSON blobs.

### How does Logto prevent duplicate usernames or emails?

The system uses the `checkIdentifierCollision` function in [`packages/core/src/libraries/user.ts`](https://github.com/logto-io/logto/blob/main/packages/core/src/libraries/user.ts) to verify uniqueness before creating or updating users. This check prevents duplicate usernames, emails, phone numbers, or external identities across the tenant.

### Where is the password policy enforcement implemented?

Password policies are enforced via `PasswordPolicyChecker` in the user library. When changing passwords through `/api/me/password`, the system validates the new password against configured rules before encryption via `buildUserPasswordPayloadFromPassword` and storage via `updateUserById`.

### Can I extend user data with custom fields?

Yes. Logto supports custom user data through the `/api/me/custom-data` endpoints. Store arbitrary JSON blobs using PATCH requests, and retrieve them via GET requests. Both operations validate that the account is not suspended before updating the database through `updateUserById`.