# Implementing Just-In-Time (JIT) Provisioning with Logto and Email Domains

> Implement Just-In-Time JIT provisioning with Logto and email domains. Automatically add users and assign roles on first sign-in using Logto's REST API and configuration.

- Repository: [Logto/logto](https://github.com/logto-io/logto)
- Tags: how-to-guide
- Published: 2026-07-04

---

**Logto's JIT provisioning automatically adds users to organizations and assigns roles on first sign-in when their email domain matches configured rules, using the `organization_jit_email_domains` table and REST API endpoints under `/api/organizations/:id/jit/email-domains`.**

This guide walks through implementing automatic user provisioning in the `logto-io/logto` repository. By configuring email domain rules, you can enable seamless organization onboarding where users with corporate email addresses are instantly granted access without manual invitations.

---

## How JIT Email Domain Provisioning Works

When JIT provisioning is enabled, Logto intercepts the authentication flow to evaluate whether a new or existing user should be auto-assigned to an organization based on their email domain. The flow executes in four stages:

1. **Domain Configuration**: Administrators define allowed email domains (e.g., `example.com`) for specific organizations.
2. **Sign-In Interception**: During the OIDC flow, Logto extracts the domain from the user's email identifier.
3. **Organization Lookup**: The system queries the JIT email domain table to find matching organizations.
4. **Automatic Assignment**: Upon a match, Logto creates the user-organization link and applies default roles before completing the sign-in process.

All logic resides in the **core** package, while the Admin Console provides a management interface for the underlying REST APIs.

---

## Database Schema for JIT Email Domains

The persistence layer uses a dedicated table defined in [`packages/schemas/src/organization.sql`](https://github.com/logto-io/logto/blob/main/packages/schemas/src/organization.sql). The `organization_jit_email_domains` table stores tuples of `(organization_id, email_domain)`, enabling many-to-many relationships between organizations and allowed domains.

```sql
-- Schema location: packages/schemas/src/organization.sql
CREATE TABLE organization_jit_email_domains (
  organization_id VARCHAR(32) NOT NULL,
  email_domain VARCHAR(256) NOT NULL,
  PRIMARY KEY (organization_id, email_domain),
  FOREIGN KEY (organization_id) REFERENCES organizations(id) ON DELETE CASCADE
);

```

This schema ensures that domain rules are automatically cleaned up when an organization is deleted, maintaining referential integrity.

---

## Query Layer Implementation

The `EmailDomainQueries` class in [`packages/core/src/queries/organization/email-domains.ts`](https://github.com/logto-io/logto/blob/main/packages/core/src/queries/organization/email-domains.ts) abstracts database interactions. It exposes methods that the user library and API routes consume:

- **`getJitOrganizations(emailDomain)`**: Retrieves all organizations that accept the specified domain.
- **`insert(organizationId, emailDomain)`**: Adds a new domain rule.
- **`delete(organizationId, emailDomain)`**: Removes a specific domain rule.
- **`replace(organizationId, emailDomains[])`**: Performs bulk replacement of domain rules for an organization.

These methods handle the SQL generation and parameterization, ensuring safe queries against the JIT email domains table.

---

## REST API Endpoints for JIT Management

Logto exposes full CRUD operations under the base path `/api/organizations/:id/jit/email-domains`, implemented in [`packages/core/src/routes/organization/jit/email-domains.ts`](https://github.com/logto-io/logto/blob/main/packages/core/src/routes/organization/jit/email-domains.ts).

| Method | Endpoint | Description |
|--------|----------|-------------|
| `GET` | `/` | Lists configured email domains with pagination support. |
| `POST` | `/` | Adds a single domain via JSON body `{ emailDomain: string }`. |
| `PUT` | `/` | Replaces all domains with the provided array `{ emailDomains: string[] }`. |
| `DELETE` | `/:emailDomain` | Removes a specific domain rule from the organization. |

The router is mounted within the JIT sub-router at [`packages/core/src/routes/organization/jit/index.ts`](https://github.com/logto-io/logto/blob/main/packages/core/src/routes/organization/jit/index.ts), ensuring consistent authentication and authorization middleware.

---

## Sign-In Flow Integration

The critical integration point occurs in [`packages/core/src/libraries/user.ts`](https://github.com/logto-io/logto/blob/main/packages/core/src/libraries/user.ts). During the sign-in process, Logto calls `organizations.jit.emailDomains.getJitOrganizations()` to determine automatic organization membership.

```typescript
// Located in packages/core/src/libraries/user.ts
const userEmailDomain = email.split('@')[1];
const jitOrgs = await organizations.jit.emailDomains.getJitOrganizations(userEmailDomain);

if (jitOrgs.length > 0) {
  // Auto-join logic executes here
  for (const org of jitOrgs) {
    await organizationMemberships.addUserToOrg(user.id, org.id, org.defaultRoles);
  }
}

```

This execution happens transparently within the OIDC flow, ensuring users are provisioned before the session is established.

---

## Configuring JIT via the Admin Console

The Admin Console consumes these APIs to provide a visual interface for JIT configuration. The relevant utilities in [`packages/console/src/pages/OrganizationDetails/utils.ts`](https://github.com/logto-io/logto/blob/main/packages/console/src/pages/OrganizationDetails/utils.ts) handle state management and API data transformation.

Administrators can:
- View and modify allowed email domains.
- Select default roles to assign to JIT-provisioned users.
- Access documentation via links defined in [`packages/console/src/consts/external-links.ts`](https://github.com/logto-io/logto/blob/main/packages/console/src/consts/external-links.ts).

The console ensures that domain validation and role assignment are configured through the same REST endpoints available to programmatic clients.

---

## Implementation Examples

### Adding an Email Domain via API

Use the Management API to programmatically add domains:

```typescript
import fetch from 'node-fetch';

async function addJitDomain(organizationId: string, domain: string, token: string) {
  const response = await fetch(
    `http://localhost:3002/api/organizations/${organizationId}/jit/email-domains`,
    {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        'Authorization': `Bearer ${token}`
      },
      body: JSON.stringify({ emailDomain: domain })
    }
  );
  
  if (!response.ok) {
    throw new Error(`Failed to add domain: ${response.statusText}`);
  }
  
  return response.json();
}

// Usage
await addJitDomain('org_12345', 'example.com', 'YOUR_ADMIN_TOKEN');

```

This corresponds to the `POST` handler in [`packages/core/src/routes/organization/jit/email-domains.ts`](https://github.com/logto-io/logto/blob/main/packages/core/src/routes/organization/jit/email-domains.ts).

### Simulating the Sign-In Check

To verify which organizations a user would join, replicate the library logic:

```typescript
// Simulates packages/core/src/libraries/user.ts logic
async function checkJitEligibility(email: string, queries: any) {
  const domain = email.split('@')[1];
  const matchedOrgs = await queries.jit.emailDomains.getJitOrganizations(domain);
  
  return {
    eligible: matchedOrgs.length > 0,
    organizations: matchedOrgs
  };
}

```

### Removing a Domain Rule

Delete rules using the domain-specific endpoint:

```typescript
async function removeJitDomain(organizationId: string, domain: string, token: string) {
  const encodedDomain = encodeURIComponent(domain);
  await fetch(
    `http://localhost:3002/api/organizations/${organizationId}/jit/email-domains/${encodedDomain}`,
    {
      method: 'DELETE',
      headers: { 'Authorization': `Bearer ${token}` }
    }
  );
}

```

---

## Testing JIT Provisioning

The integration test suite validates end-to-end JIT functionality. Key test files include:

- **[`packages/integration-tests/src/tests/api/organization/organization-jit.test.ts`](https://github.com/logto-io/logto/blob/main/packages/integration-tests/src/tests/api/organization/organization-jit.test.ts)**: Covers CRUD operations for email domains and role assignments.
- **[`packages/integration-tests/src/tests/api/experience-api/register-interaction/organization-jti.test.ts`](https://github.com/logto-io/logto/blob/main/packages/integration-tests/src/tests/api/experience-api/register-interaction/organization-jti.test.ts)**: Verifies that users are automatically joined to organizations during the registration interaction flow.

These tests create organizations, configure JIT rules, simulate sign-ins with matching and non-matching emails, and assert that membership records are created correctly.

---

## Summary

Implementing JIT provisioning with Logto and email domains requires coordination across several layers:

- **Database**: The `organization_jit_email_domains` table stores domain-to-organization mappings.
- **Queries**: `EmailDomainQueries` in [`packages/core/src/queries/organization/email-domains.ts`](https://github.com/logto-io/logto/blob/main/packages/core/src/queries/organization/email-domains.ts) handles data access.
- **API**: REST endpoints in [`packages/core/src/routes/organization/jit/email-domains.ts`](https://github.com/logto-io/logto/blob/main/packages/core/src/routes/organization/jit/email-domains.ts) provide management interfaces.
- **Sign-In Logic**: [`packages/core/src/libraries/user.ts`](https://github.com/logto-io/logto/blob/main/packages/core/src/libraries/user.ts) executes automatic provisioning during authentication.
- **Admin UI**: The Console provides visual management tools backed by these APIs.

This architecture enables zero-touch onboarding for organizations by simply maintaining a list of approved email domains.

---

## Frequently Asked Questions

### What happens if a user's email domain matches multiple organizations?

Logto returns all matching organizations from `getJitOrganizations()` and provisions the user into each one. The user library in [`packages/core/src/libraries/user.ts`](https://github.com/logto-io/logto/blob/main/packages/core/src/libraries/user.ts) iterates through all matches and assigns the respective default roles for each organization.

### Can I use wildcard or subdomain matching for JIT email domains?

Based on the current implementation in [`packages/core/src/queries/organization/email-domains.ts`](https://github.com/logto-io/logto/blob/main/packages/core/src/queries/organization/email-domains.ts), the matching performs exact domain string comparison. To support subdomains like `sub.example.com`, you must explicitly add each domain or implement custom logic in your query layer.

### How do I set default roles for JIT-provisioned users?

Default roles are configured through the Admin Console or Management API alongside email domains. When `getJitOrganizations()` returns matching records, the response includes the organization's default roles, which the user library applies via `organizationMemberships.addUserToOrg()` during the sign-in flow.

### Is JIT provisioning triggered for existing users or only new registrations?

The JIT logic in [`packages/core/src/libraries/user.ts`](https://github.com/logto-io/logto/blob/main/packages/core/src/libraries/user.ts) executes during the sign-in flow regardless of whether the user is new or existing. If an existing user signs in with a newly configured email domain, Logto will automatically create the organization membership at that time, provided one does not already exist.