# Introduction

Domain Verification allows your customers to claim ownership of a domain. Once they have claimed ownership, features that require a higher level of trust and security can be activated.

WorkOS Domain Verification provides a self-serve flow through the Admin Portal in which IT admins can prove ownership through the creation of DNS TXT records.

## Before getting started

You’ll need a [WorkOS account](https://dashboard.workos.com/).

### API object definitions

[Organization](/reference/organization)
: Describes an organization whose users sign in with a SSO Connection, or whose users are synced with a Directory Sync Connection.

[Organization Domain](/reference/organization-domain)
: Describes a domain associated to an organization, verified or unverified.

[Portal Link](/reference/admin-portal/portal-link)
: A temporary link to initiate an Admin Portal session. Valid for 5 minutes.

All domains belong to an [Organization](/reference/organization). In order to create and verify a domain through the Admin Portal, an Organization must first be [created](/reference/organization/create).

## (A) Setup link from the WorkOS dashboard

- Sign in to your [WorksOS dashboard](https://dashboard.workos.com/) account and create or locate an Organization.
- Click the “Invite Admin” button, select **Domain Verification** then click “Next." Enter the email of the IT admin for the organization to automatically send them a setup link, or click "Copy setup link".

If you chose to copy the setup link you can share it over email, Slack or direct message. We also recommend including details on what the link does and how long the link is active.

![A screenshot showing the workOS dashboard admin invite.](https://images.workoscdn.com/images/c9196bbf-3860-4a9a-be8c-83b503ae4e3d.png?auto=format&fit=clip&q=80)

## (B) Integrate with your app

Admin Portal links can also be programmatically generated for the domain verification flow. This can be used to provide a link to the Admin Portal flow directly in your application.

You’ll have to generate the link with the `domain_verification` intent:



Please refer to the [Admin Portal Integration Guide](/admin-portal/b-integrate-with-your-app) for additional integration details.

---

## Admin Portal domain verification

After receiving the invitation and clicking on the setup link, the organization's admin is prompted to enter the domain they wish to verify.

![A screenshot show the Admin portal domain entry form.](https://images.workoscdn.com/images/e7e719d6-579b-4567-8c80-772bc2f77563.png?auto=format&fit=clip&q=80)

If the domain is valid, we identify the DNS service provider and offer custom setup instructions.

The admin will find instruction to add a DNS TXT record with a token generated by our system.

![A screenshot showing the Admin Portal domain DNS instructions.](https://images.workoscdn.com/images/9ab7e2a4-8b11-4a03-8203-d95d1c1abb07.png?auto=format&fit=clip&q=80)

When we detect and verify the DNS record, we will mark the domain as `verified` and dispatch a [domain verification event](/events) to inform your application.

### API

Programmatic domain verification

Instead of leveraging the Admin Portal, the Domain Verification API can be used to verify domains programmatically.

Integrating with the API goes as follows:

1. Create an organization domain for an organization
2. Share the token and setup instructions with the organization owner (IT admin)
3. Wait for the verification to complete

## Create a new organization domain

All domains belong to an [organization](/reference/organization). In order to create and verify a domain, an organization must first be [created](/reference/organization/create).



The `verification_token` returned can then be set as the value of a TXT record that WorkOS will periodically check until the record is found. The TXT record for the above response example would be:

- Name: `domain-to-verify.com`
- Value: `verification_token=3CVZxo4HgvSiYRKlV4RdOWwWl`

## Get a domain

Fetch an existing domain and it’s current verification status. This endpoint can be polled once verification has been initiated to determine if verification has been successful.



Possible `state` values:

- `pending`: domain verification has been initiated and not yet completed
- `verified`: domain has been verified
- `failed`: domain was not able to be verified

Possible `verification_strategy` values:

- `dns`: domain is verified with the DNS flow
- `developer`: domain is verified by a person or a system, without running the DNS flow

## Initiate verification for existing domain

If a domain has not successfully verified within thirty days and moves to the `failed` state, verification can be restarted manually.


## Directory Sync {#directory-sync}

### Understanding the Events Lifecycle

Understand the lifecycle of the events that occur in Directory Sync.

## Introduction

Directory Sync events represent actions performed within directory providers. For example, an action could mean an IT admin assigning a user to your app or modifying a user group assigned to your app. These actions form the basis of user lifecycle management (ULM).

WorkOS provides information about these actions through a set of structured events. This reference guide will cover the events Directory Sync produces and what they mean. To learn about how to handle these events on your side, see the [data syncing guide](/events/data-syncing).

---

## Directory events

### `dsync.activated`

This event occurs when you or your customer have successfully created a connection between WorkOS and your customer’s directory provider.

- | Lifecycle

  

- | Payload

  

`dsync.activated` is triggered if you manually create the directory connection in the [Developer Dashboard](https://dashboard.workos.com/), or your customer sets the connection up using the [Admin Portal](/admin-portal).

The directory ID identifies a connection with the directory of a particular customer. Your app should save it and associate the directory ID with the corresponding organization ID.

### `dsync.deleted`

This event occurs when a Directory Sync connection is deleted in WorkOS, thus tearing down the link between your customer’s directory provider and your app.

- | Lifecycle

  

- | Payload

  

A connection can be deleted through the [Admin Portal](/admin-portal), [Developer Dashboard](https://dashboard.workos.com), or [WorkOS API](/reference/directory-sync/directory/delete). At this point your app should remove the association between the corresponding organization and its directory, as it no longer exists. Directories, users, and groups are typically deleted if your app offboards a customer altogether.

When receiving a `dsync.deleted` event, you can ignore the connection’s `state` attribute, since it indicates the state before the deletion occurs. When a directory is deleted in WorkOS, a sole `dsync.deleted` event is sent.

When a `dsync.deleted` event is received, it indicates that the users and groups in that directory have been deleted in WorkOS. You can process the `dsync.deleted` event accordingly in your application, removing the organization’s groups and its users from your application or marking them as deleted. `dsync.user.deleted` and `dsync.group.deleted` events will not be sent for the deleted directory.

---

## Directory user events

### `dsync.user.created`

This event occurs when an IT admin creates a user using their directory provider. It is standard to create and provision the user in your app when you receive this event.

- | Lifecycle

  

- | Payload

  

You can add this user to your users table in your app and associate them with the directory ID and organization ID. You can begin to engage with the user at this point, e.g., send the user a “Getting Started” email.

During the initial sync of any directory, you will receive a `dsync.user.created` event for each existing user in the directory.

### `dsync.user.updated`

This event occurs when users’ attributes change. These attributes may be [standard attributes](/directory-sync/attributes/standard-attributes), [auto-mapped attributes](/directory-sync/attributes/custom-attributes/auto-mapped-attributes), or [custom-mapped attributes](/directory-sync/attributes/custom-attributes/custom-mapped-attributes).

- | Lifecycle

  

- | Payload

  

The payload for `dsync.user.updated` event shows changes between directory group snapshots in the `previous_attributes` property.

The changes in the object are shallow differences for root properties, `raw_attributes`, and `custom_attributes`. If the current snapshot has a new attribute that did not exist previously, then the value for the attribute will be indicated as `null`.

### `dsync.user.deleted`

This event occurs when a user is hard-deleted from a directory. Typically, you would remove the user from your app in this case.

- | Lifecycle

  

- | Payload

  

When users are removed from a directory, most providers will use a form of soft user deletion. In these cases, rather than receiving a `dsync.user.deleted` event, you will receive a `dsync.user.updated` event with the user’s `state` marked as `inactive`.

> After Oct. 19, 2023, all new environments will delete Directory Users that get moved to the "inactive" state. If you would like to retain these users, please reach out to support. You can find [more details here](/directory-sync/handle-inactive-users).

---

## Directory group events

### `dsync.group.created`

This event occurs when creating a directory group in the directory provider. WorkOS also sends this event when a directory connection is established.

- | Lifecycle

  

- | Payload

  

When WorkOS ingests this event, it first processes the users in the group. So, in most cases, you would receive `dsync.user.created`, then `dsync.group.created`, and finally, `dsync.group.user_added`.

For more information on best practices for out-of-sequence events, see the [data syncing guide](/events/data-syncing).

### `dsync.group.updated`

This event is sent when an attribute of a directory group has changed.

- | Lifecycle

  

- | Payload

  

The payload for `dsync.group.updated` events shows changes between directory group snapshots in the `previous_attributes` property.

The changes in the object are shallow differences for root properties, `raw_attributes`, and `custom_attributes`. If the current snapshot has a new attribute that did not exist previously, then the value for the attribute will be indicated as `null`.

### `dsync.group.deleted`

This event occurs when deleting a directory group in the directory provider.

When a `dsync.group.deleted` event is received, it indicates that the members in that group have been deleted in WorkOS. You can process the `dsync.group.deleted` event accordingly in your application, removing the group's members from your application or marking them as deleted. `dsync.group.user_removed` events will not be sent for the members in the deleted group.

- | Lifecycle

  

- | Payload

  

If your app relies on groups to sync users or map roles, you should remove access for the users who belonged to the deleted group.

### `dsync.group.user_added`

This event occurs when adding a directory user to a directory group.

- | Lifecycle

  

- | Payload

  

If you map roles using groups, you should assign the group’s role to the newly added user.

### `dsync.group.user_removed`

This event occurs when removing a directory user from a directory group.

- | Lifecycle

  

- | Payload

  

If you map roles using groups, you should remove the group’s role from the user who belonged to the group.

---

## Data reconciliation techniques

### With the WorkOS state API

The WorkOS API allows for data reconciliation for your app. You can use the WorkOS API to pull the latest data to reconcile any data discrepancies between WorkOS and your app.

A standard method apps use for data reconciliation is to set up a cron job that pulls from the WorkOS API on a consistent interval, e.g., every 1 to 6 hours, depending on your app’s user provisioning volume.

> **Known issue:** Keeping track of WorkOS updated timestamps is of limited use right now because group membership changes for users do not alter the WorkOS `updated_at` timestamp. We're actively working on this issue.

The general approach for performing a full sync of Directory Sync objects goes as follows:

1. Traverse all directory groups and update all local objects.
2. Traverse all directory users and update all local objects.
3. Extract group membership information from each user. Compare with local membership state. Add and remove memberships accordingly.
4. Compare the list of local users to all users seen in WorkOS traversal. Deactivate any users that exist locally but not on WorkOS.
5. Compare the list of local groups to all groups seen on WorkOS traversal. Deactivate any groups that exist locally but not on WorkOS.

### With the events API

You can also reconcile directory data using the events API. See our [data syncing guide](/events/data-syncing/data-reconciliation) to learn more.

### Quick Start

Set up a directory, install the SDK, and integrate Directory Sync.

## What you’ll build

In this guide, we’ll take you from learning about Directory Sync and POC-ing all the way through to building production-ready features fully integrated with the WorkOS Directory Sync API.

This guide will show you how to:

1. Create a new directory in the WorkOS Dashboard
2. Add Directory Sync to your app and fetch directory resources
3. Use events to keep your app in sync with the directory changes

## Before getting started

To get the most out of this guide, you’ll need:

- A [WorkOS account](https://dashboard.workos.com/)
- A directory from a directory provider that WorkOS supports

## API object definitions

[Directory](/reference/directory-sync/directory)
: Stores info about an organization’s user management system (i.e. directory provider).

[Directory user](/reference/directory-sync/directory-user)
: Represents an organization user that is active in an organization’s directory provider.

[Directory group](/reference/directory-sync/directory-group)
: A collection of organization users within a directory, e.g. IT, database admins, HR.

> The WorkOS Directory Sync API exclusively uses read-only operations. We never mutate end-user directories.

---

## (1) Create a new directory connection

The first step to connecting with a directory is creating an organization in the [WorkOS Dashboard](https://dashboard.workos.com/). You will then be able to create a new [connection](/glossary/connection) to the organization’s directory. Let’s start by creating one for development in your sandbox environment

Get provider-specific instructions by selecting the directory provider you want to test:



> You can view and copy the unique identifier for the directory connection on the directory page, once it has been set up. The id takes the form `directory_*`.

---

## (2) Add Directory Sync to your app

Let’s integrate the Directory Sync API into your app to enable fetching directory resources programmatically.

### Install the WorkOS SDK

WorkOS offers native SDKs in several popular programming languages. Choose a language below to see instructions in your application’s language.



### Set secrets

To make calls to WorkOS, provide the API key and, in some cases, the client ID. Store these values as managed secrets, such as `WORKOS_API_KEY` and `WORKOS_CLIENT_ID`, and pass them to the SDKs either as environment variables or directly in your app's configuration based on your preferences.

```plain title="Environment variables"
WORKOS_API_KEY='sk_example_123456789'
WORKOS_CLIENT_ID='client_123456789'
```

> The code examples use your staging API keys when [signed in](https://dashboard.workos.com).

### Fetch directory resources

Get the details of an existing directory user.

Example use case: pre-populate user attributes for new user accounts.



#### List directory users

Get directory users for a given directory or directory group.

Example use case: Build an onboarding experience that allows an admin to select who to invite and create accounts for.



> Use the optional `limit`, `before`, and `after` parameters to paginate through results. See the [API Reference](/reference/pagination) for details.

#### Get directory group

Get the details of an existing directory group.

Example use case: Pre-populate team attributes for new organizations.



#### List directory groups

Get directory groups for a given directory or directory user.

Example use case: Build an onboarding experience that allows an admin to select which groups of employees to invite and create accounts for.



> Use the optional `limit`, `before`, and `after` parameters to paginate through results. See the [API Reference](/reference/pagination) for details.

---

## (3) Handle directory events

Actions performed in a WorkOS environment are represented by events. These can occur as a result of user-related actions, manually via the WorkOS dashboard, or via API calls. To keep your app in sync with the latest directory data, follow the corresponding guides:

- We recommend using our [events API](/events/data-syncing/events-api) to sync data to your application. To learn more about other ways to sync data, see the [data syncing guide](/events/data-syncing).
- Learn about the different types of events that you can receive. See [event types](/events).
- Understand how directory events work. See the [understanding events guide](/directory-sync/understanding-events).
- Optionally, stream events to Datadog. See the [observability guide](/events/observability/datadog).

### Directory Sync

Build frictionless onboarding for organizations with real‑time user provisioning and deprovisioning.

## Introduction

Organizations use company directories and HRIS systems to manage users and enforce their access to organization resources. Directories enable IT admins to activate and deactivate accounts, create groups that inform access rules, accelerate adoption of new tools, and more.

## Definitions

**ULM**
: User Lifecycle Management (or ULM) is the process of managing a user’s access to an app. This occurs from app onboarding until they are removed from an app. ULM is also commonly referred to as identity provisioning.

**SCIM**
: System for Cross-domain Identity Management (or SCIM) is an open standard for managing automated user and group provisioning. It’s a standard that many directory providers interface with.

**HRIS**
: A Human Resources Information System (or HRIS) is software designed to maintain, manage, and process detailed employee information and human resources-related policies. Examples include: Workday, HiBob, BambooHR, etc.

**User Provisioning**
: Provisioning is the process of creating a user and setting attributes for them – inside of an app.

**User Deprovisioning**
: Deprovisioning is the process of removing a user from an app.

## What is Directory Sync?

Directory Sync is a set of developer-friendly APIs and IT admin tools that allows you to implement enterprise-grade User Lifecycle Management (ULM) into your existing app.

ULM allows IT admins to centrally provision and deprovision users from their directory provider. A directory provider is the source of truth for your enterprise customer’s user and group lists. Directory Sync sends automatic updates to your app for changes to directories, groups, users, or access rules.

Common directory providers include: [Microsoft Active Directory](/integrations/microsoft-ad-fs-saml), [Okta](/integrations/okta-scim), [Workday](/integrations/workday), and [Google Workspace](/integrations/google-directory-sync). See the full list of supported directory providers on the [integrations](/integrations) page.

## Why use Directory Sync?

ULM increases the security of your app and makes it easier for your customers to use your app. ULM is most often implemented using [SCIM](/glossary/scim). SCIM requests are sent between directory providers and your app to inform you of changes to a user’s identity. Changes can include:

- Provisioning an identity for a user (account creation)
- When a user’s attribute has changed (account update)
- Deprovisioning a user from your app (account deletion)

Each directory provider implements SCIM differently. Implementing SCIM is often a challenging process and can introduce security vulnerabilities into your app. Directory Sync hides this complexity, so you can focus on building core product features in your app.

## What your customer experiences

Let’s take a look at two different user provisioning scenarios.

### (N) Your app doesn’t use Directory Sync

Without ULM, your customers have to manually add, update, and remove users from your app.

Imagine a scenario where your customer has purchased your software and onboards a new employee to your app. Your customer would have to do the following:

1. The IT admin provisions the employee in their directory provider (_if they use one_) and manually in your app.
2. All employee information has to be set manually in both the directory provider and your app.
3. The IT admin has to manually provision a login method for the employee; through either SSO (_if they use an identity provider_) or a self-registration page.
4. The IT admin sends the invite link to their employee. Often initiating a back and forth via either email, messaging app, or IT helpdesk ticket.
5. The employee has to proceed with the registration method and can then use your app.

All future changes to this employee’s data and access are manually entered by the IT admin. This is error prone and can lead to security vulnerabilities where users get unauthorized access to resources.

As your customers adopt more cloud software, these manual processes do not scale well. Manual input error can lead to the source of truth (directory) drifting from your app’s state. As a result, ULM has become a table stakes product requirement for enterprises.

### (Y) Your app uses Directory Sync

If your app supports ULM via Directory Sync, the IT admin can provision this employee from one place:

1. Add the employee to their directory provider.
2. Assign the employee to your app with the appropriate role once; via the directory provider admin page.
3. **Optional.** Have the employee go through a password setup if they are not using an identity provider (SSO).

Directory Sync makes this integration easy by providing APIs your app interfaces with. All updates for this directory will automatically be sent to your app from WorkOS.

---

## API overview

[Directory](/reference/directory-sync/directory), [directory group](/reference/directory-sync/directory-group), and [directory user](/reference/directory-sync/directory-user) are the main components your app interfaces with.

### Directory



A directory is the source of truth for your customer’s user and group lists.

WorkOS supports dozens of integrations including SCIM. Directory updates are delivered to you via webhooks. Your app stores a mapping between your customer and their directory. This allows you to maintain your app in sync with the directory provider used by your customer.

You can enable self-service Directory Sync setup for your customers using the [Admin Portal](/admin-portal).

### Directory group



A directory group is a collection of users within an organization who have been provisioned with access to your app.

Directory groups are mapped from directory provider groups. Directory groups are most often used to categorize a collection of users based on shared traits. i.e. Grouping software developers at a company under an “Engineering” group.

### Directory user



A directory user is a person or entity within an organization who has been provisioned with access to your app.

Users can belong to multiple directory groups. Users have [attributes](/directory-sync/attributes) associated with them. These attributes can be configured for your app’s needs.

### Identity Provider Role Assignment

Learn how to map role data from identity providers to roles in your app with Directory Sync.

## Introduction

A role represents a logical grouping of permissions, defining access control levels for users within your application. Roles are identified by a unique, immutable slug and are assigned to Directory Sync [users](/directory-sync/api-overview/directory-user) through their group memberships. These role assignments can be configured on the WorkOS dashboard.

To utilize Identity Provider (IdP) role assignment, you must first [configure roles](/rbac/configuration).

## Directory group role assignment

Users are assigned to groups via the identity provider. Groups usually correspond to roles in your app. Therefore, IT admins will often map a group one-to-one to a role. This can be defined within the WorkOS dashboard or Admin Portal for your application to receive automatic role updates.

> Only supported in directories using SCIM-based or Google Workspace providers.

### Sample scenario

Consider the fictional SaaS company _HireOS_. _HireOS_ has integrated Directory Sync and supports group-based role assignment. For example, a _HireOS_ customer would like to assign their engineering team to it. The customer’s IT admin would take the following steps:

1. Create a group “Engineering” using their identity provider.
2. Push the group to _HireOS_ via the identity provider. This is configured in the identity provider admin console.

The developer on the WorkOS dashboard can then assign users of that group to the role "Developer".

1. Navigate to the directory page on the WorkOS dashboard.

![The directory page on the WorkOS dashboard](https://images.workoscdn.com/images/88d4701b-7e6d-4655-ad74-48bc7c14959f.png?auto=format&fit=clip&q=50)

2. Create an assignment for “Engineering" to the "Developer" role.

![The role assignment dialog on the WorkOS dashboard](https://images.workoscdn.com/images/bdd4cc91-11cb-49b1-858b-f2509ce3675e.png?auto=format&fit=clip&q=50)

From this point on, all new users added to “Engineering" will be given "Developer” role from the WorkOS API. The role will be in the [directory user response](/reference/directory-sync/directory-user).

![Screenshot of directory user page](https://images.workoscdn.com/images/18f584f6-d758-4f11-bfc8-e93c16452580.png?auto=format&fit=clip&q=50)

### Multiple roles

When [multiple roles is enabled](/rbac/configuration/configure-roles/multiple-roles) in your environment, directory users can be assigned multiple roles from their identity provider group memberships. If a user belongs to multiple mapped groups, they will receive all corresponding roles.

For example, if a user is a member of both "Engineering" and "Design" groups, and both groups are mapped to roles, the directory user will receive both the "Developer" and "Designer" roles. If a user is not a member of any groups with explicit mappings, they will receive the [default role](/rbac/configuration).

When using [AuthKit with Directory Provisioning](/authkit/directory-provisioning), these multiple roles are automatically applied to the user's [organization membership](/reference/authkit/organization-membership) and reflected in their [session token](/authkit/sessions/integrating-sessions/access-token).

#### Use cases

By default, multiple roles is disabled and users can only have a single role per entity. It's recommended to start with a single-role setup for simplicity, where it's easier to maintain consistent and correct access patterns.

You might want to enable multiple roles when you need:

- **Cross-department collaboration**: e.g., designers who need some engineering permissions.
- **Additive, disjoint permissions**: independent permission sets that should stack.
- **Temporary access**: grant time-bound extra capabilities without creating hybrid roles.

### Role assignment in Admin Portal

Once [roles](/rbac/configuration) are configured for your application, enable directory group role assignment in [Admin Portal](/admin-portal) to allow IT admins to assign roles to groups during directory setup.

![Enable directory group role assignment dashboard setting](https://images.workoscdn.com/images/fe19e3ac-6370-404e-9590-cdb06b3de127.png?auto=format&fit=clip&q=50)

This is an environment-level setting, but can be configured per organization via the _Roles_ tab under an organization in the WorkOS Dashboard. If your application is integrated with Directory Sync, it is recommended to use directory group role assignment as the environment default.

![Screenshot of admin portal with role assignment step](https://images.workoscdn.com/images/74d09b62-b2b2-4aba-9803-3ec6305897e0.png?auto=format&fit=clip&q=50)

If enabled, all Admin Portal sessions for SCIM-based or Google Workspace directories will have the ability to see and assign roles to identity provider groups.

## Other forms of role assignment

Your customers will store role information in different forms, depending on their preferred provisioning workflow. WorkOS allows for flexibility in how you source role data, though these formats are not automated today and not available on the role property on the [directory user response](/reference/directory-sync/directory-user). You can fetch role data via two distinct mechanisms:

- A custom-mapped role attribute from the directory user profile.
- A groups attribute in the SSO user profile.

The type of mechanism needed will depend on the level of support for roles in your app, your app’s architecture, and your customer’s workflows:

| Approach                        | Your app                                               | Your customer                                                      |
| ------------------------------- | ------------------------------------------------------ | ------------------------------------------------------------------ |
| SSO group role assignment       | Receives role data each time a user logs in            | Uses identity provider groups to assign roles in your app          |
| Attribute-based role assignment | Sets roles based on a per-user custom-mapped attribute | Assigns roles using attributes on users in their identity provider |

### SSO group role assignment

[SSO group role assignment](/sso/identity-provider-role-assignment/sso-group-role-assignment) involves mapping identity provider (IdP) groups to roles within your application during [Single Sign-On](/sso) and [JIT Provisioning](/sso/jit-provisioning). In this method, SSO groups corresponding to IdP groups are defined in the WorkOS Dashboard, and roles are assigned based on these group memberships.

The user’s role is then included in the [SSO profile](/reference/sso/profile) returned from WorkOS. [Read more](/sso/identity-provider-role-assignment/considerations/drawbacks) on this approach, including [drawbacks](/sso/identity-provider-role-assignment/considerations/drawbacks) to consider.

### Attribute-based role

You can use [custom-mapped attributes](/directory-sync/attributes/custom-attributes/custom-attributes) if your customers do not use groups to establish and manage user roles.

You can create a custom-mapped attribute role (e.g., `myRole`) in the [WorkOS Dashboard](https://dashboard.workos.com/) under Configuration → Directory Sync. You can set the status of a role attribute to “Required” or “Optional”.

```json language="json" title="Directory user with a custom-mapped attribute"
{
  "id": "directory_user_01E1X7B89OH8Z3SXFJR4H7RGX7",
  "idp_id": "821991",
  "first_name": "Jane",
  "last_name": "Doe",
  "email": "jane@example.com",
  "state": "active",
  "created_at": "2021-06-25T19:07:33.155Z",
  "updated_at": "2021-06-25T19:07:33.155Z",
  "object": "directory_user",
  "directory_id": "directory_01E1X194NTJ3PXMAY79DYV0F0P",
  "organization_id": "org_01EHWNCE74X7JSDV0X3SZ3PJNY",
  "custom_attributes": {
    "myRole": "admin"
  },
  "raw_attributes": {}
}
```

The newly created attribute will appear as a field in the [Admin Portal](/admin-portal). When setting up Directory Sync with their identity provider in Admin Portal, your customers can map this role field to a field in their identity provider. You’ll have to communicate with your customer what value(s) you expect in the custom-mapped attribute.

An example being that `myRole` should be one of `"admin"`, `"viewer"`, or `"editor"`. This allows your app to parse the `myRole` field value correctly.

## Common edge cases

### A user is part of multiple groups

Having a user who belongs to multiple groups is a common scenario. For example, there might be a case where an employee _Jane_ is an _Engineering Manager_ and belongs to an “Engineering”, “Manager”, and “Admin” group. With group-based role assignment, the user will be assigned the role that has the [highest priority defined](/rbac/configuration/configure-roles/priority-order).

### Role assignment availability on Directory Sync

Identity provider role assignment through groups is only available through SCIM compliant and Google Workspace directories.

### Handle Inactive Users

Learn why inactive users are deleted from directories by default and how to configure this behavior.

## Introduction

Traditionally, user provisioning involves the ingestion of user information from various providers (either through SCIM or non-SCIM integrations). This process typically includes categorizing users into states such as `active`, or `inactive` as provided by the IdP data source.

However, the challenge arises when businesses need to handle these `inactive` users differently based on their unique operational and security requirements. Some developers may prefer a security-first approach, automatically deleting these users to enhance data security, while others may opt to retain this information for reactivation processes or comprehensive directory management.

---

## Configuration

To provide improved security and customizability, you can choose how `inactive` users are handled during the provisioning process. Here is an overview of the two options available:

### Secure flow (default)

By selecting this option, customers can opt for a security-focused workflow. Any user marked as `inactive` will be automatically deleted from the directory, resulting in cleaner and potentially more secure data. This approach reduces the data footprint and minimizes potential security risks associated with unused accounts.

### Custom management flow

Alternatively, customers can choose to maintain the existing flow, keeping the `inactive` users in the directory. This approach supports reactivation processes and ensures a comprehensive view of the directory, allowing for easier reintegration of users when needed.

> Contact [customer support](mailto:support@workos.com) to enable custom management flow for your environment.

![A breakdown of the different events for each configuration path on handling inactive users.](https://images.workoscdn.com/images/2304739c-17e9-4521-96d2-81fa3fa83110.png?auto=format&fit=clip&q=80)

## Weighing the tradeoffs

Both options offer distinct advantages, and the right choice depends on your organization's unique needs and security posture:

### Security vs. flexibility

The automatic deletion option prioritizes data security by minimizing the data footprint, while the customized management option provides flexibility for reactivation flows and comprehensive directory oversight.

### Compliance and regulations

Depending on industry regulations and compliance requirements, one option may align better with your organization's obligations.

### Operational efficiency

Consider how each option impacts operational efficiency using WorkOS to handle a set of the computation for you.

### Reactivation

If a user is temporarily removed, does the same user information need to be retained when they return? A new directory user will be created by default on return, whereas using the `inactive` user will retain the same information on reactivation.

### Example Apps

View sample Directory Sync apps for each SDK.

You can view minimal example apps that demonstrate how to use the WorkOS SDKs to power Directory Sync:


  
  
  
  
  
  
  
  
  
</ExampleApps.Root>

### User Attributes

Configure how attributes map from directory providers to Directory Users.

## Introduction

WorkOS can automatically find and normalize most common attributes from directory providers into the [Directory User](/reference/directory-sync/directory-user) object, which represents an enterprise user. More unique cases can be mapped by your customers admins.

- ### Directory User object

  Here is an example Directory User. The data stored varies per directory provider and may include attributes such as photo URLs, pay groups, supervisors, etc.

  ```json language="json" title="Directory User"
  {
    "id": "directory_user_01E1X7B89OH8Z3SXFJR4H7RGX7",
    "idp_id": "821991",
    "first_name": "Jane",
    "last_name": "Doe",
    "email": "jane@example.com",
    "state": "active",
    "created_at": "2021-06-25T19:07:33.155Z",
    "updated_at": "2021-06-25T19:07:33.155Z",
    "object": "directory_user",
    "directory_id": "directory_01E1X194NTJ3PXMAY79DYV0F0P",
    "organization_id": "org_01EHWNCE74X7JSDV0X3SZ3PJNY",
    "custom_attributes": {
      "emails": [
        {
          "type": "work",
          "value": "jane@example.com",
          "primary": true
        }
      ],
      "employee_type": "Full Time",
      "employment_start_date": "2021-06-27T12:00:00.000Z",
      "department_name": "Engineering",
      "manager_email": "john@example.com",
      "division_name": "Analytics",
      "cost_center_name": "IT",
      "job_title": "Software Engineer",
      "addresses": [
        {
          "type": "work",
          "street_address": "101 123rd Ave",
          "locality": "Brooklyn",
          "region": "New York",
          "postal_code": "12345",
          "country": "USA",
          "raw_address": "101 123rd Ave, Brooklyn,  New York, 12345, USA",
          "primary": true
        },
        {
          "type": "home",
          "street_address": "102 W 321st St",
          "locality": "Brooklyn",
          "region": "New York",
          "postal_code": "54321",
          "country": "USA",
          "raw_address": "102 W 321st St, Brooklyn,  New York, 54321, USA",
          "primary": false
        }
      ],
      "username": "jane@example.com",
      "my_new_key": ""
    },
    "raw_attributes": {
      "name": {
        "givenName": "Jane",
        "familyName": "Doe"
      },
      "active": true,
      "emails": [
        {
          "type": "work",
          "value": "jane@example.com",
          "primary": true
        }
      ],
      "groups": [],
      "locale": "en_US",
      "schemas": ["urn:directory:schemas:core:1.0"],
      "password": "redacted",
      "userName": "jane@example.com",
      "externalId": "821991",
      "displayName": "Jane Doe"
    }
  }
  ```

In this guide, we’ll explain how to map data from directory providers to the Directory Users.

## Definitions

**Standard attributes**
: The most common user information, normalized across providers.

**Predefined attributes**
: Detailed user attributes for specific use cases, normalized across providers. You can opt-in to each attribute you'd like auto-mapped.

**Custom attributes**
: For unique cases, you can create custom attributes your customers can map when setting up a directory.

## Standard attributes

Every Directory User comes with the following standard attributes. These are the core set of attributes that are common across all identity providers. These are structured fields with a guaranteed schema in the top-level Directory User payload.

| Attribute    | Type and description                                                                                                     | Status   |
| ------------ | ------------------------------------------------------------------------------------------------------------------------ | -------- |
| `idp_id`     | The user’s unique identifier, assigned by the directory provider. Different directory providers use different ID formats | Required |
| `first_name` | The user’s first name                                                                                                    | Optional |
| `last_name`  | The user’s last name                                                                                                     | Optional |
| `email`      | The user’s email                                                                                                         | Optional |
| `state`      | The user’s state. May be `active`, or `inactive`                                                                         | Required |

> `emails`, `job_title`, and `username` were previously considered standard attributes, but have been deprecated in favor of equivalent [auto-mapped custom attributes](/directory-sync/attributes/custom-attributes/predefined-attributes).

---

## Custom attributes

For more detailed user information, you can opt-in to additional predefined attributes and define your own custom attributes. These attributes will appear in the custom attributes field on [Directory User](/reference/directory-sync/directory-user) objects and can be configured in the [WorkOS Dashboard](https://dashboard.workos.com/).

> Custom attributes are configured at the environment level. To configure attributes for a specific organization, please [contact our support team](mailto:support@workos.com).

### Predefined attributes

When enabled, the values will be mapped without additional setup. Not every directory provider has data for every field, so they are always optional if enabled. These fields are named and schematized by WorkOS – they cannot be renamed.

| Attribute               | Type and description                                                                                                            |
| ----------------------- | ------------------------------------------------------------------------------------------------------------------------------- |
| `addresses`             | The user’s list of address objects (`street_address`, `locality`, `region`, `postal_code`, `country`, `primary`, `raw_address`) |
| `cost_center_name`      | The user’s cost center name                                                                                                     |
| `department_name`       | The user’s department name                                                                                                      |
| `division_name`         | The user’s division name                                                                                                        |
| `emails`                | The user’s list of email objects (`type`, `value`, `primary`)                                                                   |
| `employee_type`         | The user’s employment type                                                                                                      |
| `employment_start_date` | The user’s start date                                                                                                           |
| `job_title`             | The user’s job title                                                                                                            |
| `manager_email`         | The email address for the user’s manager                                                                                        |
| `username`              | The user’s username                                                                                                             |

#### Enable or disable a predefined attribute

Predefined attributes can be enabled or disabled in the [WorkOS Dashboard](https://dashboard.workos.com/) on the Identity Provider Attributes page.

![WorkOS Dashboard UI showing editing predefined attributes](https://images.workoscdn.com/images/73b775ed-c3ef-4c81-bb56-7b040d3d073a.png?auto=format&fit=clip&q=50)

> Updates to these settings may take up to an hour to reflect in your Directory User API response. A [dsync.user.updated](/events/directory-sync) event is emitted for each Directory User changed by toggling auto-mapped attributes.

### Support per directory provider

The following support table outlines the attribute availability across directory providers.



### Custom attributes

Custom attributes can be utilized to enrich [Directory User](/reference/directory-sync/directory-user) objects with additional data from the identity provider. You can create attributes that appear as fields in the [Admin Portal](https://workos.com/admin-portal). Your customers can map these fields to the correct values in their system when setting up Directory Sync with their identity provider.

#### Create a custom attribute

Custom attributes can be created in the [WorkOS Dashboard](https://dashboard.workos.com/) on the Identity Provider Attributes page.

![WorkOS Dashboard UI showing custom attribute creation](https://images.workoscdn.com/images/c1d00c4c-4dea-415e-a8f8-c870317410df.png?auto=format&fit=clip&q=50)

#### Delete a custom attribute

When a custom attribute is deleted, the attribute will be deleted from all [Directory User](/reference/directory-sync/directory-user) objects.

> Updates to custom attributes may take up to an hour to reflect in your Directory User API response. A [dsync.user.updated](/events/directory-sync) event is emitted for each Directory User changed.

#### Nested attributes

Custom attributes support nested attribute mapping. Different directory providers structure their data differently, and nested attribute support allows you or your customer to map values regardless of where they appear in the directory structure.

Nested attributes from the directory can be mapped to custom attributes using the WorkOS [Dashboard](https://dashboard.workos.com/) or [Admin Portal](/admin-portal) by selecting attributes from an interactive schema viewer. The schema viewer displays the structure of user data from their directory, allowing them to browse and select any nested field. This ensures accurate mapping without manual configuration.

##### Example: Different structures across providers

The same logical attribute (like "license") may appear at different nesting levels depending on the provider:

```json language="json" title="Provider A - Nested under URN"
{
  "userName": "jdoe@example.com",
  "urn:ietf:params:scim:schemas:extension:enterprise:2.0:User": {
    "license_tier": "silver"
  }
}
```

```json language="json" title="Provider B - Under custom schemas"
{
  "userName": "jdoe@example.com",
  "customSchemas": {
    "license_tier": "silver"
  }
}
```

With nested attribute support, you can create a single custom attribute called `license_tier`, and IT admins can map it to the correct location in their specific directory provider's structure using the schema viewer.

---

## Raw attributes [**Deprecated**]

These are unfiltered and unstructured attributes that are unique to each directory provider. These attributes are included as fields in the `raw_attributes` object that is included in the [Directory User](/reference/directory-sync/directory-user). The `raw_attributes` property will be phased out soon. [IdP role assignment](/directory-sync/identity-provider-role-assignment) or [custom attributes](/directory-sync/attributes/custom-attributes) should be used instead.
Contact support [via email](mailto:support@workos.com) or slack if you need help with the migration.

### Migration from raw_attributes to custom attributes

If you currently access nested fields from `raw_attributes` in your application code, you should migrate to using custom attributes with nested attribute mapping. This provides a consistent, structured API while giving your customers' IT admins the flexibility to map any field from their directory provider.

**Before (deprecated):**

```javascript
const license_tier =
  user.raw_attributes[
    'urn:ietf:params:scim:schemas:extension:enterprise:2.0:User'
  ]?.license_tier;
const employeeId = user.raw_attributes.customSchemas?.Company?.employeeId;
```

**After (recommended):**

```javascript
const license_tier = user.custom_attributes.license_tier;
const employeeId = user.custom_attributes.employee_id;
```

**How to migrate:**

1. **Identify nested fields** you currently access from `raw_attributes`
2. **Create custom attributes** in the [WorkOS Dashboard](https://dashboard.workos.com/) for each field
3. **Instruct your customers' IT admins** to map these attributes using the schema viewer during directory setup
4. **Update your application code** to read from `custom_attributes` instead of `raw_attributes`
5. **Test with a pilot directory** before migrating all directories

The nested attribute mapping feature ensures IT admins can map any field from their identity provider, regardless of structure, while your application receives a consistent, predictable schema.

---

## Frequently asked questions

### Are existing directories required to update the attribute mapping when new required custom attributes are added?

No, when you add a new required custom attribute to your settings, this won’t be retroactively required for directories that have already been set up and configured. However, in the WorkOS dashboard, you will be able to navigate directly to the existing directory and fill in details for those attributes manually.

### Can our customers add their own custom attributes outside of what is defined in the WorkOS dashboard?

We do not currently support this functionality, as you have to define any custom attributes in the dashboard first. Please reach out to [support](mailto:support@workos.com) if you have a specific use case that you would like to discuss.

### What happens if an attribute cannot be mapped from the IdP?

Attributes that cannot be mapped for a particular [Directory User](/reference/directory-sync/directory-user) will result in a `null` value for the attribute. [dsync.user.updated](/events/directory-sync) events are not emitted when an attribute changes from `null` to `undefined` or vice versa.

### How do IT admins map nested attributes?

IT admins can map nested attributes using the schema viewer in the WorkOS [Admin Portal](/admin-portal) when configuring their directory. The schema viewer displays the actual structure of user data from their directory provider, showing how attributes are organized.

To map a nested attribute:

1. IT admins navigate to the attribute mapping step during directory configuration
2. They view a visual representation of their directory's user data structure
3. They select any field, regardless of nesting level, from the schema viewer
4. The mapping is automatically configured for that nested attribute

This approach works consistently across all directory providers, even though each provider may structure their data differently. The schema viewer adapts to show the specific structure of the IT admin's directory provider, ensuring accurate mapping without requiring technical knowledge of the underlying data format.


## Docs {#demo}

### Tabs

Test page for the tabs component

- | Directory User

  Whether to parse strikethrough with a single tilde (boolean, default: true).

- | List Directory

  Get the details of an existing Directory User.

- | Get Directory Group

### Table

Demo example of a table.

## Default table

| First Header | Second Header |
| ------------ | ------------- |
| Content Cell | Content Cell  |
| Content Cell | Content Cell  |

## Formatting with `inline code blocks`, `links`, and `text styles`

| Command      | Description                                                              |
| ------------ | ------------------------------------------------------------------------ |
| `git status` | List all _new or modified_ files                                         |
| `git diff`   | Show [file](https://workos.com) differences that **haven’t been** staged |

## Text alignment

| Left-aligned | Center-aligned | Right-aligned |
| ------------ | -------------: | ------------: |
| git status   |     git status |    git status |
| git diff     |       git diff |      git diff |

### Code Block Replacements

Test page for the code block replacements

```js
'foo@foo-corp.com foo-corp.com foo@foo-corp.com';
```

```js
'foo-corp.com';
```

```js
'afoo-corp.com';
```

```js
'foo-corp.com foo-corp.com';
```

```js
const api_key = 'sk_example_123456789';
const client_id = 'client_123456789';
```

### Punctionation

This will be SEO description.

The punctuation should use proper typographic symbols, like dashes and curly quotes via [SmartyPants](https://daringfireball.net/projects/smartypants/).

## Apostrophe and smart quotes

"It'll be great" they said. Here's a sentence. "Great", he said. They said it'll be great. "We'll do that" they said. "You'll do that" he said. _They_ can't do that.

`don't replace the apostrophe here`.

`"string"`

```plaintext
Don't replace the "apostrophe" and "quotes" here too.
```

## Dashes

One - a hyphen. Two – an en-dash. Three—an em dash.

All should be transformed into en-dashes.

Code should not be transformed:

```plaintext
One - a hyphen. Two – an en-dash. Three—an em dash.
```

`One - a hyphen. Two – an en-dash. Three—an em dash.`

## Ellipsis

Counting...

### Demo page

This will be SEO description.

These pages are for demo purposes only. They are not meant to be used in production.

### Definition List

Demo example of a definition list.

Not a definition item.

Term 1
: Definition 1

Not a definition item.

Term 2
: Definition 2

**Term 3**
: _Definition_ 3

**Term 4**
: Definition 4

Term 5
: `Definition 5`

Term 6
: Definition `6`

Term `7`
: Definition 7

: Not a definition item.

Not a term
:

### Code Block

Test page for the code block component

## From file



## Without title

```js
console.log();
```

## Empty code block

```plain

```

## With tabs across all languages

Input:

```html

```

Output:



## One language with tabs

Only Java should have tabs here.

Input:

```html

```

Output:

### Checklist

Test page for the checklist component

## Checklist syntax stress test

Adjacent lists shouldn’t be mangled.

- [ ] One two.
- [x] Three four.
- [ ] Five six.
- [ ] Seven eight.
- Nine
- Ten
- Eleven
- Twelve

- [ ] Implement an SSO UI/UX. See our guide for ideas – [UI/UX Best Practices for IdP & SP-Initiated SSO](/)
- [ ] Unlock your Production environment by adding your billing information
  > Only enterprise connections in your Production environment will be charged. Any Google OAuth, Microsoft OAuth, GitHub OAuth, Sign in with Apple, or Magic Link connections will be free.
- [ ] Set your Production Client’s ID and API Key as environment variables
- [ ] Secure your Production Project’s API key
- [ ] Configure production redirect URI(s) in your Product Project. Verify the default redirect URI is correct
- [ ] Ensure that your application can receive redirects from WorkOS.
      Depending on your network architecture, you may need to allowlist incoming redirect traffic from `api.workos.com` → WorkOS currently cannot promise that redirect traffic will originate from a static set of IP addresses.
- [ ] Add Connections for your customers in the Production Environment

- one
- two
- three
- four

### Accordion

Test page for the accordion component

## Accordion syntax stress test

Adjacent lists shouldn’t be mangled.

- ### One?
  Two.
- ### Three?
  Four.
- ### Five?
  Six.
- ### Seven?
  Eight.
- Nine
- Ten
- Eleven
- Twelve
- ### `dsync.deactivated` 
  Accordion content goes here.
- ### `dsync.activated` 
  Accordion content goes here.
- ### `dsync.deactivated` 
  Accordion content goes here.
- ### `dsync.deleted` 
  Accordion content goes here.
- One
- Two
- Four
- Three


## Custom Domains {#custom-domains}

### Custom Domains

Configure your integration to match your brand identity.

## Overview

By default, a WorkOS domain will be used for services such as sending email and hosting AuthKit. In the staging environment these will always use a WorkOS domain, however in production you have the option to provide your own custom domain.

This is a paid service, for which you can find additional details on our [pricing page](https://workos.com/pricing).

Custom domains can be set for:

- [Email](/custom-domains/email)
- [AuthKit](/custom-domains/authkit)
- [Admin Portal](/custom-domains/admin-portal)
- [Authentication API](/custom-domains/auth-api)

### Email Domain

Guidance on configuring a custom domain for emails.

## Configuring a domain

Several AuthKit features require sending emails:

- Magic Auth
- Email verification
- Password resets
- Invitations

While developing with WorkOS in a staging environment, WorkOS will send AuthKit emails from `workos.dev`.

In production environments, emails are sent from a custom domain when configured or from `workos-mail.com` by default.

### (1) Navigate to Domains configuration

With the production environment selected, navigate to the _Domains_ section of the [WorkOS Dashboard](https://dashboard.workos.com/).

![Dashboard displaying domain configuration settings](https://images.workoscdn.com/images/1b02397b-89b0-451d-9d6d-d222db5635b9.png?auto=format&fit=clip&q=80)

### (2) Add an email domain

Click the _Add Domain_ button and enter the domain you would like to use for sending emails.

![Dashboard displaying a domain entry input](https://images.workoscdn.com/images/2e7840fb-1a05-444c-a68f-52f3ac3eceb5.png?auto=format&fit=clip&q=80)

### (3) Create CNAME records

You will be prompted to create 3 CNAME records with your DNS provider. After creating these DNS records, click _Verify now_.

![Dashboard displaying CNAME entries](https://images.workoscdn.com/images/0d85fad9-2af7-425d-8b4b-55c407a0db83.png?auto=format&fit=clip&q=80)

> It can take some time for DNS changes to take effect. If the initial verification attempt is not successful, WorkOS will continue trying to verify your domain for 72 hours.

Once your domain is successfully verified, authentication emails and Admin Portal invites will be sent from `no-reply@your-domain.com`. It’s important to keep the CNAME records in place to ensure that WorkOS can deliver mail on your behalf.

### AuthKit Domain

Guidance on configuring a custom domain for AuthKit.

## Configuring a domain

The domain for AuthKit will consist of a randomly generated phrase plus the domain `authkit.app`, for instance `youthful-ginger-43.authkit.app`.

This is the default in the staging environment, in Production environments a custom domain can be configured via the dashboard.

### (1) Navigate to Domains configuration

With the production environment selected, navigate to the _Domains_ section of the [WorkOS Dashboard](https://dashboard.workos.com/).

![Dashboard displaying domain configuration settings](https://images.workoscdn.com/images/f04be28f-4af8-4a2e-a812-815d1a441674.png?auto=format&fit=clip&q=80)

### (2) Add an AuthKit domain

Click the _Configure AuthKit domain_ button and enter the domain you would like to use.

![Dashboard displaying a domain entry input](https://images.workoscdn.com/images/b7e45bfe-2948-4d5e-a7ee-be3eb223d710.png?auto=format&fit=clip&q=80)

### (3) Create CNAME records

You will be prompted to add a CNAME record to your DNS provider. If your DNS provider is Cloudflare, ensure the CNAME record is configured as DNS-only and is not proxied. To manage custom domains, WorkOS uses Cloudflare, who prohibit domains from being proxied across accounts.

![Dashboard displaying CNAME entries](https://images.workoscdn.com/images/68d96188-8d68-4a89-8843-5d21d9b739c5.png?auto=format&fit=clip&q=80)

> It can take some time for DNS changes to take effect. If the initial verification attempt is not successful, WorkOS will continue trying to verify your domain for 72 hours.

Once your domain is successfully verified, users signing in via AuthKit will be redirected to your custom domain.

### Authentication API Domain

Guidance on configuring a custom domain for the Authentication API.

## Configuring a domain

WorkOS authentication requests are done via the Authentication API, which defaults to `api.workos.com`. This can be configured to a custom domain if you prefer to use your own branding instead of the default.

While developing with WorkOS in a Sandbox environment, requests are made to the `api.workos.com` domain. In production environments, requests are made to `api.workos.com` by default or a custom domain if configured.

For instance, if you were retrieving a user via the API and you had a custom Authentication API domain `api.example.com` set up, you'd make requests to:

`https://api.example.com/user_management/users/:id`

Instead of the default:

`https://api.workos.com/user_management/users/:id`

> When a custom domain is configured, requests to the API should be routed through that domain. Continuing to make requests to `api.workos.com` after a custom domain is configured for the Authentication API can result in issues with your integration.

### Custom domains for SCIM endpoints

Custom Authentication API domains are also used for SCIM endpoints when using [Directory Sync](/directory-sync). For example, if you had a custom domain `api.example.com` set up, your customer's identity provider would make requests to `https://api.example.com/scim/v2.0/:id`. The custom domain is reflected in the Admin Portal setup steps for a directory.

Adding a custom domain does not affect existing directory integrations pointing to `api.workos.com`.

### Using custom domains in SDKs

When using the WorkOS SDKs, a custom API hostname can be configured:



### (1) Navigate to Domains configuration

With the production environment selected, navigate to the _Domains_ section of the [WorkOS Dashboard](https://dashboard.workos.com/).

![Dashboard displaying domain configuration settings](https://images.workoscdn.com/images/28d1072b-1bc7-42af-8d7d-fb8da0363413.png?auto=format&fit=clip&q=80)

### (2) Add an email domain

Click the _Configure authentication API domain_ button and enter the domain you would like to use.

![Dashboard displaying a domain entry input](https://images.workoscdn.com/images/cc648df5-603a-4001-bfc0-cea2bfca02e5.png?auto=format&fit=clip&q=80)

### (3) Create CNAME records

You will be prompted to add a CNAME record to your DNS provider. If your DNS provider is Cloudflare, ensure the CNAME record is configured as DNS-only and is not proxied. To manage custom domains, WorkOS uses Cloudflare, who prohibit domains from being proxied across accounts.

![Dashboard displaying CNAME entries](https://images.workoscdn.com/images/e920f0c9-4bd5-4f11-a2e3-9d709b483ed1.png?auto=format&fit=clip&q=80)

> It can take some time for DNS changes to take effect. If the initial verification attempt is not successful, WorkOS will continue trying to verify your domain for 72 hours.

Once your domain is successfully verified, your custom domain will act as the [ACS URL](/glossary/acs-url) for authentication.

### Admin Portal Domain

Guidance on configuring a custom domains for the Admin Portal.

## Configuring a domain

When your customer's organization admins use the Admin Portal self-serve onboarding experience, they'll be directed to a `setup.workos.com` domain.

While developing with WorkOS in a staging environment, users will see the `setup.workos.com` domain.

In production environments, users will see `setup.workos.com` by default or a custom domain if configured.

### (1) Navigate to Domains configuration

With the production environment selected, navigate to the _Domains_ section of the [WorkOS Dashboard](https://dashboard.workos.com/).

![Dashboard displaying domain configuration settings](https://images.workoscdn.com/images/b6afe130-219c-4e0e-8209-49b1a0fb6098.png?auto=format&fit=clip&q=80)

### (2) Add an Admin Portal domain

Click the _Configure Admin Portal domain_ button and enter the domain you would like to use.

![Dashboard displaying a domain entry input](https://images.workoscdn.com/images/6dc8c261-3f1a-4d7a-b9ea-e7541d5e1361.png?auto=format&fit=clip&q=80)

### (3) Create CNAME records

You will be prompted to add a CNAME record to your DNS provider. If your DNS provider is Cloudflare, ensure the CNAME record is configured as DNS-only and is not proxied. To manage custom domains, WorkOS uses Cloudflare, who prohibit domains from being proxied across accounts.

![Dashboard displaying CNAME entries](https://images.workoscdn.com/images/467edbdc-ba6d-42be-809e-7b0b1e560385.png?auto=format&fit=clip&q=80)

> It can take some time for DNS changes to take effect. If the initial verification attempt is not successful, WorkOS will continue trying to verify your domain for 72 hours.

Once your domain is successfully verified, admins using the self-serve Admin Portal will be redirected to your custom domain.


## AuthKit {#authkit}

### Users and Organizations

Flexible application modeling with user and membership features.

## Users

The [User object](/reference/authkit/user) represents an identity that has access or owns artifacts in your application. A User object may not uniquely identify an individual person, since a person may present themselves as having multiple identities in the same system.

What uniquely identifies a user is their **email address**, since having access to that email inbox ultimately gives access to all accounts based on that address.

### Authentication methods

There may be multiple authentication methods on a single user object, such as [Email + Password](/authkit/email-password) or [OAuth](/authkit/social-login). A user can sign in with any of the authentication methods associated with them, as long as you have enabled those authentication methods in the WorkOS Dashboard.



### Identity linking

Because a user is uniquely identified by their email address, you won’t have users with duplicate email addresses. WorkOS handles [identity linking](/authkit/identity-linking) automatically.

### Email verification

All users will go through an initial [email verification process](/authkit/email-verification) by default.

This applies to all authentication methods, including OAuth and SSO. This unifying interface simplifies how your application considers the authenticity of your users.

### Domain verification

If a user’s email domain matches a verified organization domain when signing in with SSO, they will [automatically be considered verified](/authkit/domain-verification) and will not need to go through the email verification flow.

---

## Organizations

Organizations represent both a collection of users that your customer’s IT admin has control over and a workspace within which members collaborate. Organizations are a first-class concept in WorkOS and support a suite of features around organizational management. There is no limit to the number of organizations you can create in WorkOS.

### Organization memberships

An organization contains users as members. Organization membership allows you to model organizations as "workspaces" and user’s access to them with memberships.

WorkOS organization memberships are designed to be flexible, and support any B2B app model. For example:



- **Multiple Workspaces:** A self-serve productivity app, like Figma, where each user can be in any number of organizations, can create their own workspace and join any number of other workspaces.
- **Single Workspace:** An app that has no collaboration outside a customer’s company, like an employee survey tool, where each user is in exactly one organization.

While these are two distinct models, your choice may depend on your go-to-market strategy, which may change over time. **WorkOS AuthKit supports both**.

### Organization access

It’s common for users to create resources in B2B applications. You can use the organization as a container for these resources, so that access is dependent on a user’s access to the organization.

This means when a user leaves an organization and is no longer a member, the data remains with the organization and not the user. Organizations provide the level of data ownership that B2B applications structure around.

While organization membership conveys the most basic form of access, you can attach more granular role information per member within your own application’s database.

### Organization roles

In addition to the [environment-level roles](/authkit/roles-and-permissions/configure-roles-and-permissions), organizations can define their own custom roles, which are assignable only within the context of the organization. Refer to the [organization roles documentation](/authkit/roles-and-permissions/organization-roles) for more details.

### Membership management

If your application uses a soft-delete model, you can utilize the extended organization membership lifecycle. Organization memberships have three possible statuses:

- `pending`, when a user is invited to an organization
- `active`, when a user is added as an organization member or accepts an invitation
- `inactive`, when an organization membership is deactivated

For soft-delete use cases, we also provide deactivation and reactivation APIs:

- [Deactivating an organization membership](/reference/authkit/organization-membership/deactivate) sets its status to `inactive` and revokes all active [sessions](/authkit/sessions). Note `pending` memberships cannot be deactivated and should be deleted using the [deleting membership API](/reference/authkit/organization-membership/delete) instead.
- [Reactivating an organization membership](/reference/authkit/organization-membership/reactivate) sets its status to `active` and retains the role attached to the organization membership prior to deactivation. This role can be updated using the [update organization membership API](/reference/authkit/organization-membership/update). Note `pending` memberships cannot be reactivated. For this the user should go through the [invitation acceptance flow](/authkit/invitations) instead. If invitations are not needed, the organization membership can be [created as active directly](/reference/authkit/organization-membership/create).

If your application uses a hard-delete model, you may use organization memberships without deactivation/reactivation by [deleting memberships](/reference/authkit/organization-membership/delete) for users who should no longer have access to an organization.

### When to use deletion vs. deactivation

Hard deletion is preferred if the app has no need to "remember" the membership. For example, when members operate solely on customer data and have no data of their own. When a member of the organization is gone, there's no need to keep around their membership data. An app in this case may even want to entirely [delete the User](/reference/authkit/user/delete) once the membership is deleted.

Deactivation may be preferred in cases where a member retains some data after leaving the organization, for example: messages, documents, or other data which reference that member. It also allows for building a user interface to list former members, perhaps with the option to reactivate them.

### Automated memberships

Beyond manually adding or removing users to and from organizations as members, users can be automatically [Just-in-Time (JIT) provisioned](/authkit/jit-provisioning) into an organization if their email address matches one of the organization's [verified domains](/authkit/domain-verification). This allows customers to quickly onboard teammates.

Users can also [invite individuals to organizations](/authkit/invitations), regardless of their email domain. This is handy for contractors within a company, or a collection of people without a shared domain.

### Creating organizations for new users

In some applications, all activity should happen within the context of an organization. This pattern is common in B2B applications where:

- All features and data are scoped to an organization
- Users need to be associated with an organization to use the application

For these applications, when new users don't already belong to an organization via [Just-in-Time provisioning](/authkit/jit-provisioning) or an [invitation](/authkit/invitations), you'll want to create an organization for them.

To ensure all users have at least one organization:

1. **Check the access token**: When AuthKit redirects a user to your application after sign up or sign in, check whether the [access token](/authkit/sessions/integrating-sessions/access-token) contains an `org_id`
2. **Present organization creation form**: If no organization is present, show the user a form with a name field to create a new organization
3. **Create the organization**: On form submission, use the [create organization API](/reference/organization/create) to create the organization
4. **Create organization membership**: Use the [create organization membership API](/reference/authkit/organization-membership/create) to add the user as a member of the new organization
5. **Refresh the token**: Call the [authenticate with refresh token API](/reference/authkit/authentication/refresh-token) with the new organization ID to receive a new access token that includes the organization

### Single Sign-On

Facilitate greater security, easier account management, and accelerated application onboarding and adoption.

## Introduction

Single Sign-On is the most frequently asked for requirement by organizations looking to adopt new SaaS applications. SSO enables authentication via an organization’s [identity provider (IdP)](/glossary/idp).

This service is compatible with any IdP and supports both the [SAML](/glossary/saml) and [OIDC](/glossary/oidc) protocols. It’s modeled to meet the [OAuth 2.0](/glossary/oauth-2-0) framework specification, abstracting away the underlying authentication handshakes between different IdPs.

## Getting started

AuthKit greatly simplifies the process of integrating SSO into your application. AuthKit will make the necessary API calls automatically and handle the routing of SSO users when their account is associated with an existing SSO connection.

## (1) Enable SSO

Navigate to the _Authentication_ settings section in the [WorkOS Dashboard](https://dashboard.workos.com/) and enable Single Sign-On.

![Dashboard demonstrating how to enable Single Sign-On](https://images.workoscdn.com/images/09c9b3c5-833e-4fe0-985b-f5b1934e4284.png?auto=format&fit=clip&q=80)

AuthKit will now automatically detect when a user is attempting to sign in via SSO and redirect them to the appropriate IdP.

## (2) Test with the Test Identity Provider

To confirm your Single Sign-On integration works correctly you can use the Test Identity Provider to simulate login flows end-to-end. Your staging environment includes a default Test Organization and active SSO connection configured with the Test Identity Provider.

![WorkOS Test Identity Provider](https://images.workoscdn.com/images/7b7407d7-dcc7-4fd4-859f-4ee4214d69c2.png?auto=format&fit=clip&q=80)

### Getting started

Log into the [WorkOS Dashboard](https://dashboard.workos.com/) and navigate to the _Test SSO_ page to get started with the Test IdP. This page outlines a number of different SSO scenarios you can follow and provides all the necessary information to complete the tests.

![Test SSO WorkOs Dashboard](https://images.workoscdn.com/images/7b7407d7-dcc7-4fd4-859f-4ee4214d69c2.png?auto=format&fit=clip&q=80)

### Service provider-initiated SSO

This case is likely the first [login flow](/sso/login-flows/sp-initiated-sso) you would test when implementing SSO in your app. The test simulates users initiating authentication from your sign-in page. In this scenario, the user enters their email in your app, gets redirected to the identity provider, and then is redirected back to your application.

### Identity provider-initiated SSO

This test simulates users initiating authentication from their identity provider. It is a common [login flow](/sso/login-flows/idp-initiated-sso) that developers forget to consider. In the scenario, users log in to the identity provider directly, select your application from their list of SSO-enabled apps, and are redirected to your application upon successful authentication.

### Guest email domain

This test simulates users authenticating with an email domain different from the verified domain of the test organization, `example.com`. A relevant scenario is authenticating freelance users, whose email domain is not owned by the company.

### Error response

This test simulates a generic [error response](/reference/sso/get-authorization-url/error-codes) from the user’s identity provider. In this scenario, SSO authentication has failed for the user. Below is an example of the error-related parameters passed to the [redirect URI](/sso/redirect-uris) in your application.

---

## (3) Test with other identity providers

Test Identity Provider saves time by providing an out of the box experience compared to the configuration process that someone using a real identity provider would have to go through to enable Single Sign-On for your app.

If your integration works with the Test Identity Provider, you can be sure it will work with other identity providers. However, it may be helpful to also learn about the setup process that your customers will go through on their side, which varies depending on a specific identity provider.

### Create an organization

To get started, you will need to [create an organization](https://dashboard.workos.com/organizations) in the WorkOS Dashboard. Organizations in WorkOS represent your customer, so by creating an organization, you can test your SSO connection the way your customers will experience it.

![Create an organization dialog](https://images.workoscdn.com/images/2ef3565c-526a-42e6-9830-622e83b67ee5.png?auto=format&fit=clip&q=80)

### Create a connection

Go to the organization you created and click _Invite admin_. Select _Single Sign-On_ from the list of features. In the next step, enter an email address to send the setup link to, or click _Copy setup link_.

The setup link goes to Admin Portal, where your customers get the exact instructions for every step they need to take to enable Single Sign-On with your app.

> You can also integrate [Admin Portal](/admin-portal) directly into your app to enable self-serve setup of Single Sign-On and other enterprise features for your users.

![Invite an admin dialog](https://images.workoscdn.com/images/b9ab80fc-606a-417c-bade-3483ef48c2ae.png?auto=format&fit=clip&q=80)

### Follow the Admin Portal instructions

To complete the integration, you’ll have to also create an account with the identity provider you want to test with. After you have signed up with an identity provider of your choice, follow the corresponding Admin Portal instructions from the setup link. Once done, you can start testing your SSO integration with that identity provider.

![Admin Portal setup instructions](https://images.workoscdn.com/images/0ee15c3d-5356-4f41-a26a-440f95355b28.png?auto=format&fit=clip&q=80)

The setup instructions you’ve seen in the Admin Portal are also available directly in the docs if you want to create a connection manually:



---

## Integrating via the API

If you'd prefer to build and manage your own authentication UI, you can do so via the AuthKit [Authentication API](/reference/authkit/authentication).

Examples of building custom UI are also [available on GitHub](https://github.com/workos/authkit).

### SSO with contractors

Enforcing organization SSO access with external contractors.

## Introduction

In this scenario, we outline the considerations, concepts, and best practices for configuring and enforcing SSO sign-in for all members of an organization, we'll also cover how to enforce these same constraints on external contractors who may need access to company resources but are not permanent members.

## Goals & requirements

The application should be available to logged in users only. Each user is typically assigned to a single organization and will be able to collaborate with other users within that organization.

- The majority of users collaborate within the same organization.
- Permanent members of the organization use an email address that matches the organization's domain.
- Organization members are required to authenticate using SSO.
- External contractors and collaborators are also required to authenticate using SSO.

![Diagram of AnalyticsOS auth flow for users and contractors](https://images.workoscdn.com/images/b9752155-4f5d-4702-890d-c64caa54005e.png?auto=format&fit=clip&q=80)[border=false]

## Integrating SSO

Adding SSO to your application is a straightforward process when using AuthKit, and can mostly be done via the WorkOS dashboard. Steps include:

(1) Add a new SSO connection to an organization in the dashboard

(2) Configure a callback endpoint in your application

(3) Add your endpoint URL as a redirect URI in the WorkOS dashboard

(4) Handle the user session and grant access to the application

### Enforcing SSO authentication

With an active SSO connection established, it's now possible to enable it as an authentication method in the WorkOS Dashboard. This can be achieved by visiting the authentication settings view for the environment and enabling Single Sign-On.

![Enable SSO in the WorkOS dashboard](https://images.workoscdn.com/images/feb25320-d75c-47bf-ae63-ae7a99a84afb.png?auto=format&fit=clip&q=80)[border=false]

This will allow users to sign-in with AuthKit using SSO, but will not enforce it as a requirement. In order to do so, we'll need to configure an authentication policy for the organization.

### SSO authentication flow

When the user logs in, they will move through the following flow:

(1) User clicks “Sign in” button

(2) User is redirected to AuthKit, where they sign via SSO

(3) User is redirected from AuthKit to the redirect URI configured in the WorkOS dashboard

(4) User is authenticated via code presented in the `redirect_uri`

(5) Access is provisioned by the application

More in-depth information on configuration can be found in the [Single Sign-On section](/authkit/sso), with AuthKit implementation guidance available in the [Quick Start guide](/authkit).

## Understanding authentication policies

An authentication policy is a way to enforce specific authentication methods during sign-in. They typically apply to all users attempting to access the organization.

There are several approaches to enforcing SSO within an organization, including domain verification and enforcing specific policies on those inside and outside of the organization based on their attached email domain, but for the purposes of simplicity we will simply cover a blanket authentication policy which applies to all users attempting to sign-in, regardless of verified domain existence.

An authentication policy allows the organization to:

- Enforce SSO be used by all users accessing the organization
- Enforce an MFA requirement for all users accessing the organization

## Adding an authentication policy

Authentication policies can be applied in the organization settings view of the WorkOS Dashboard.

![Applying authentication policy in the WorkOS dashboard](https://images.workoscdn.com/images/22fc71c4-1565-45e4-a148-d96de148cdd4.png?auto=format&fit=clip&q=80)[border=false]

After adding SSO enforcement to the policy, all users will be required to sign-in using SSO. This will apply to all users, including external contractors. For this to function with external contractors, they will need to be added to the organization's IdP.

For cases where external contractors can not be added to the organization's IdP, an MFA requirement can be set as an enforcement fallback. This is sometimes a reasonable compromise if many contractors rotate in out of the organization.

## Summary

The organization should now be set up and to both accept SSO connections and enforce their use for all users, including external contractors. In the contractor case they will require adding to the organizations IdP or otherwise fallback to using MFA.

This scenario did not cover the full range of access constraints that can be applied to an organization following domain verification, for more information on this topic see the Access Constraints section of the documentation.

### Social Login

Quickly and easily integrate with social OAuth providers.

## Introduction

Social Login allows users to sign in or sign up using their existing credentials with OAuth providers such as Google, Microsoft, GitHub, and Apple.

## Getting started

AuthKit will make the necessary API calls and route users through OAuth providers automatically during the authentication flow, though the relevant providers must first be configured and enabled.

### (1) Configure OAuth providers

Configuration can be supplied via the _Authentication_ section of the [WorkOS Dashboard](https://dashboard.workos.com). WorkOS provides integration guides for common providers such as [Google](/integrations/google-oauth), [Microsoft](/integrations/microsoft-oauth), [GitHub](/integrations/github-oauth), [Apple](/integrations/apple), [GitLab](/integrations/gitlab-oauth), [LinkedIn](/integrations/linkedin-oauth), and [Slack](/integrations/slack-oauth).

![Dashboard configure OAuth settings](https://images.workoscdn.com/images/037ede88-0b7a-4e26-9ede-441a25ce584c.png?auto=format&fit=clip&q=80)

### (2) Enable OAuth providers

After a provider has been configured and enabled, it will appear as a sign in option on the AuthKit authentication page.

![AuthKit sign in page with social providers highlighted](https://images.workoscdn.com/images/f743cf4f-a32c-464b-94a4-db9f5c146773.png?auto=format&fit=clip&q=80)

## Custom OAuth scopes

AuthKit offers support for custom OAuth scopes for Google, Microsoft, GitHub, GitLab, and Xero integrations. This allows you to request specific permissions when accessing user profile data from these providers. For instance, requesting access to read Google Calendar events or retrieve emails from a Microsoft account. See the relevant provider section for more information:

- [Google](/integrations/google-oauth/configure-additional-oauth-scopes-optional)
- [Microsoft](/integrations/microsoft-oauth/configure-additional-oauth-scopes-optional)
- [GitHub](/integrations/github-oauth/configure-additional-oauth-scopes-optional)
- [GitLab](/integrations/gitlab-oauth/configure-additional-oauth-scopes-optional)
- [Xero](/integrations/xero-oauth/configure-additional-oauth-scopes-optional)

## Provider-driven user profile updates

When a user logs in with a social provider, the user profile information may be updated. The user's profile picture and name will always be updated to match the information supplied by the social provider. If the email address at the provider has changed and the user is only linked to a single provider, the email will also be updated to match (If the new email is already in use, no change will take place).

---

## Integrating via the API

If you'd prefer to build and manage your own authentication UI, you can do so via the AuthKit [Authentication API](/reference/authkit/authentication).

Examples of building custom UI are also [available on GitHub](https://github.com/workos/authkit).

### Sessions

Learn more about integrating sessions.

## Introduction

When a user signs in to your app, a user session is created. Along with the [User object](reference/authkit/user), a successful authentication response will include an access token and refresh token. Your application can use these tokens to ensure that the user’s session is still active.

Each user session can be viewed from within the WorkOS dashboard:

![Sessions Detail UI](https://images.workoscdn.com/images/295ded3e-7e8f-4322-bcc0-95db1cfc255b.png?auto=format&fit=clip&q=80)

Navigate to _Users_ and select a user. Then, switch to _Sessions_ tab and click on a user session to get more information.

## Integrating Sessions

Successful authentication responses will include both an access token and a refresh token. The access token should be stored as a secure cookie in the user’s browser and should be validated by the backend on each request. The refresh token should either be stored in a secure cookie or persisted on your backend. Once the access token has expired, a new one can be obtained using the refresh token.

![Sessions Diagram](https://images.workoscdn.com/images/aa420ffa-3b8c-462c-992b-b53e458dd916.png?auto=format&fit=clip&q=80)[border=false]

### Access Token

If you’re using our [Next SDK](https://www.npmjs.com/package/@workos-inc/authkit-nextjs) or [Remix SDK](https://github.com/workos/authkit-remix), all the work of validating access tokens and refreshing expired tokens is handled for you (more framework support coming soon). Read on for details about how token handling works.

The access token is a JSON Web Token (JWT), which should be validated on each request using a library like jose. The [signing JWKS](/reference/authkit/session-tokens/jwks) can be found at `http://api.workos.com/sso/jwks/`. The JWT includes the following claims:

- `sub`: the WorkOS user id
- `sid`: the session ID (used for signing out)
- `iss`: `https://api.workos.com/` (will be your custom auth domain if configured)
- `org_id`: the organization that was selected at sign-in time (if applicable)
- `role`: the role of the selected organization membership (only applicable if an organization is selected)
- `permissions`: the permissions assigned to the role (if applicable)
- `exp`: the standard `expires_at` claim (the token should not be trusted after this time)
- `iat`: the standard `issued_at` claim

### Refresh Token

Refresh tokens should be persisted on the backend in, for instance, a database, cache, or secure http-only cookie. A new access token can be obtained by using the [authenticate with refresh token](/reference/authkit/authentication/refresh-token) endpoint. If the session is still active, a new access token and refresh token will be returned. Refresh tokens may be rotated after use, so be sure to replace the old refresh token with the newly returned one.

### Switching Organizations

Refresh tokens can be used to obtain a new access token for a different organization by passing the `organization_id` parameter to the [authenticate with refresh token](/reference/authkit/authentication/refresh-token) endpoint. If the session for the refresh token is authorized to access the organization, then the `org_id` will be set to the given organization, along with the `role` and `permissions` claims matching the user's membership in that organization.

If the user is not authorized for the organization, then an appropriate [authentication error](/reference/authkit/authentication-errors) will be returned and the user will need to authenticate. Applications can use the [Get Authorization URL](/reference/authkit/authentication/get-authorization-url) and the `organization_id` parameter to initiate the authentication flow specifically for the organization.

### Signing Out

When a user signs out of your app, the following steps should occur:

- Get the session id (`sid` claim) out of the access token.
- Delete the user’s app session.
- Redirect the user’s browser to [logout endpoint](/reference/authkit/logout) endpoint (this will ensure the user’s session ends at WorkOS).
- The user will be redirected back to the URL configured as your _App homepage URL_

#### Example

```javascript
// extract sessionId from access token
const sessionId = jose.decodeJwt(session.accessToken).sid;

// delete app session cookie
cookies().delete('my-app-session');

// redirect to logout endpoint
// (the user will be redirected to your app homepage url
//  after the logout completes)
redirect(workos.userManagement.getLogoutUrl({ sessionId }));
```

## Configuring Sessions

Using the WorkOS dashboard you can configure how Sessions work in your integration. You’ll find the settings in the _Authentication_ section.

![Session Configuration UI](https://images.workoscdn.com/images/158987a3-127c-4bcd-a3ac-53b1be0abd8a.png?auto=format&fit=clip&q=50)

- **Maximum session length:** The session will expire after this length of time. Once expired the user will need to sign in again.
- **Access token duration:** Your backend can verify the access token on each request (see the [Integrating Sessions](authkit/sessions/integrating-sessions) section above). It’s recommended to keep the access token duration short so that changes in the session are quickly reflected in your app.
- **Inactivity timeout:** The session ends if a refresh has not occurred in this length of time. The user will need to sign in again.

Additionally, make sure to review your settings in the _Redirect_ section:

### Sign-out redirect

![Sign-out redirect settings](https://images.workoscdn.com/images/4a90f418-8ce0-44f2-a34c-c8336a317277.png?auto=format&fit=clip&q=50)

Make sure to set a default Sign-out redirect, which will be the location users will be redirected to after their session has been ended. Non-default Sign-out redirects can be used as values to the `return_to` parameter of the [Logout API](/reference/authkit/logout/get-logout-url) in order to dynamically choose the final logout redirect location.

#### Wildcards

The `*` symbol can be used as a wildcard for subdomains; however, it must be used in accordance with the following rules in order to properly function.

- The wildcard **must** be located in a subdomain within the hostname component. For example, `http://*.com` will not work.
- The wildcard **must** be located in the subdomain which is furthest from the root domain. For example, `https://sub.*.example.com` will not work.
- The URL **must not** contain more than one wildcard. For example, `https://*.*.example.com` will not work.
- A wildcard character **may** be prefixed and/or suffixed with additional valid hostname characters. For example, `https://prefix-*-suffix.example.com` will work.
- A URL with a valid wildcard **will not** match a URL more than one subdomain level in place of the wildcard. For example, `https://*.example.com` will not work with `https://sub1.sub2.example.com`.
- In production environments, wildcards cannot be used with [public suffix domains](https://publicsuffix.org). For example, `https://*.ngrok-free.app` will not work.
- The wildcard will match a sequence of letters (`A` – `Z`, `a` – `z` ); digits (`0` – `9`), hyphens (`-`), and underscores (`_`).
- A URL with a wildcard cannot be set as the default redirect URI.

### Roles and Permissions

Manage and assign roles and permissions to users.

## Introduction

A role represents a logical grouping of permissions, defining access control levels for users within your application. Roles are identified by unique, immutable slugs and are assigned to users through [organization memberships](/authkit/users-organizations/organizations).

Permissions grant users privileged access to resources and actions in your application and are referenced in your code by unique, immutable slugs. A permission can be assigned to any number of roles.

### Standalone roles

Roles alone can be sufficient when your application only requires very coarse-grained access control. This is suitable if users only need to be separated into broad categories and there is minimal overlap between roles. Simple roles can be easier to manage, but are less flexible for complex access control scenarios.

### Utilizing permissions with roles

Permissions allow for more detailed and flexible management of access. They are particularly useful when:

- You anticipate the need to frequently modify access rights or introduce new roles.
- There is significant overlap in access rights between different roles, but with some variations.
- You want to minimize code changes when modifying access rights. By abstracting access control checks to permissions, you can add or modify roles and their access rights without changing the application code.

## Configure roles and permissions

Roles and permissions are managed in their own section of the [WorkOS Dashboard](https://dashboard.workos.com/environment/roles-and-permissions/).

![Roles section WorkOS Dashboard](https://images.workoscdn.com/images/09c8fb23-5748-4236-914e-79849ac03a9a.png?auto=format&fit=clip&q=50)

### Create permissions

When configuring permissions, we recommend:

- Defining a common naming scheme to use across all permissions for your application. Consider separating the resource and action with a delimiter, such as `users:view`. The following delimiters are permitted: `-.:_*`.
- Keep permission slugs clear and concise. When assigned to roles, these slugs will be included in session cookies in the [session JWT claims](/authkit/sessions/integrating-sessions/access-token), which is limited to a maximum size of 4KB in many modern web browsers.

### Assign permissions to roles

Permissions can be assigned when creating a new role or when editing an existing role.

![Assign permissions to a role](https://images.workoscdn.com/images/f6fd6d9a-a7b0-4df7-908b-b657e669a3dc.png?auto=format&fit=clip&q=50)

### Default role

Role configuration occurs at the environment level. Each environment is seeded with a default `member` role, which is automatically assigned to every organization member. This default role cannot be deleted, but any role can be set as the default.

If you need to set default roles or other role configurations at the organization level, refer to the [Organization roles](/authkit/roles-and-permissions/organization-roles) section.

### Assign roles

By default, organization memberships require exactly one role. Every user with an organization membership is automatically assigned the default role when added to an organization. This role can be edited.

When [multiple roles is enabled](/authkit/roles-and-permissions/multiple-roles), you can assign several roles to an organization membership. The user gets all permissions from each role.



You can retrieve the role information from the user's [organization membership object](/reference/authkit/organization-membership) to determine their access level and capabilities within your application.

Role changes are tracked and logged via the [`organization_membership.updated` event](/events/organization-membership). To view these changes, go to the [events page](https://dashboard.workos.com/environment/events) and filter by `organization_membership.updated`.

### Delete roles

When roles are deleted:

- **Single role mode**: All organization memberships with the deleted role are reassigned to the default role.
- **Multiple roles mode**: The deleted role is removed from all organization memberships that have it assigned, while other roles on the organization membership remain intact. If the deleted role was the only role assigned to the membership, it will be reassigned the default role.

Role deletion happens asynchronously, so there may be a slight delay between deleting a role and updating all affected organization memberships.

> To migrate from one default role to another, set the new default role and delete the old one. All users will then be reassigned to the new default role.

### Priority order

If a user is provisioned from multiple sources with conflicting roles, the role with the highest priority will be assigned. This is applicable for a single role architecture utilizing [role assignment](/authkit/roles-and-permissions/role-assignment).

Priority order also determines which role will be assigned to users when migrating from a multiple roles to single role configuration in the environment.

## Multiple roles

When [enabled](/rbac/configuration/configure-roles/multiple-roles), AuthKit supports multiple roles per organization membership. A user receives the **union of permissions** across all assigned roles. For example, a user with the _Designer_ and _Engineer_ roles gets both sets of permissions in their session. This prevents role explosion by avoiding redundant hybrid roles, like "designer-engineer". Each organization membership must have **at least one** role, they will always receive the default role if no other role(s) are set.

### Use cases

By default, multiple roles is disabled and users can only have a single role per entity. You might want to enable multiple roles when you need:

- **Cross-department collaboration**: e.g., designers who need some engineering permissions.
- **Additive, disjoint permissions**: independent permission sets that should stack.
- **Temporary access**: grant time-bound extra capabilities without creating hybrid roles.

For most apps, start with **single-role relationships** for simplicity and predictability, and adopt multiple roles only when overlapping permission sets become common.

| Mode           | Access calculation                         | Pros                                         | Considerations                                      |
| -------------- | ------------------------------------------ | -------------------------------------------- | --------------------------------------------------- |
| Single role    | One role's permissions per membership      | Simple model; predictable audits; small JWTs | May require hybrid roles for cross-functional users |
| Multiple roles | Union of permissions across assigned roles | Flexible; avoids role sprawl                 | Larger JWTs; more governance                        |

### Manually assign roles to a user

Roles can be assigned manually following the steps below, or via identity provider roles assignment outlined in the next section.

1. From the WorkOS Dashboard, open [Users](https://dashboard.workos.com/environment/users).
2. Select a user and go to **Organization memberships**
3. Click **Edit roles** and add all relevant roles
4. Or assign roles [via the API](/reference/authkit/organization-membership/update)

Each organization membership must have **at least one** role.

## Role assignment

You can map identity provider groups to roles to automatically assign roles to users. AuthKit supports two methods for role assignment:

### SCIM (Directory Sync)

Roles can be assigned via SCIM through [directory group role assignment](/directory-sync/identity-provider-role-assignment/directory-group-role-assignment). Admins can map group memberships to roles in the Admin Portal during SCIM or Google Workspace directory setup. These mappings are used to assign roles to organization memberships via [Directory Provisioning](/authkit/directory-provisioning).

### SSO

Roles can also be assigned via [SSO group role assignment](/sso/identity-provider-role-assignment/sso-group-role-assignment). Groups returned in the SSO profile can be mapped to roles in the WorkOS Dashboard. If an AuthKit user authenticates via SSO and belongs to a mapped group, the corresponding role will be set on the [organization membership](/reference/authkit/organization-membership) and reflected in the [user’s session](/authkit/sessions/integrating-sessions/access-token).

> Ensure [SSO JIT provisioning](/authkit/jit-provisioning/sso-jit-provisioning) is enabled for each organization using SSO role assignment.

### Enabling in Admin Portal

Organization admins can assign roles to identity provider groups in the [Admin Portal](/admin-portal) during SSO or directory setup.

From the _Roles and Permissions_ section in the WorkOS Dashboard, role assignment is an environment-level setting. However, it can also be configured per organization via the _Roles_ tab on that organization's page. If enabled, all Admin Portal sessions for the relevant SSO connection or directory will support group role assignment.

![Enable directory group role assignment dashboard setting](https://images.workoscdn.com/images/fe19e3ac-6370-404e-9590-cdb06b3de127.png?auto=format&fit=clip&q=50)

Whether to enable role assignment for SSO or directory groups depends on your application’s setup. When [provisioning users with Directory Sync](/authkit/directory-provisioning), we recommend enabling directory group role assignment due to [limitations of SSO role assignment](/sso/identity-provider-role-assignment/considerations/drawbacks).

If you’re not yet using directory provisioning, you can enable SSO group role assignment as the environment default.

Because this setting is configurable per organization, choose a sensible default based on your customers' typical setup:

- **A. All organizations use SSO:**
  If no organizations are using Directory Sync, enable SSO group role assignment in Admin Portal at the environment level.

- **B. Some organizations use Directory Sync:**
  Enable directory group role assignment in Admin Portal for those specific organizations.

- **C. Most organizations use Directory Sync:**
  Enable directory group role assignment in Admin Portal at the environment level, and override the setting for organizations that only use SSO.

### Migrating role assignment source

It’s recommended to use only one role assignment source per organization. If your organization currently uses SSO group role assignment and you'd like to switch to [directory group role assignment](/directory-sync/identity-provider-role-assignment/directory-group-role-assignment), consider the following paths:

- **A. Directory is not yet configured:**
  [Enable directory group role assignment](/authkit/roles-and-permissions/role-assignment/enabling-in-admin-portal) for this organization via the **Roles** tab under an organization in the WorkOS Dashboard. The organization admin will be prompted to set up directory group role assignments in the Admin Portal.

- **B. Directory is already configured:**
  Manually assign roles to directory groups in the WorkOS Dashboard, or regenerate an Admin Portal link so the organization admin can set the role mappings there.

Directory group role assignments take precedence and will override any SSO group role assignments on the organization membership. Once directory group roles are properly set up and reflected, you can delete the SSO group mappings.

### Role source priority

AuthKit enforces strict priority rules for assigning roles. When roles are sourced from SSO group role assignment:

- An explicit SSO group role assignment **overrides** any role manually assigned via the [organization memberships API](/reference/authkit/organization-membership) or the [WorkOS Dashboard](https://dashboard.workos.com/). However, a default SSO group role assignment **does not override** a manual one.
- The system may allow a temporary override through the [organization memberships API](/reference/authkit/organization-membership), but it **reapplies** the SSO-assigned role when the user next authenticates, provided the assignment came from an explicit SSO group.
- The system **always overwrites** previous SSO role assignments with new ones, whether they originate from an explicit or default mapping.

Role assignments sourced from [Directory Provisioning](/authkit/directory-provisioning):

- An explicit directory group role assignment **overrides** any role manually assigned via the [organization memberships API](/reference/authkit/organization-membership) or the [WorkOS Dashboard](https://dashboard.workos.com/), or any SSO group role assignment.
- The system **does not allow** SSO to override these roles.
- You **can override** these roles temporarily via the [organization memberships API](/reference/authkit/organization-membership), but directory provisioning reapplies them during the next sync.
- The system **always replaces** previous directory provisioned role assignments with new ones, regardless of whether they came from an explicit or default mapping.

## Role-aware sessions

When a user signs into your app, a [user session](/authkit/sessions) is initiated. The authentication response includes an access token, a JSON Web Token (JWT), with role claims indicating the user organization membership's role(s) for that session.

## Organization roles

Organization roles are custom roles scoped to a particular organization. They are managed via the "Roles" tab under an organization in the WorkOS Dashboard.

![Roles tab for organization](https://images.workoscdn.com/images/5c09cd78-041f-4bb9-9e76-f7267106b22c.png?auto=format&fit=clip&q=50)

### Why might I use organization roles?

In some cases, an application's fixed set of roles may not meet the needs of certain organizations. For example, an organization may require a lesser privileged set of permissions for their members. Organization roles allow you to create custom roles, with the organization's desired set of permissions, without affecting access control for other organizations.

### Creating organization roles

By default, organizations have no custom organization roles and simply inherit the environment-level roles. You can create an organization role by clicking the "Create role" button on the organization's "Roles" tab. All organization role slugs are automatically prefixed with `org`.

![Create an organization role](https://images.workoscdn.com/images/90f3f3c0-3c66-48b2-b962-04b34f30599e.png?auto=format&fit=clip&q=50)

### Organization role configuration

Once you create the first role for an organization, that organization will have its own [default role](/authkit/roles-and-permissions/configure-roles-and-permissions/default-role) and [priority order](/authkit/roles-and-permissions/configure-roles-and-permissions/priority-order), independent from the environment.

New roles added to the environment will be available to the organization and placed at the bottom of the organization's role priority order.

### Using organization roles

Like environment-level roles, organization roles can be used in [role assignment](/authkit/roles-and-permissions/role-assignment), [sessions](/authkit/roles-and-permissions/role-aware-sessions), and the [organization membership API](/reference/authkit/organization-membership). No additional action is required to enable this behavior after creating organization roles.

### Deleting an environment role

When attempting to delete an environment role that's the default role for one or more organizations, you'll be prompted to select a new default role for all affected organizations. Organization members previously assigned the deleted role will be assigned the new organization default role.

![Select a replacement role](https://images.workoscdn.com/images/5e6f3e51-5de5-4bb1-a850-52b2196282b9.png?auto=format&fit=clip&q=50)

### Radar

Protecting against bots, fraud and abuse.

## Introduction

Radar adds automated protections on top of AuthKit by collecting signals on the behavior of users as they sign in to your app.
These signals feed into an engine that identifies abusive or anomalous behavior.
When Radar detects a suspicious authentication attempt, it can block or challenge that attempt based on the settings you configure.

Radar leverages device fingerprinting to identify which client is being used to authenticate with AuthKit.
This enables Radar to differentiate between legitimate and malicious users, so that automated protections won't impact your app's availability during an attack.
It's also a signal for suspicious behavior, such as when a device is used for multiple accounts or multiple devices are using the same account.

## Getting Started

Radar works with AuthKit without additional integration effort.
You can enable Radar directly from the WorkOS dashboard.
If you are interested in using Radar but are not an AuthKit customer, please reach out to [our team](mailto:support@workos.com), or for current customers, drop a note in your shared Slack channel.

## Dashboard

Radar's dashboard provides a summary of authentication activity in your app.
The top chart shows counts of suspicious events that Radar is detecting, along with automated actions that Radar took based on configuration.
The counts are updated in real time to make it easy to spot spikes in anomalous behavior.
To see historical trends, the time range for the chart can be toggled between 24 hours, 7 days and 30 days.

![Radar dashboard](https://images.workoscdn.com/images/87812b47-e72f-4edb-8164-e171545c9d8d.png?auto=format&fit=clip&q=50)

Below the top chart is a set of cards that show Radar detection activity for different user identifiers.
This is helpful to understand which types of devices, locations, users, etc. have been detected most often by Radar.
Each card is linked to the events page to drill into a list of individual event activity.

![Radar identifier cards](https://images.workoscdn.com/images/a2239641-2ef4-4742-bf9c-97189069ddaa.png?auto=format&fit=clip&q=50)

## Event list

A complete list of Radar events appears under the "Events" tab.
This list can be filtered by detection type, action taken, or a specific user ID or email.
By clicking into a single event, you can view all of the metadata related to that action including device, location, user agent and IP address.
Reviewing events in Radar can help inform when custom restrictions may be useful, such as allowing a known legitimate user to bypass detections, or blocking an IP range that is abusing your sign-in.

![Radar events](https://images.workoscdn.com/images/bde4e906-d680-4f18-baa5-d33fac803a7d.png?auto=format&fit=clip&q=50)

## Configuration

Radar gives you full control over the automated actions that are taken to suppress suspicious activity.
By enabling a detection, you can choose to block or challenge an authentication attempt that exhibits the detection's behavior.

**Blocking** an attempt will cause the authentication to fail, even if valid credentials are provided.
The user will see a message indicating their sign-in was not successful, and can reach out to an administrator for more detail.

**Challenging** an attempt will send the user an email with a one-time passcode (OTP).
The user is then prompted to enter that code to continue authentication.
Challenging suspicious authentication attempts with an OTP is effective in stopping bots that are capable of solving CAPTCHAs,
as well as malicious users who have stolen credentials but don't have access to the user's email account.

> Radar supports SMS challenges for sign ups in preview.
> Reach out to support via [email](mailto:support@workos.com) or Slack if you are interested in using SMS challenges.
> Additional fees may apply.

**Notifying** on an attempt will send an informational email to users and/or admins when Radar detects a suspicious behavior.
This is helpful to proactively make individuals aware that an attack might be taking place, or their account was compromised.

![Radar configuration](https://images.workoscdn.com/images/f9f112c8-e731-4b8a-9e83-7bcea4527ac9.png?auto=format&fit=clip&q=50)

Out of the box, Radar ships with the following detections:

### Bot detection

Block or challenge sign-in attempts that are initiated by a bot or program.

In addition to detecting that the client is a bot, Radar can differentiate between different types of bots
such as AI agents or search engine crawlers, giving developers the ability to control which kinds of bots are restricted.

![Radar bot configuration](https://images.workoscdn.com/images/9191ad0f-e898-4044-8947-cfb388ea7952.png?auto=format&fit=clip&q=50)

### Brute force

Block or challenge sign-in attempts that are part of an attempt to break into accounts using brute force.

These are attacks where a bad actor is trying many sign-ins over a short period of time.
Radar leverages the device fingerprint to identify and isolate bad actors from legitimate traffic, ensuring that your users
can use your app even when it’s under attack.

![Radar brute force configuration](https://images.workoscdn.com/images/28988276-a698-4517-8d9b-48e75d4e1b2b.png?auto=format&fit=clip&q=50)

### Impossible travel

Block or challenge sign-in attempts that occur from different geolocations in short succession.

By tracking device geolocation, Radar can detect when subsequent authentication requests are spread around the globe.
Radar will detect if these attempts happen over a short period where it's not possible for the person to physically travel that distance

![Radar impossible travel configuration](https://images.workoscdn.com/images/458b59af-a3e8-4646-b8a2-1bc04ae27e8e.png?auto=format&fit=clip&q=50)

### Repeat sign up

Block or challenge repeat sign up attempts from the same email. By default, AuthKit fully deletes users.

If your application allows for account deletion and has a free-trial, then users may be able to delete their account and sign up again to get a new free-trial.
This protection restricts an email to a max of three uses before denying further sign ups.

![Radar Repeat Sign Up protection modal](https://images.workoscdn.com/images/3e4a6937-c891-4076-b2bf-a2ed829c8004.png?auto=format&fit=clip&q=50)

### Stale accounts

Get notified when an account that has been dormant without use becomes active

In contexts such as financial services, a dormant account becoming active might be an indication that the account
has been taken over from the user and is being used for fraud.
For these kinds of apps, Radar can notify the user and administrator if an account that hasn’t been used in a while has a sign-in attempt.
Accounts are considered stale if there have been no successful sign-in in the past 60 days.

![Radar stale account configuration](https://images.workoscdn.com/images/f7a1a8e9-61ba-4c54-9cdb-d54ae7890092.png?auto=format&fit=clip&q=50)

### Unrecognized device

Get notified when a device that has never been used before signs in to an account

Using the device fingerprint, Radar checks if the device being used has been part of a successful sign-in before.
If it hasn’t, both the user and an administrator can be notified by email.

![Radar unrecognized device configuration](https://images.workoscdn.com/images/f574e3d9-feed-43bc-aadb-2790cbfd2ce0.png?auto=format&fit=clip&q=50)

## Managed lists

### Disposable email domains

Radar maintains a constantly updated list of email domains known to provide disposable email services.
Disposable email services may be used to bypass free account or free trial limits in your application.

You can choose to block or log registrations that match an email domain in this list.
Logging is useful to verify no adverse impact will occur before blocking all the domains.

![Radar Disposable Email List Protection](https://images.workoscdn.com/images/33d5c007-a5c3-4451-8da2-745bb4096268.png?auto=format&fit=clip&q=50)

### U.S. Sanctioned countries

Block users from countries under US Sanctions from signing up or logging into your application. Contact [support](mailto:support@workos.com) to get the current list of countries.

> If you need to block a different set of countries, please reach out to support via [email](mailto:support@workos.com) or slack to configure regional blocks.
> Radar supports any region in the [ISO 3166-1 specification](https://en.wikipedia.org/wiki/List_of_ISO_3166_country_codes).

![Radar US sanctions country managed lists](https://images.workoscdn.com/images/a2cff432-daf6-4c8f-a192-f33071afacf5.png?auto=format&fit=clip&q=50)

## Custom restrictions

Specific user identifiers can be configured to always allow or deny an authentication attempt.
Examples of using a custom restrictions:

- Restricting sign-ins to a corporate IP range
- Allow a script with a specific user agent to bypass bot detection
- Banning specific devices (i.e. iPods) from using your app
- Allowing certain users to bypass detections that are false positives

> Note: the allow list takes preference -- if an user matches an identifier that is in the allow list, they will bypass all other Radar rules.

![Radar restrictions configuration](https://images.workoscdn.com/images/bda02327-1d15-4e9c-b559-08b101930880.png?auto=format&fit=clip&q=50)

### Pipes

Enable your customers to connect their third-party accounts to your application.

## Introduction

Pipes allows your users to securely connect their third-party accounts to your
application. With Pipes, you can easily integrate with popular services like
GitHub, Slack, Google, Salesforce, and many more without managing OAuth flows,
token refresh logic, or credential storage.

> Reach out to support via [email](mailto:support@workos.com) or Slack if you are interested in early access.

## Configuring providers

To make an provider available to your users, you will need to configure it in
the WorkOS Dashboard.

Visit the _Pipes_ section of the WorkOS Dashboard to get started. Click _Connect
provider_ then choose the provider from the list. If you don’t see the provider
you need, please reach out to [our team](mailto:support@workos.com).

![A screenshot of a GitHub provider being configured via the Pipes page in the WorkOS Dashboard](https://images.workoscdn.com/images/88a1f135-53ba-4d26-92ef-6c61e924e178.png?auto=format&fit=clip&q=50)

### Shared Credentials

For the fastest setup, you can use WorkOS-managed shared credentials in sandbox
environments. This allows users to connect immediately without requiring you to
create OAuth applications with each provider.

1. Specify the required **scopes** for your application.
2. Provide an optional **description**. This will be used in the widget to inform users
   on how your application will use their data from the provider.

### Custom Credentials

For production applications, configure the provider with your own OAuth credentials:

1. **Create an OAuth application** within the provider's dashboard.
   1. You can find instructions on setting up the provider in the documentation section of the setup modal.
1. Use the provided **redirect URI** when configuring the provider.
1. Set the **client ID and secret** from the provider.
1. Specify the required **scopes** for your application.
   1. You may need to set these scopes in the provider configuration as well.
1. Provide an optional **description**. This will be used in the widget to inform users
   on how your application will use their data from the provider.

Commonly used scopes are provided in-line, but you should consult each provider's documentation for the full list of available scopes.

## Provider management in your application

The [Pipes Widget](/widgets/pipes) provides a pre-built UI for users to connect
and manage their connected accounts. The widget shows the user which
providers are available, and lets them easily initiate the authorization
flow. It communicates with the WorkOS API and stores the connection information
for the user. If there's ever a problem with the user’s access token, the widget
will let them know they need to reauthorize.

![Pipes widget screenshot](https://images.workoscdn.com/images/e84a33bb-0510-4d85-9041-33b1a0ce938c.png?auto=format&fit=clip&q=50)

> The description in the widget is set in the provider's configuration in the WorkOS Dashboard.

## Fetching access tokens

Once a user has connected a provider, you can [fetch access tokens](/reference/pipes) from your
backend to make API calls to the connected service on their behalf. Pipes takes
care of refreshing the token if needed, so you’ll always have a fresh token. If
there’s a problem with the token, the endpoint will return information about the issue so you can
direct the user to the correct it. This may require sending the user to re-authorize directly
or via the page with the Pipes widget.

### Passkeys

Configuring passkey authentication and enrollment.

## Introduction

Passkey authentication allows users to sign up and sign in to your application using public-key cryptography, which is more secure than a traditional password.

Passkeys are built on top of the [WebAuthn](https://developer.mozilla.org/en-US/docs/Web/API/Web_Authentication_API) standard that enable the user's device to securely store the passkey's private key, with access protected by biometrics, such as a fingerprint. When the user signs in, the private key is used to sign a payload that is verified with the matching public key.

You can read more about how WebAuthn and passkeys work at the [official FIDO alliance passkey website](https://passkeys.dev/), the consortium behind passkey web standards.

## Passkey configuration

Passkeys can be enabled in the _Authentication_ section of the [WorkOS Dashboard](https://dashboard.workos.com).

![The WorkOS Dashboard displaying the configuration dialog for passkeys](https://images.workoscdn.com/images/5ed77bef-cfa0-4a40-958f-d19b23a1ec1b.png)

> **Developers should configure an [AuthKit custom domain](/custom-domains/authkit) before enabling passkeys in production**. Passkeys are bound to the domain they were registered on. Adding the domain later would prevent the usage of passkeys that were registered on the old domain.

### Enabling progressive enrollment

Optionally, your users who are still using password-based authentication can be prompted to create a passkey on their next sign-in. This flow, known as "progressive enrollment", is disabled by default but can be enabled alongside passkey authentication in the WorkOS Dashboard.

![AuthKit displaying the passkey progressive enrollment prompt.](https://images.workoscdn.com/images/c541f4f6-5956-489d-be7e-c8b242c9923d.png)

If users skip passkey enrollment they will be reminded every two weeks, and additionally have the option to never be prompted again if they prefer passwords.

### Multi-factor auth

If [MFA](/authkit/mfa) is also enabled and required, users who sign in with a passkey will not be prompted for a separate TOTP code. AuthKit treats passkeys as both a first and second factor by requiring user verification when a passkey is presented.

User verification in the context of a passkey means the passkey must be combined with another "authorization gesture", like the scanning of a fingerprint, or entering a separate PIN.

---

## Integrating via the API

Passkey authentication is currently only available with the hosted UI in AuthKit.

### AuthKit

Easy to use authentication APIs designed to provide a flexible, secure, and fast integration.

## Introduction

WorkOS AuthKit is a user management platform that provides a set of user authentication and organization security features that securely power your application. Features are designed to be flexible, while offering a fast integration to handle all of the user management complexity that comes with advanced business and customer needs.

## Authentication

AuthKit supports many authentication methods out of the box. They are designed to grow with your app, from the simplest use case all the way to complex Enterprise [SSO](/authkit/sso) for your largest customers, these include:

- [Single Sign-On](/authkit/sso)
- [Email & Password](/authkit/email-password)
- [Social Login](/authkit/social-login)
- [Multi-Factor Auth](/authkit/mfa)
- [Magic Auth](/authkit/magic-auth)

These features are available via [AuthKit](/authkit), or through the public [API](/reference/authkit). AuthKit provides an easy to integrate, pre-built authentication flow, while integrating against the API allows you to implement your own UI and Sign In flow.

## Fast integration

The fastest way to integrate AuthKit features is with the [Hosted UI](/authkit/hosted-ui).

![AuthKit Sign In UI](https://images.workoscdn.com/images/b8286e7e-3ad9-4d43-99d5-e022e6bb36fb.png?auto=format&fit=clip&q=80)

AuthKit abstracts away many of the UX and WorkOS API calling concerns automatically, allowing you to focus on building your application. See the [Quick Start](/authkit) guide for more information on how to get started.

## Security

Adopting AuthKit in your app provides a wealth of security benefits.

- Best-in-class [email verification](/authkit/email-verification), enabled by default.
- Safe [identity linking](/authkit/identity-linking) and merging of duplicate accounts. This protects against spoofing and reduces the user support burden.
- Identity Provider (IdP) differences are normalized, so that you get consistency across user profiles. This reduces the likelihood of security issues related to differing semantics across providers.
- Automatic bot detection and blocking, to protect against brute force attacks.
- [Multi-Factor Authentication (MFA)](/authkit/mfa) available per environment to further enhance your app’s security posture.

## Getting started

Start integrating AuthKit into your app today, check out the [Quick Start](/authkit) guide to get started with AuthKit or review the [API Reference](/reference/authkit) material.

### Organization Authentication Policies

Customize available authentication methods for each organization.

## Introduction

Some organizations may prefer to limit their users to specific authentication methods to meet security requirements. These organization-level customizations can be configured on the organization page in the Dashboard.

## Domain policy

A domain policy allows an organization to control authentication and membership behavior of users whose email domain matches one of the organization’s [verified domains](/authkit/domain-verification). Domain policies are enforced for _all_ users with email domains included in the policy, regardless of their membership status within the organization or the organization selected during sign-in.

Additionally, users provisioned through a [directory](/authkit/directory-provisioning) with an email domain included in the organization's domain policy will be automatically added as active members of the organization without needing an invitation.

![Configuring a domain policy in the dashboard](https://images.workoscdn.com/images/b98493d9-f9fe-475d-a448-f9099558cd19.png?auto=format&fit=clip&q=50)

> Only one organization can include a specific domain in its domain policy per environment.

### Requiring SSO by default

When an SSO connection is first set up for an organization, all non-SSO authentication methods for the organization are automatically disabled. Typically, the IT admin who configures SSO intends for it to be the sole authentication method. Any additional methods can be enabled manually if the organization prefers.

## Organization policy

Unlike the domain policy, which is enforced regardless of the organization selected during sign-in, the organization policy is enforced when a member selects the specific organization during sign-in.

The organization policy can require authentication via its SSO connection or require MFA. This is particularly useful for guest members who do not have an organization email domain, or when the organization cannot include an email domain in its domain policy because the domain is managed by another organization.

![Configuring an organization policy in the dashboard](https://images.workoscdn.com/images/bcc25d91-602d-420f-a465-6ea790d27005.png?auto=format&fit=clip&q=50)

### Modeling Your App

Learn how to architect your WorkOS integration

## Introduction

WorkOS has a wide suite of products to solve your business needs. This guide explains some of the common choices when it comes to modeling your integration.

WorkOS is designed to support a wide array of use cases and architectural scenarios, from simple business-to-consumer (B2C) user authentication to complex business-to-business (B2B) architectures with multiple organizations and authentication policy enforcement.

Features are designed to grow with you, allowing you get started easily and expand your security options as you onboard larger and larger enterprise customers.

Whether you are looking to add the initial authentication piece to a new application, or exploring migration from an existing vendor, you may find yourself asking:

- Does WorkOS fit into my existing architecture?
- Can I start small and later adopt more features?
- I have very specific requirements, can I still use WorkOS?

In most cases the answer is yes, and the aim of this guide is to help you understand how. We’ll cover the terminology used in this space, describe some common B2C and B2B flows, and finally demonstrate some scenarios that explain how everything fits together.

## Understanding the authentication flow

Supporting Email + Password, Single Sign-On, OAuth, Magic Auth, and other authentication concerns on your own is a complex task. You'd need to understand the authentication process, as well as model your applications sign-in and signup user interfaces to account for and handle all possible routes, error states and edges cases.

There are three main ways to add WorkOS authentication to your application:

### AuthKit

A [hosted login solution](/authkit) that provides a customizable UI and supports a wide range of authentication methods.

### Custom AuthKit UI

If you prefer to craft your own UI in your own stack, you can use the [AuthKit APIs](/reference/authkit) directly.

### Standalone Single Sign-On (SSO)

For applications that are only interested in [SSO capabilities](/reference/sso).

In the majority of cases we recommend using the hosted AuthKit solution.

![AuthKit authentication flow diagram](https://images.workoscdn.com/images/12d849cf-c710-493d-a189-48264d1c3ed7.png?auto=format&fit=clip&q=80)[border=false]

On successful completion, AuthKit will return an authentication code to your application via your specified redirect URI, this is exchanged for the user object and used to create a session.

See the [Quick Start guide](/authkit) for more information on how to implement this.

## Authentication methods

Prior to building your integration, it is useful to think about which authentication methods are part of your requirements. Typical consumer authentication methods include the following:

### Email + Password

The most common method of authentication, users sign up or sign in to your app with email and password. This method is enabled by default.

### OAuth

OAuth, also known as Social Login, is when a user logs in with an account belonging to a different service. Examples include logging in with your Google, Microsoft or GitHub account instead of making a new account with your app. These authentication methods can be configured from the WorkOS dashboard.

### Magic Auth

Also known as Passwordless, Magic Auth authentication works by emailing the user a unique, one-time-use 6 digit code which they then use to authenticate.

A similar technique is Magic Link, where the user can log in by clicking a link emailed to them. This method has proven to be unreliable as IT teams often employ security measures that scan user emails and programmatically click any links found in the email, invalidating the Magic Links. As such, WorkOS has deprecated Magic Links in favour of Magic Auth.

### SSO

The favored authentication method by enterprise sized companies, SSO allows an organization's users to sign in with a single ID to related, yet independent software systems.

The AuthKit APIs make the above easy to implement using your own UI, or you can use AuthKit's Hosted UI for a fully hosted experience.

## Single-tenant and multi-tenant models

You might encounter the concepts of single-tenant and multi-tenant when discussing an application’s authentication model. Single-tenant and multi-tenant architectures offer different approaches to managing resources and data in software applications, especially in cloud services or Software as a Service (SaaS) environments.

![Diagram of single and multi-tenant models](https://images.workoscdn.com/images/48ef829e-6758-4606-a696-7963beb803b2.png?auto=format&fit=clip&q=80)[border=false]

### Single-tenant

In the context of authentication and authorization, single-tenant refers to a software architecture where a separate instance of the software is set up for each client on separate servers or virtual machines.

Each organization is paired with its own instance of both the application and the underlying database. This approach offers full data isolation between customers, but comes at the cost of being more resource-intensive and costly. Each client’s setup may require separate maintenance, updates, and support.

### Multi-tenant

Multi-tenant refers to a software architecture where a single instance of the software serves multiple customer organizations, known as tenants.

Each tenant's data is isolated and remains invisible to other tenants because the software is designed to securely handle this data across all tenants.

In the context of WorkOS, a multi-tenant application could be accomplished by the use of [Organizations](/reference/organization) and [Organization Memberships](/reference/authkit/organization-membership) to ensure that end-users only have access to the data they are authorized to.

By default WorkOS comes with two environments: staging and production. The former is for development and testing, the latter for live traffic. For single-tenant applications, new environments can be added to your WorkOS account to accommodate each of your users. For more information or to request new environments, please reach out to [support](mailto:support@workos.com).

## Simple B2C model

![Simple B2C model](https://images.workoscdn.com/images/55ffa6da-ec2b-4f03-881c-3f68e5a2a818.png?auto=format&fit=clip&q=80)[border=false]

In a simple B2C model, all users belong to the application, users do not require assignment or management by an organization in order to perform actions or access resources. In this model your customer is also the end-user, they sign up or sign in to your app in order to use your services without being associated with an organization.

### User data in B2C models

It's common to have a single users table in your database linking to the WorkOS user (among other services), you may optionally decide to use WorkOS as the source of truth for other user information such as `firstName`, `lastName` or `email`, though this depends on your application requirements.

In this model, WorkOS's primary role is to authenticate users and store them in a simple, flat structure. This is the default model and is the simplest to implement, but as your needs grow you may find yourself needing to add additional functionality.

## B2B modeling

In contrast to the B2C example, a typical B2B model introduces the concept of Organizations.

Organizations relate a set of users and provide a structure to manage and enforce authentication methods and resource access. We can extend our previous diagram to introduce this concept.

![B2B model](https://images.workoscdn.com/images/21e07368-ab1a-460e-8dcb-499d763cbdd4.png?auto=format&fit=clip&q=80)[border=false]

In this model, we have a one-to-many relationship between users and organizations, a user can belong to many organizations and an organization can have many users, this relationship is expressed by our Organization Membership table. Unlike the B2C example, the customer here is a (usually enterprise) company that has its own users, typically employees or contractors.

While you can still use all the authentication methods outlined above with B2C models, the main difference between B2C and B2B is that the latter tends to prefer SSO as its authentication method of choice.

This model starts to become incredibly powerful as we can now capture more complex scenarios. For example, we could leverage features such as domain verification and domain policies to control authentication behavior and provision members automatically.

### Domain verification and domain policies

[Domain verification](/authkit/domain-verification) is the process of proving ownership of a specific domain, typically handled by a company’s IT department. Once a domain is verified, all existing and future users with email addresses matching the domain are, by default, managed by the organization's [domain policy](/authkit/organization-policies/domain-policy). This allows the organization to control authentication and membership behavior for these users, such as requiring these users to authenticate via SSO.

Users signing in with SSO with a verified email domain are automatically considered verified and do not need to complete the email verification process.

### Integrating SSO with the Admin Portal

When onboarding a new enterprise customer, they will likely ask to integrate their SSO connection provided by their own Identity Provider (IdP) with your application.

The Admin Portal provided by WorkOS makes this process easy to implement by providing a hosted UI that guides your user through SSO configuration.

![Admin Portal flow](https://images.workoscdn.com/images/77050e33-5dcc-480f-a0a7-3f46a6b33abf.png?auto=format&fit=clip&q=80)[border=false]

With the Admin Portal, the process of configuring your customer’s SSO integration is reduced to creating an Organization in WorkOS, saving relevant data in your own database and then redirecting the user to the Admin Portal to guide them through the configuration flow.

### User data in B2B models

As with B2C models, user data on the WorkOS side can be used as the source of truth, but the far more common scenario is to store user information in your own database which links to the WorkOS user.

The AuthKit and SSO products can be used independently, with the latter acting as authentication middleware which intentionally does not handle user database management for your application. If you're unsure which is best for your business, it's recommended to stick with AuthKit as it gives you the aforementioned flexibility to add and/or remove features as your needs grow.

## Example scenarios

As your application grows you may find yourself needing to add additional features to support customer needs. AuthKit is designed with this is mind. As you move upmarket and take on larger and larger customers, you can easily adopt and extend your feature set within an established architecture. The following scenarios explain some common use cases with specific feature sets.

- [SSO with contractors](/authkit/sso-with-contractors) – Enforcing Organization SSO with external guest members.
- [Invite-only Signup](/authkit/invite-only-signup) – An invite only application that allows existing users to invite new members.

### Migrating to AuthKit

Guidance on moving your existing users to WorkOS.

## Introduction

WorkOS provides a [range of guides](/migrate) to help you migrate your existing integration to AuthKit.

## Migrate from another service

These guides will walk you through the process of moving your users and organizations to WorkOS from another service.



## Migrating an existing WorkOS integration

If you already have an integration with WorkOS (for example, using the [standalone API](/sso) to provide SSO to your customers), you can migrate to AuthKit and take advantage of all of the features it provides by following [this guide](/migrate/standalone-sso).

### Multi-Factor Authentication

Add an additional layer of security to your application.

## Introduction

Multi-Factor Authentication (MFA) is an additional method of securing your application. MFA adds a layer of security during sign in that requires a user to provide an additional time-based one-time password (TOTP).

## Getting started

AuthKit will make the necessary API calls to handle first-time configuration of users’ MFA factors automatically, and validate one-time codes as part of the authentication flow.

### Enabling MFA

MFA can be enabled in the _Authentication_ section of the [WorkOS Dashboard](https://dashboard.workos.com). New and existing users will be required to set up multi-factor authentication with an authenticator app that supports one-time passcodes before they can sign in.

> The MFA requirement **does not** apply to SSO users.

![Dashboard showing how to enable MFA](https://images.workoscdn.com/images/f660826c-cb2d-4912-ba10-1f622d6a447d.png?auto=format&fit=clip&q=80)

![AuthKit displaying MFA configuration](https://images.workoscdn.com/images/31fcbe12-63c2-47e2-9685-d45fe4d41fb5.png?auto=format&fit=clip&q=80)

---

## Integrating via the API

If you’d prefer to build and manage your own authentication UI, you can do so via the AuthKit [Multi-Factor API](/reference/authkit/mfa).

Examples of building custom UI are also [available on GitHub](https://github.com/workos/authkit).

### Metadata and External IDs

Store additional information about users and organizations.

## Introduction

Metadata is an attribute of organizations and users that allows you to store additional information about these objects, structured as key-value pairs. For example, you can use metadata to store information about a user's profile picture, or the organization's address.

External identifiers allow you to associate organizations and users with an identifier in your own system.

---

## External identifiers

External identifiers are an attribute of organizations and users that allows you to associate these objects with an identifier in your own system. Once you have set an external identifier for an object, you can query on it via dedicated endpoints in the WorkOS API.

External identifiers must be unique within your environment and are limited to 64 characters.

## Metadata

You can add up to 10 key-value pairs to an organization or user within these data limits:

- **Key**: Up to 40 characters long. ASCII only.
- **Value**: Up to 600 characters long. ASCII only.

If your integration requires more than 10 key-value pairs, consider storing the additional data in your own external database and use an external identifier to associate the data with an organization or user.

> Never store sensitive information in metadata such as passwords, API keys, or other private information.

Metadata is returned in the response body for backend API operations that return organization or user objects, but not in the response body of the [User Authentication](/reference/authkit/authentication) operations. If you want to publicly expose metadata properties from users or organizations in your access tokens, you can use JWT templates to customize claims in your application's access tokens.

## Set an external identifier

To set an external identifier for an organization or user, include the `external_id` property in the request body of the [Create an organization](/reference/organization/create) or [Create a user](/reference/authkit/user/create) endpoints.



To update an external identifier, include the `external_id` property in the request body of the [Update an organization](/reference/organization/update) or [Update a user](/reference/authkit/user/update) endpoints.

## Query by external identifier

To query an organization or user by their external identifier, use the [Get organization by external identifier](/reference/organization/get-by-external-id) or [Get user by external identifier](/reference/authkit/user/get-by-external-id) endpoints.

## Add and update metadata

Updates to metadata are partial. This means that you only need to include the metadata attributes that you want to update.

Metadata can be included in the request body of the following endpoints:

- [Create an organization](/reference/organization/create)
- [Update an organization](/reference/organization/update)
- [Create a user](/reference/authkit/user/create)
- [Update a user](/reference/authkit/user/update)

To add a metadata attribute to an entity, include the key and value pair in the `metadata` object of the request body.

```json
{
  "metadata": {
    "key": "value"
  }
}
```

To update a metadata attribute, include the key and value pair in the `metadata` object of the request body.

```json
{
  "metadata": {
    "key": "new_value"
  }
}
```

To delete a metadata attribute, set the key to `null` in the `metadata` object of the request body.

```json
{
  "metadata": {
    "key": null
  }
}
```

To delete all metadata attributes, set the `metadata` property an empty object.

```json
{
  "metadata": {}
}
```

## Exposing metadata in JWTs

Custom metadata and external identifiers can be exposed as claims in JWTs using [JWT Templates](/authkit/jwt-templates).

### Model Context Protocol

How to use AuthKit as the authorization server for your MCP server.

## Introduction

[Model Context Protocol](https://modelcontextprotocol.io/) (MCP) is a new protocol that standardizes how LLM-based clients can programmatically interact with applications. This includes querying data, in the form of resources, or taking direct actions in the application in the form of tools.

This guide is intended for application developers implementing an MCP _server_ that requires authentication. WorkOS and AuthKit can provide a secure way to manage access to your MCP server with minimal effort.

> If you have feedback or questions about MCP, we'd love to hear from you! Reach out to [WorkOS support](mailto:support@workos.com?subject=MCP%20Authentication%20with%20AuthKit) or via your team's WorkOS Slack channel.

## Authorization

The MCP specification builds on industry-standard protocols like [OAuth 2.0](https://datatracker.ietf.org/doc/html/rfc6749) in order to secure access to an MCP server. It makes the following distinctions between entities in the authorization flow:

- **Resource Server** – This is your MCP server, which you may choose to build using the [official Model Context Protocol SDKs](https://github.com/modelcontextprotocol).
- **Authorization Server** – This is AuthKit, which is a spec-compatible OAuth authorization server. While the spec allows the authorization and resource server to be the same, it can be architecturally simpler to delegate to an existing authorization server like AuthKit.

Support for MCP authorization is built on top of [WorkOS Connect](/authkit/connect/oauth), which provides all of the necessary OAuth API endpoints MCP clients will use to authenticate. You can view your AuthKit metadata by making a request to its `/.well-known/oauth-authorization-server` endpoint:

```bash
curl https://authkit_domain/.well-known/oauth-authorization-server | jq
{
  "authorization_endpoint": "https://authkit_domain/oauth2/authorize",
  "code_challenge_methods_supported": ["S256"],
  "grant_types_supported": ["authorization_code", "refresh_token"],
  "introspection_endpoint": "https://authkit_domain/oauth2/introspection",
  "issuer": "https://authkit_domain",
  "registration_endpoint": "https://authkit_domain/oauth2/register",
  "scopes_supported": ["email", "offline_access", "openid", "profile"],
  "response_modes_supported": ["query"],
  "response_types_supported": ["code"],
  "token_endpoint": "https://authkit_domain/oauth2/token",
  "token_endpoint_auth_methods_supported": [
    "none",
    "client_secret_post",
    "client_secret_basic"
  ]
}
```

## Integrating

AuthKit handles the authentication flow so your MCP server only needs to implement the following concerns:

1. Verifying access tokens issued by AuthKit for your MCP server.
1. Direct clients to AuthKit using standardized metadata endpoints.

### Enabling Client ID Metadata Document (CIMD)

MCP clients often have no prior relationship with your MCP server. The MCP protocol specifies that authorization servers (AuthKit) should support [Client ID Metadata Document](https://datatracker.ietf.org/doc/html/draft-ietf-oauth-client-id-metadata-document-00) (CIMD) to allow MCP clients to identify themselves.

Client ID Metadata Document is off by default, but you should enable it in the WorkOS Dashboard under _Connect_ → _Configuration_.

Client ID Metadata Document was added to the MCP specification in November 2025. You can enable the previous approach, [Dynamic Client Registration](https://datatracker.ietf.org/doc/html/rfc7591) (DCR), in the WorkOS Dashboard for backwards compatibility with MCP clients that don't yet support Client ID Metadata Document.

![A screenshot of the Connect Configuration page in the WorkOS Dashboard.](https://images.workoscdn.com/images/c0ed8f95-9b41-462d-98e8-934b8f022f22.png)

### Token Verification

Your app needs to gate access to the MCP endpoints by verifying access tokens issued by AuthKit for your MCP server. This process is very similar to [the way any Connect JWT is verified](/authkit/connect/oauth/verifying-tokens), with one important addition:

```js
;

const JWKS = createRemoteJWKSet(new URL('https://authkit_domain/oauth2/jwks'));

const WWW_AUTHENTICATE_HEADER = [
  'Bearer error="unauthorized"',
  'error_description="Authorization needed"',
  `resource_metadata="https://mcp.example.com/.well-known/oauth-protected-resource"`,
].join(', ');

const bearerTokenMiddleware = async (req, res, next) => {
  const token = req.headers.authorization?.match(/^Bearer (.+)$/)?.[1];
  if (!token) {
    return res
      .set('WWW-Authenticate', WWW_AUTHENTICATE_HEADER)
      .status(401)
      .json({ error: 'No token provided.' });
  }

  try {
    const { payload } = await jwtVerify(token, JWKS, {
      issuer: 'https://authkit_domain',
    });

    // Use access token claims to populate request context.
    // i.e. `req.userId = payload.sub;`

    next();
  } catch (err) {
    return res
      .set('WWW-Authenticate', WWW_AUTHENTICATE_HEADER)
      .status(401)
      .json({ error: 'Invalid bearer token.' });
  }
};
```

Note the addition of a [`WWW-Authenticate`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/WWW-Authenticate) header with the `resource_metadata` challenge parameter containing a `/.well-known/oauth-protected-resource` URL. This allows clients to dynamically discover the appropriate authorization server, enabling zero-config interoperability between different MCP clients and servers.

### Metadata

Your MCP server should implement `/.well-known/oauth-protected-resource` endpoint mentioned in the previous section, returning the following minimal JSON response:

```js
app.get('/.well-known/oauth-protected-resource', (req, res) =>
  res.json({
    resource: `https://mcp.example.com`,
    authorization_servers: ['https://authkit_domain'],
    bearer_methods_supported: ['header'],
  }),
);
```

MCP clients that support metadata discovery will automatically fetch this metadata when they initially encounter a `401 Unauthorized` error from the middleware implemented above. Since AuthKit is included in the metadata under `authorization_servers` the MCP client will redirect the user to AuthKit in order for them to sign in.

![The authorization prompt users will be shown when giving access to an MCP client.](https://images.workoscdn.com/images/ce1d133c-503c-4abc-8422-c274bbd8786c.png)

Behind the scenes, AuthKit implements the necessary authorization and token endpoints so that your application doesn't need to. You can read more in the [latest version of the MCP authorization spec](https://github.com/modelcontextprotocol/modelcontextprotocol/blob/901ac03e1c72827acb8017f80eeb14e38ad8ba42/docs/specification/draft/basic/authorization.mdx) but most apps can consider them implementation details of AuthKit as the authorization server.

Upon successful authentication the client will receive credentials and can start making requests to your application's MCP endpoints.

## Compatibility

The MCP space is rapidly evolving and not every client may support the latest version of the specification.

In particular, some clients may not support [OAuth 2.0 Protected Resource Metadata](https://datatracker.ietf.org/doc/html/draft-ietf-oauth-resource-metadata-13) and its `/.well-known/oauth-protected-resource` endpoint, instead attempting to fetch [OAuth 2.0 Authorization Server Metadata](https://datatracker.ietf.org/doc/html/rfc8414) directly from your application's MCP server.

For these clients, your server can implement a metadata endpoint as a proxy with AuthKit as the upstream source of truth:

```js
app.get('/.well-known/oauth-authorization-server', async (req, res) => {
  const response = await fetch(
    'https://authkit_domain/.well-known/oauth-authorization-server',
  );
  const metadata = await response.json();

  res.json(metadata);
});
```

Clients will use AuthKit as the authorization server and the rest of the flow will be identical.

## Standalone MCP OAuth

If you already have an existing authentication system in your application, you can use [Standalone Connect](/authkit/connect/standalone) to integrate AuthKit's OAuth capabilities with your MCP server while preserving your current authentication stack.

Standalone Connect is particularly useful for MCP scenarios where you:

- Want to add MCP support to an application with existing user authentication.
- Need to maintain consistency with your current login experience and user management.
- Require custom authentication logic.

### How it works

With Standalone Connect for MCP, the authentication flow works differently from the standard AuthKit integration described above:

1. **MCP clients** initiate the OAuth flow for your MCP server with AuthKit as your authorization server.
2. **AuthKit redirects** users to your application's Login URI instead of showing AuthKit's login page.
3. **Your application** authenticates users using your existing authentication system.
4. **Your application** calls [AuthKit's completion API](/reference/workos-connect/standalone/complete) to complete the OAuth flow.
5. **AuthKit** handles the OAuth consent, token issuance, and returns control to the MCP client.

This approach allows you to maintain full control over the user authentication experience while leveraging AuthKit's OAuth infrastructure for MCP client authorization.

### Implementation considerations

When using Standalone Connect with MCP:

- You'll still need to enable [Client ID Metadata Document](#enabling-client-id-metadata-document-cimd) to support MCP clients that register themselves.
- [Token verification](#token-verification) works identically—your MCP server validates tokens using the same JWT verification process.
- The [metadata endpoints](#metadata) remain the same, ensuring MCP clients can discover and interact with your server.
- Your Login URI must be configured in the WorkOS Dashboard and handle the `external_auth_id` parameter from AuthKit.

The key difference is in the authentication step: instead of users signing in through AuthKit's interface, they authenticate with your existing system, and AuthKit handles only the OAuth authorization and token issuance portions of the flow.

For detailed implementation steps and code examples, see the [Standalone Connect documentation](/authkit/connect/standalone).

### Magic Auth

Maximize user experience and security with passwordless authentication.

## Introduction

Magic Auth is a passwordless authentication method that allows users to sign in or sign up via a unique, six digit one-time-use code sent to their email inbox.

## Getting started

AuthKit will make the necessary API calls to issue one-time-use codes via email and provide input verification and authentication automatically. If desired, you can [send these emails yourself](/authkit/custom-emails).

### Enabling Magic Auth

Magic Auth can be enabled in the _Authentication_ section of the [WorkOS dashboard](https://dashboard.workos.com). Users will then be able to sign in or sign up via Magic Auth on the AuthKit authentication page.

One-time-use codes expire after **10 minutes**.

![Dashboard showing how to enable Magic Auth](https://images.workoscdn.com/images/78df279e-3bd5-451e-a0a0-7e93ed5e5bd5.png?auto=format&fit=clip&q=80)

![AuthKit displaying email sign-in](https://images.workoscdn.com/images/9129ad29-d488-462b-ad85-3a2a7908235d.png?auto=format&fit=clip&q=80)

![AuthKit displaying code input UI](https://images.workoscdn.com/images/1810724e-466f-4f76-b905-12167a051cdf.png?auto=format&fit=clip&q=80)

---

## Integrating via the API

If you’d prefer to build and manage your own authentication UI, you can do so via the AuthKit [Magic Auth API](/reference/authkit/magic-auth).

Examples of building custom UI are also [available on GitHub](https://github.com/workos/authkit).

### JWT Templates

Customize the claims in your application's access tokens.

## Introduction

JWT templates allow you to customize the claims in your application's access tokens issued by WorkOS. You can leverage core attributes of users and organizations, in addition to [custom metadata](/authkit/metadata) you set on these objects.

---

## Create a JWT template

JWT templates are managed in the Authentication section of the [WorkOS Dashboard](https://dashboard.workos.com/). Under the sessions section, choose Configure JWT Template.

![WorkOS dashboard demonstrating the position of the "Configure JWT template" button](https://images.workoscdn.com/images/27daa36f-ca4e-4733-bd1b-ebb961f45454.png?auto=format&fit=clip&q=50)

JWT templates are comprised of a template string which is rendered with the user and organization context after a user successfully authenticates.

## Example usage



## Syntax

### 1. **Basic Variable Interpolation**

You can reference variables inside the template.



### 2. **Fallback Values** (`||` operator)

If the first value is `null` or undefined, the next value in the fallback chain is used.



### 3. **String Literals**

Strings can be used as fallback values.



### 4. **Concatenation in Strings**

Multiple variables can be used within a single string.



### 5. **Object Interpolation**

Interpolating entire objects and arrays is allowed if they are valid JSON objects. This is not allowed inside string literals and will throw a validation error.



### 6. **Reserved Keys Restriction**

The following keys cannot be used in templates:

- `iss`
- `sub`
- `exp`
- `iat`
- `nbf`
- `jti`

Any attempt to use these keys will result in a validation error.

## Whitespace Handling

The rendering engine trims whitespace from the beginning and end of string values.

## Error Handling

If the template contains invalid syntax, an error will be thrown:

- **Template must render to an object with at least one explicitly defined top-level key:** If the template does not evaluate to a valid JSON object (e.g., an array or primitive value). Example:
  ```js
  [ {{ user.email }} ]
  ```
- **Keys reserved (`iss`, `sub`, `exp`, etc.):** These keys cannot be used in the template.
  ```js
  { "iss": {{ user.email }} }
  ```
- **String encapsulated expression cannot contain object reference:** Objects cannot be interpolated inside a string.
  ```js
  { "user": "{{ user.metadata }}" }
  ```
- **Invalid expression segment:** Logical operators (`||` with empty operands) or malformed expressions are not allowed.
  ```js
  {{ user.email && user user }}
  {{ user.email || || user.email }}
  ```
- **Template parse error: missing '}}':** A template block was opened but never closed.
  ```js
  {{ user.id
  ```
- **Expression cannot be empty:** An empty expression inside `{{ }}` is invalid.
  ```html
  {{}}
  ```
- **Missing closing braces:** `Template parse error: missing '}}'`
- **Invalid key usage:** `Keys reserved (iss, sub, exp, etc.)`
- **Unknown variables:** `Invalid path: "unknown.variable"`

## Null Handling

JWT templates provide built-in handling for `null` values to ensure access tokens only contain populated claims.

### **1. Removing Top-Level Null Values**

If an expression evaluates to `null`, the corresponding key is removed from the final JSON output.

#### **Example**



### **2. Handling Null Values in Concatenated Strings**

If a `null` value appears in a string concatenation, it is replaced with an empty string (`""`) instead of being removed.



### **3. Using Fallbacks to Avoid Null Values**

The `||` operator can be used to provide a fallback value when an expression evaluates to `null`.



## Size limits

JWT templates must render to a JSON object that is 3072 bytes or smaller due to cookie size constraints in web browsers.

### Just-in-time Provisioning

Automatically provision users and memberships with JIT provisioning.

## Introduction

JIT provisioning automatically creates users and organization memberships when a user signs in. This feature allows users to access an organization’s resources without requiring manual invitations from the IT admin.

## Automatically add users with verified domains as members

Users with [verified email domains](/authkit/domain-verification) can be automatically added as members to an organization through the organization's [domain policy](/authkit/organization-policies/domain-policy). This feature is useful when an application or organization wants to automatically group individuals into the same workspace based on their email domain.

![Configuring a domain policy in the dashboard](https://images.workoscdn.com/images/b98493d9-f9fe-475d-a448-f9099558cd19.png?auto=format&fit=clip&q=50)

## SSO JIT provisioning

When a user signs in, WorkOS detects when their email domain matches a verified domain of an organization and prompts the user to sign in through the organization's IdP. If the user existed in WorkOS previously, that existing user is automatically added to the organization. Otherwise, a new user is created and added to the organization.

![Configuring just-in-time provisioning for SSO users in the dashboard](https://images.workoscdn.com/images/90a85516-ed7a-4bd4-88a5-384b2f818436.png?auto=format&fit=clip&q=50)

### Guest provisioning

SSO JIT provisioning is not fully supported for guests whose email domain has not been [verified](/authkit/domain-verification) by the organization.

For example, an IT admin may want to gate all contractor access through their IdP (to enable access revocation across applications) but the contractor prefers to use their own email address.

Instead, guest users must be [invited](/authkit/invitations) to join the organization before they are able to sign in with the organization's IdP.

## Disabling JIT provisioning

Both automatic membership by email domain and SSO JIT provisioning are enabled by default but can be disabled in the [WorkOS Dashboard](https://dashboard.workos.com).

Disabling these features may be useful if the IT admin prefers to manually control membership through [invitations](/authkit/invitations).

### Invite-only signup

Modeling an invite-only application without a public signup page.

## Introduction

In this scenario, we outline the considerations, concepts, and best practices for modeling a closed-registration application in which users may only be added to the application via an invitation.

## Goals & requirements

Imagine a company that wishes to model an invite only application that requries an exclusive invite to access. The product is yet to launch, and as the initial release approaches they plan to seed memberships from a small subset of organizations and later allow existing users to invite new members from a quota.

The requirements are as follows:

- Signup should be unavailable to the general public.
- An initial set of invites will be sent from a pre-existing mailing list.
- Later, members should be able to invite other members, but only to a given quota.
- Invites should be sent and accepted via email.

## Invite-only model

In order to implement a invite only structure, the application must account for the following:

- AuthKit must not expose sign up controls to the general public.
- Invites will be performed programmatically from a seed script.
- Members can invite other members via an invite UI within the application.

![Closed-registration authentication flow](https://images.workoscdn.com/images/4cb2c8f9-896d-4191-8b04-90d7bd908e6b.png?auto=format&fit=clip&q=80)[border=false]

## Disabling signup

AuthKit provides an out-of-the-box signup form which handles validation UX, makes the necessary WorkOS API calls and handles the end-to-end lifecycle of the invite flow (emailing of members, accepting of invites, assignment of members to organizations where appropriate).

In this scenario, the application should not expose the signup flow to the general public. It can be disabled per environment by toggling the "Sign up" setting in the authentication section of the WorkOS dashboard.

![Image of a modal in the WorkOS dashboard to disable sign-ups](https://images.workoscdn.com/images/76369560-2e4c-41bd-b3ba-1e7e1cf806e3.png?auto=format&fit=clip&q=80)[border=false]

## Inviting users

User invitations can be issued in one of two ways:

- Via the WorkOS dashboard.
- Programmatically via the WorkOS SDK.

The simplest way to get started is via the WorkOS dashboard. Invites can be created by navigating to "Invites" tab in the "Users" section of the dashboard.

![Image of a modal in the WorkOS dashboard to invite new users](https://images.workoscdn.com/images/94103735-c85e-43e2-aee1-3a46c33bf2a2.png?auto=format&fit=clip&q=80)[border=false]

This is helpful in the early stages of product development where there may be a small number of potential users and the product is not yet mature enough to warrant development time spent implementing custom invitation controls, or when dealing with support requests from users who are having difficulty.

## Programmatic invitations

Manually issuing invitations from the dashboard is not scalable nor always feasible when needing to issue a large number of invites, or if organic sign up growth is desired without a support team. In this case using the WorkOS API to perform and manage invitations is preferred.

### Seeding initial users from a script

Typically, an application will implement a "seed" script which will be run once to issue a set of invites to a pre-existing mailing list. This can be done by using the WorkOS API to create an invite for each email address in the list.

## Inviting users within the application

Custom invitation controls can be implemented within the application to allow members to invite other members. This generally requires adding a form to the UI to collect the email address of the user to invite alongside a button to trigger the API call. Additionally, a count of the number of invites a user has made might be stored and checked against a quota to ensure they don't exceed the number of invites they are allowed to send.

A call can then be made to [the WorkOS API](/reference/authkit/invitation/send) by supplying the target users email address as well as the ID of the originating organization. The invited user can then accept this invite via email and move through the steps to gain access to the application.

## Summary

This scenario covers some high-level considerations when thinking about closed-registration application. By using AuthKit, the WorkOS API, and the dashboard it's possible to limit signup and implement an invite flow within your application.

In cases where the signup restriction is temporary, signup in AuthKit can be easily re-enabled via a setting in the WorkOS dashboard.

### Invitations

Easily add users to your application or as members of an organization.

## Introduction

Invitations are a way of adding a specific user to your application or as a member of an organization. They provide a flow for end-users to engage in collaboration that takes into consideration security and user choice.

## Invitation flow

Each invitation is for a specific email address to a specific organization. Invitations are for both new users and existing users.

Each invitation is a two step process:

- The inviter expresses intent for someone to join an organization.
- The invitee chooses to join that organization.

### Inviting new users to an organization

If an invitation is created for an email address that does not yet exist, an email is sent to that user with a link to sign up for your application and join the organization.

As part of signing up, they automatically join the organization. If a user is invited to multiple organizations, they only join the organization for which they clicked the invitation email for, indicating intent to join that specific organization.

### Inviting existing users to an organization

If an invitation is for an existing user, clicking the link in the email and signing in adds the user as a member to the organization. If the user is already signed in, you can use the invitation code to validate that the signed-in user is eligible to use the invitation, by querying the [Invitation API](/reference/authkit/invitation).

This offers choice for the end-user so that they aren’t automatically added to organizations that may be attempting phishing attacks.

## Application-wide invitations

Invitations do not have to be specific to an organization. An invitation sent without specifying an organization is an invitation to join the application. This enables your existing users to help grow your application by inviting peers organically.

When signup is disabled, users cannot register for a new account through [AuthKit](/authkit) or the [API](/reference/authkit/invitation). When a valid invitation code is present in the sign-in flow, registration is opened up both in AuthKit and the API so that a new user may sign up. This lets you model your application as a closed-registration invitation-only system.

## Sending invitations

Invitations can be sent programmatically by your application with the [Invitation API](/reference/authkit/invitation), or viewed and manually created in the [WorkOS Dashboard](https://dashboard.workos.com/). By default, WorkOS sends these emails, but you can also [send the emails yourself](/authkit/custom-emails).

![Dashboard displaying a list of user invitations](https://images.workoscdn.com/images/18299a05-f824-410e-a17b-828fbe5826f1.png?auto=format&fit=clip&q=80)

## Email address used to accept an invite

Often, a user might want to accept their invitation using an email address that’s different from the one that the invitation was sent to.

### Without organization membership

When an invitation doesn’t include an organization to join, a user can accept the invitation using any email address.

For example, an invitation sent to `user@example.com` can be used with `another-user@foo-corp.com` email address.

### With organization membership

For organization-specific invitations, there are different rules based on the email domain on the invitation.

- **Consumer email domains**, such as Gmail or Yahoo: the invited user must sign up using exactly the same email address to which the invitation was sent.
- **Corporate domains**: the user can sign up with any email address from the same domain as the email on the invitation. For example, an invitation sent to `user@foo-corp.com` can be accepted with `another-user@foo-corp.com`

### AuthKit

Easy to use authentication platform designed to provide a flexible, secure, and fast integration.

## Introduction {{ "visibility": "no-quick-nav" }}

Integrating AuthKit into your app is quick and easy. In this guide, we'll walk you through adding a hosted authentication flow to your application using AuthKit.

In addition to this guide, there are a variety of [example apps](/authkit/example-apps) available to help with your integration.

## Before getting started {{ "visibility": "no-quick-nav" }}

To get the most out of this guide, you'll need:

- A [WorkOS account](https://dashboard.workos.com/)
- Your WorkOS [API Key](/glossary/api-key) and [Client ID](/glossary/client-id)

Additionally you'll need to activate AuthKit in your WorkOS Dashboard if you haven't already. In the _Overview_ section, click the _Set up AuthKit_ button and follow the instructions.

![WorkOS dashboard with the AuthKit setup button highlighted](https://images.workoscdn.com/images/48aa13fa-0295-4027-8e08-e7bd14753fd3.png?auto=format&fit=clip&q=50)

---

## (1) Configure your project

Let's add the necessary dependencies and configuration in your WorkOS Dashboard.



### Install dependencies

- $ frontend="client-only"

  For a client-only approach, use the `authkit-react` library to integrate AuthKit directly into your React application. Start by installing the library to your project via `npm`.

  ```bash title="Install React SDK"
  npm install @workos-inc/authkit-react
  ```

- $ frontend="nextjs"

  For a Next.js integration, use the `authkit-nextjs` library. Start by installing it in your Next.js project via `npm`.

  ```bash title="Install Next.js SDK"
  npm install @workos-inc/authkit-nextjs
  ```

- $ frontend="remix"

  To use AuthKit with a Remix application, use the `authkit-remix` library. Start by installing it in your Remix project via `npm`.

  ```bash title="Install Remix SDK"
  npm install @workos-inc/authkit-remix
  ```

- $ backend="nodejs"

  First, install the required Node SDK via `npm`.

  ```bash title="Install Node SDK"
  npm install @workos-inc/node
  ```

- $ backend="ruby"
  First, install the WorkOS gem.

  ```bash title="Install Ruby SDK"
  gem install workos
  ```

- $ backend="python"

  First, install the Python SDK.

  ```bash title="Install Python SDK"
  pip install workos
  ```

### Configure a redirect URI

A redirect URI is a callback endpoint that WorkOS will redirect to after a user has authenticated. This endpoint will exchange the authorization code returned by WorkOS for an authenticated [User object](/reference/authkit/user). We'll create this endpoint in the next step.

You can set a redirect URI in the _Redirects_ section of the [WorkOS Dashboard](https://dashboard.workos.com).

WorkOS supports using wildcard characters in Redirect URIs, but not for the default Redirect URI. More information about wildcard characters support can be found in the [Redirect URIs](/sso/redirect-uris/wildcard-characters) guide.

- $ frontend="client-only"

  ![Dashboard Redirect URIs](https://images.workoscdn.com/images/a7525cf3-ae4e-4dcd-91dd-27965b005472.png?auto=format&fit=clip&q=80)

  > For the client-only integration, make sure to set the callback URI as the same route where you require auth.

- $ frontend="nextjs, remix, vanilla, react"

  ![Dashboard redirect URI](https://images.workoscdn.com/images/a7525cf3-ae4e-4dcd-91dd-27965b005472.png?auto=format&fit=clip&q=80)

When users sign out of their application, they will be redirected to your app's [Sign-out redirect](/authkit/sessions/configuring-sessions/sign-out-redirect) location which is configured in the same dashboard area.

### Configure sign-in endpoint

- $ frontend="client-only"

  All sign-in requests must originate at your application for the [PKCE](/reference/authkit/authentication/get-authorization-url/pkce) code exchange to work properly. In some instances, requests may not begin at your app. For example, some users might bookmark the hosted sign-in page or they might be led directly to the hosted sign-in page when clicking on a password reset link in an email.

- $ frontend="nextjs, remix, vanilla, react"

  Sign-in requests should originate from your application. In some instances, requests may not begin at your app. For example, some users might bookmark the hosted sign-in page or they might be led directly to the hosted sign-in page when clicking on a password reset link in an email.

In these cases, AuthKit will detect when a sign-in request did not originate at your application and redirect to your application's sign-in endpoint. This is an endpoint that you define at your application that redirects users to sign in using AuthKit. We'll create this endpoint in the next step.

You can configure the sign-in endpoint from the _Redirects_ section of the WorkOS dashboard.

![Sign-in endpoint](https://images.workoscdn.com/images/25b53ea7-95ba-48cc-b6e7-ccd1b1bc35eb.png?auto=format&fit=clip&q=80)

- $ frontend="client-only"

  ### Configure CORS

  Since your user's browser will be making calls to the WorkOS API directly, it is necessary to add your domain to the allow list in your WorkOS Settings. This can be configured in the _Configure CORS_ dialog on the _Authentication_ page of the WorkOS dashboard.

  ![Screenshot of the WorkOS dashboard showing the "Configure CORS" option in the "Authentication" section.](https://images.workoscdn.com/images/3b7863df-8c59-4d48-ab91-f537fd5c9f66.png?auto=format&fit=clip&q=50)

  While building your integration in the Staging environment you should add your local development URL here. In the example below we're adding `http://localhost:5173` to the list of allowed web origins.

  ![Screenshot of the WorkOS dashboard showing the CORS configuration panel.](https://images.workoscdn.com/images/e20fdbfb-965f-47b5-9c64-b83f6e6b8a39.png?auto=format&fit=clip&q=50)

- $ frontend="nextjs, remix"

  ### Set secrets

  To make calls to WorkOS, provide the API key and the client ID. Store these values as managed secrets and pass them to the SDKs either as environment variables or directly in your app's configuration depending on your preferences.

- $ frontend="nextjs"

  ```plain title="Environment variables"
  WORKOS_API_KEY='sk_example_123456789'
  WORKOS_CLIENT_ID='client_123456789'
  WORKOS_COOKIE_PASSWORD="" # generate a secure password here

  # configured in the WorkOS dashboard
  NEXT_PUBLIC_WORKOS_REDIRECT_URI="http://localhost:3000/callback"
  ```

  The `NEXT_PUBLIC_WORKOS_REDIRECT_URI` uses the `NEXT_PUBLIC` prefix so the variable is accessible in edge functions and middleware configurations. This is useful for configuring operations like Vercel preview deployments.

- $ frontend="remix"

  ```plain title="Environment variables"
  WORKOS_API_KEY='sk_example_123456789'
  WORKOS_CLIENT_ID='client_123456789'

  WORKOS_REDIRECT_URI="http://localhost:3000/callback" # configured in the WorkOS dashboard
  WORKOS_COOKIE_PASSWORD="" # generate a secure password here
  ```

- $ frontend="nextjs, remix"

  The SDK requires you to set a strong password to encrypt cookies. This password must be at least 32 characters long. You can generate a secure password by using the [1Password generator](https://1password.com/password-generator/) or the `openssl` library via the command line:

  ```bash title="Generate a strong password"
  openssl rand -base64 32
  ```

- $ backend="nodejs, ruby, python"

  ### Set secrets

  To make calls to WorkOS, provide the API key and the client ID. Store these values as managed secrets and pass them to the SDKs either as environment variables or directly in your app's configuration depending on your preferences.

  ```plain title="Environment variables"
  WORKOS_API_KEY='sk_example_123456789'
  WORKOS_CLIENT_ID='client_123456789'
  ```

> The code examples use your staging API keys when [signed in](https://dashboard.workos.com)

---

## (2) Add AuthKit to your app

Let's integrate the hosted authentication flow into your app.

- $ frontend="client-only"

  ### Wrap your app with the AuthKit provider

  The `AuthKitProvider` component will handle the redirect from Hosted AuthKit, refresh the session when needed and provide context for hooks used in the components of your app. Initialize it with your client ID, which you can find in the WorkOS dashboard. You should also specify your custom authentication API domain.

  > If you have not set up a custom authentication domain in WorkOS, set `devMode={true}` on ``. This will keep the refresh token in local storage instead of a secure, HTTP-only cookie.

  

  > For security reasons, the client-only integration cannot be nested inside an `iframe`.

  ### Use the auth hook in your components

  The `useAuth` hook will return user information and loading status. It also provides functions to retrieve the access token and sign in and sign out the user.

  

  ### Protect routes with custom hooks

  If you have routes that you wish to only be accessible to logged in users, you can use a custom React hook.

  

  Then use that hook to protect your mandatory sign in routes.

  

- $ frontend="nextjs"

  ### Provider

  The `AuthKitProvider` component adds protections for auth edge cases and is required to wrap your app layout.

  

  ### Middleware

  [Next.js middleware](https://nextjs.org/docs/app/building-your-application/routing/middleware) is required to determine which routes require authentication.

  #### Implementing the middleware

  When implementing, you can opt to use either the complete `authkitMiddleware` solution or the composable `authkit` method. You'd use the former in cases where your middleware is only used for authentication. The latter is used for more complex apps where you want to have your middleware perform tasks in addition to auth.

  - | Complete

    The middleware can be implemented in the `middleware.ts` file. This is a full middleware solution that handles all the auth logic including session management and redirects for you.

    With the complete middleware solution, you can choose between page based auth and middleware auth.

    #### Page based auth

    Protected routes are determined via the use of the `withAuth` method, specifically whether the `ensureSignedIn` option is used. Usage of `withAuth` is covered further down in the _Access authentication data_ section.

    

    #### Middleware auth

    In this mode the middleware is used to protect all routes by default, redirecting users to AuthKit if no session is available. Exceptions can be configured via an allow list.

    

    In the above example, the home page `/` can be viewed by unauthenticated users. The `/account` page and its children can only be viewed by authenticated users.

  - | Composable

    The middleware can be implemented in the `middleware.ts` file. This is a composable middleware solution that handles the session management part for you but leaves the redirect and route protection logic to you.

    

  ### Callback route

  When a user has authenticated via AuthKit, they will be redirected to your app's callback route. Make sure this route matches the `WORKOS_REDIRECT_URI` environment variable and the configured redirect URI in your WorkOS dashboard.

  

  ### Sign-in endpoint

  We'll need a sign-in endpoint to direct users to sign in using AuthKit before redirecting them back to your application. We'll do this by generating an AuthKit authorization URL server side and redirecting the user to it.

  

  ### Access authentication data

  AuthKit can be used in both server and client components.

  - | Server component

    The `withAuth` method is used to retrieve the current logged in user and their details.

    

  - | Client component

    The `useAuth` hook is used to retrieve the current logged in user and their details.

    

  ### Protected routes

  For routes where a signed in user is mandatory, you can use the `ensureSignedIn` option.

  - | Server component

    

  - | Client component

    

  ### Ending the session

  Finally, ensure the user can end their session by redirecting them to the logout URL. After successfully signing out, the user will be redirected to your app's [Sign-out redirect](/authkit/sessions/configuring-sessions/sign-out-redirect) location, which is configured in the WorkOS dashboard.

  

- $ frontend="remix"

  ### Callback route

  When a user has authenticated via AuthKit, they will be redirected to your app's callback route. In your Remix app, [create a new route](https://remix.run/docs/en/main/discussion/routes) and add the following:

  

  ### Sign-in endpoint

  We'll need a sign-in endpoint to direct users to sign in using AuthKit before redirecting them back to your application. We'll do this by generating an AuthKit authorization URL server side and redirecting the user to it.

  

  ### Access authentication data in your Remix application

  We'll need to direct users to sign in (or sign up) using AuthKit before redirecting them back to your application. We'll do this by generating an AuthKit authorization URL server side and redirecting the user to it.

  Use `authkitLoader` to configure AuthKit for your Remix application routes. You can choose to return custom data from your loader, like for instance the sign in and sign out URLs.

  

  ### Protected routes

  For routes where a signed in user is mandatory, you can use the `ensureSignedIn` option in your loader.

  

  ### Ending the session

  Finally, ensure the user can end their session by redirecting them to the logout URL. After successfully signing out, the user will be redirected to your app's [Sign-out redirect](/authkit/sessions/configuring-sessions/sign-out-redirect) location, which is configured in the WorkOS dashboard.

  

- $ frontend="vanilla, react"

  ### Set up the frontend

  To demonstrate AuthKit, we only need a simple page with links to logging in and out.

- $ frontend="vanilla"

  

- $ frontend="react"

  

- $ frontend="vanilla, react"

  Clicking the "Sign in" and "Sign out" links should invoke actions on our server, which we'll set up next.

- $ backend="nodejs, ruby, php, go, python, java"

  ### Add a sign-in endpoint

  We'll need a sign-in endpoint to direct users to sign in (or sign up) using AuthKit before redirecting them back to your application. This endpoint should generate an AuthKit authorization URL server side and redirect the user to it.

  You can use the optional state parameter to encode arbitrary information to help restore application `state` between redirects.

- $ backend="nodejs"

  For this guide we'll be using the `express` web server for Node. This guide won't cover how to set up an Express app, but you can find more information in the [Express documentation](https://expressjs.com/en/starter/installing.html).

  

- $ backend="ruby"

  For this guide we'll be using the `sinatra` web server for Ruby. This guide won't cover how to set up a Sinatra app, but you can find more information in the [Sinatra documentation](https://sinatrarb.com/intro.html).

  

- $ backend="python"

  For this guide we'll be using the `flask` web server for Python. This guide won't cover how to set up a Flask app, but you can find more information in the [Flask documentation](https://flask.palletsprojects.com/en/stable/).

  

- $ backend="nodejs, ruby, python"

  > WorkOS will redirect to your [Redirect URI](/glossary/redirect-uri) if there is an issue generating an authorization URL. Read our [API Reference](/reference) for more details.

  ### Add a callback endpoint

  Next, let's add the callback endpoint (referenced in [Configure a redirect URI](/authkit/1-configure-your-project/configure-a-redirect-uri)) which will exchange the authorization code (valid for 10 minutes) for an authenticated User object.

- $ backend="nodejs"

  

- $ backend="ruby"

  

- $ backend="python"

  

- $ backend="nodejs, ruby, python"

  ## (3) Handle the user session

  Session management helper methods are included in our SDKs to make integration easy. For security reasons, sessions are automatically "sealed", meaning they are encrypted with a strong password.

  ### Create a session password

  The SDK requires you to set a strong password to encrypt cookies. This password must be 32 characters long. You can generate a secure password by using the [1Password generator](https://1password.com/password-generator/) or the `openssl` library via the command line:

  ```bash title="Generate a strong password"
  openssl rand -base64 32
  ```

  Then add it to the environment variables file.

  ```plain title=".env"
  WORKOS_API_KEY='sk_example_123456789'
  WORKOS_CLIENT_ID='client_123456789'

  # +diff-start
  WORKOS_COOKIE_PASSWORD=''
  # +diff-end
  ```

  ### Save the encrypted session

  Next, use the SDK to authenticate the user and return a password protected session. The refresh token is considered sensitive as it can be used to re-authenticate, hence why the session is encrypted before storing it in a session cookie.

- $ backend="nodejs"

  

  ### Protected routes

  Then, use middleware to specify which routes should be protected. If the session has expired, use the SDK to attempt to generate a new one.

  

  Add the middleware to the route that should only be accessible to logged in users.

  

  ### Ending the session

  Finally, ensure the user can end their session by redirecting them to the logout URL. After successfully signing out, the user will be redirected to your app's [Sign-out redirect](/authkit/sessions/configuring-sessions/sign-out-redirect) location, which is configured in the WorkOS dashboard.

  

- $ backend="ruby"

  

  ### Protected routes

  Then, use a helper method to specify which routes should be protected. If the session has expired, use the SDK to attempt to generate a new one.

  

  Call the helper method in the route that should only be accessible to logged in users.

  

  ### Ending the session

  Finally, ensure the user can end their session by redirecting them to the logout URL. After successfully signing out, the user will be redirected to your app's [Sign-out redirect](/authkit/sessions/configuring-sessions/sign-out-redirect) location, which is configured in the WorkOS dashboard.

  

- $ backend="python"

  

  ### Protected routes

  Then, use a decorator to specify which routes should be protected. If the session has expired, use the SDK to attempt to generate a new one.

  

  Use the decorator in the route that should only be accessible to logged in users.

  

  ### Ending the session

  Finally, ensure the user can end their session by redirecting them to the logout URL. After successfully signing out, the user will be redirected to your app's Sign-out redirect location, which is configured in the WorkOS dashboard.

  

> If you haven't configured a [Sign-out redirect](/authkit/sessions/configuring-sessions/sign-out-redirect) in the WorkOS dashboard, users will see an error when logging out.

### Validate the authentication flow

Navigate to the authentication endpoint we created and sign up for an account. You can then sign in with the newly created credentials and see the user listed in the _Users_ section of the [WorkOS Dashboard](https://dashboard.workos.com).

![Dashboard showing newly created user](https://images.workoscdn.com/images/54fa6e6c-4c6f-4959-9301-344aeb4eeac8.png?auto=format&fit=clip&q=80)

### Impersonation

Learn how to sign into your application as one of your users.

## Introduction

Impersonation allows administrators and support team members to assume the identity of any of your users, allowing them to reproduce or debug issues the user may be having in your application. The ability to see the application in an identical state as the user helps to greatly speed up the support process.

## Enabling impersonation

Since impersonation allows any member of your WorkOS team to bypass the normal authentication flow for a user, it is not enabled by default in any of your environments. You must have the **Admin** role in order to enable impersonation for an environment.

![A screenshot showing the WorkOS Dashboard configuration card for impersonation](https://images.workoscdn.com/images/bf2d2b47-1dbb-4dd2-a0e9-4d27192b6353.png?auto=format&fit=clip&q=80)

Navigate to _Authentication_ → _User Impersonation_ and select _Configure_ to enable impersonation for your current environment.

## Using impersonation

To impersonate one of your users, navigate to _Users_, select the user you'd like to impersonate, and under _Danger Zone_ select _Impersonate User_.

![A screenshot of the User details page in the WorkOS Dashboard](https://images.workoscdn.com/images/afcfc2b9-275c-4a08-9976-ab9b1b30a7b8.png?auto=format&fit=clip&q=80)

You will be prompted for the reason your are impersonating the user. The reason is required and will be recorded internally on the `session.created` event that is emitted whenever impersonation is used.

If the user is a member of more than one organization, you will also need to choose which of
these organizations you will be signing-into as the user. You can read more about users and organizations in our [dedicated guide](/authkit/users-organizations).

Finally, click _Impersonate user_ to start an impersonation session, redirecting your browser to your application's callback endpoint with an authorization code for the impersonated user. You can read more about how to implement a callback endpoint in our [Quick Start guide](/authkit/2-add-authkit-to-your-app/add-a-callback-endpoint).

> Impersonation sessions automatically expire after 60 minutes.

Be aware that impersonating a user usually generally gives the same level of access as that user, allowing the impersonator to see the user's information. If your application contains sensitive user data, see the [Integrating impersonation](/authkit/impersonation/integrating-impersonation) section about how to customize your application when using impersonation.

### Deep-linking to the impersonation flow

You can deep-link to the impersonation flow in the WorkOS Dashboard from your own admin tool using the following URL structure:

`https://dashboard.workos.com//users/?dialog=impersonate`

## Auditing impersonation usage

User sessions that were initiated via impersonation will be clearly marked as such when viewing their details in the WorkOS Dashboard. Additionally, WorkOS emits a [`session.created`](/events/session) event which you can view under the events for the user, or listen for in your application via the [events API](/events).

![A screenshot of the User events page in the WorkOS Dashboard](https://images.workoscdn.com/images/e213fa51-4aa6-4260-8c31-84823fbcc77b.png?auto=format&fit=clip&q=80)

The `session.created` event has an `impersonator` field that contains information about the impersonation session, like the `email` of your team member who performed the impersonation, along with their `reason` for doing so.

## Integrating impersonation

No additional code is required to start using impersonation once you have integrated with WorkOS. However, many developers may want to augment their application's behavior when your team members are impersonating one of your users.

The response from the [Authenticate with Code API](/reference/authkit/authentication/code) will include an additional `impersonator` field when the resulting session was created via impersonation, containing the impersonator's `email` and `reason` for using impersonation. Similarly, the `access_token` will include an `act` claim with the impersonator's `email`. Your application can use either in order to trigger impersonation-specific behavior.

A common enhancement is to change the appearance of the application in order to make it obvious to the viewer they are currently impersonating one of your users, such as a "Staff Bar" displayed at the top of the viewport. You may also want to restrict access to sensitive views or redact certain fields in your application.

### Impersonation with `authkit-nextjs`

If using the [`authkit-nextjs` library](https://github.com/workos/authkit-nextjs), impersonation can be easily added by using the provided helper component.

After completing the setup instructions in the [quick start](/authkit) guide, add the Impersonation component to your app code.

```js title="Impersonation component"
;

export default function RootLayout({ children }) {
  return (
    
      
        
        {children}
      
    
  );
}
```

The above will automatically render a visually distinct frame on your page with an option to hide it or stop the impersonation session.

![A screenshot showing the Impersonation frame rendered over a page](https://images.workoscdn.com/images/c4305ab6-d4fa-4f36-be9c-5a60ee12a7b3.png?auto=format&fit=clip&q=80)

### Identity Linking

Automatic deduplication of user credentials across identity providers.

## Introduction

Users have unique email addresses, because each user’s access to their inbox represents ultimate access to all of their credentials and thus services they control. The [User object](/reference/authkit/user) unifies all of the identities they use so that your application does not have to consider different identity systems.

Identity linking is the process in which WorkOS safely deduplicates various credentials across identity providers to offer a single, unified user interface. It does this by using the **email address** as the unique identifier and access to the email inbox as the source of truth.



## Credentials

A credential is an authentication method in a specific identity provider. For example, WorkOS offers a [password credential](/authkit/email-password) for users to authenticate with. In this case, WorkOS is the identity provider and password is the authentication method.

[Google OAuth](/authkit/social-login) is another credential, where Google is the identity provider and OAuth is the authentication method.

Users may use multiple types of authentication methods based on preference, perhaps because one is more convenient to use on one of their devices, or they simply didn’t remember which method they used in the past.

## Email verification

WorkOS ensures all user emails are unique via an [email verification process](/authkit/email-verification). By default, email verification is required by all users for authentication to succeed. This ensures that verified users are always returned to your application.

When a user signs in with a new credential for the first time, e.g. they sign in through Google OAuth despite already having a password account, WorkOS will safely attach the new credential to the existing user. This is only performed if WorkOS can verify that the user has access to the email inbox referenced by that credential.

WorkOS considers it a **security risk if the user cannot verify access to their email**. Some identity providers allow creating accounts with any email address. For instance, an IT admin of an organization with the domain `apple.com` could make an account for `billg@microsoft.com`. If access to `billg@microsoft.com` is not verified, the admin could sign in to the application as that user.

> WorkOS does not complete the authentication flow when a new identity cannot be safely linked to an existing user to ensure account takeover risks are minimized.

## Domain verification

When an IT admin [verifies a domain for their organization](/authkit/domain-verification), it means they have access to create email inboxes. Thus, a **verified domain implies the ability to verify all users with that email domain**.

In practice, when a domain is verified and an SSO connection is configured, users who sign in through an organization’s IdP are automatically considered email verified if the domain matches. This shortcut reduces friction for your end users.

> Users who sign-in through SSO with an email address that is not a verified domain are not considered verified and will have go through the [email verification](/authkit/email-verification) process.

## SSO identity linking

Not only can a user have multiple credentials, they may also have multiple SSO credentials. This might happen when a user works with multiple organizations that require SSO authentication for all members. In this case, there is still only one [User object](/reference/authkit/user), but they would choose which organization’s SSO IdP to use when authenticating.

![Example UI showing organization selection](https://images.workoscdn.com/images/876aeb05-cd96-46b4-9adf-3e9dd0208e47.png?auto=format&fit=clip&q=80)

The email verification safety still applies. When the user signs-in for the first time through an SSO IdP where the user’s email address is not a verified domain, the user is asked to verify their email before the SSO credential is linked to their account.

Users without a verified domain **must be invited to the organization** before they have access via SSO for the first time.

> An [invitation](/authkit/invitations) ensures that the authentication flow gives the user an opportunity to go to the SSO’s identity provider.

### Hosted UI

Customizable sign-in UI that abstracts away all of the complexity associated with building secure authentication flows.

## Introduction

Implementing authentication flows that handle every possible error state and edge case across multiple identity providers can be a daunting task. AuthKit makes this easy by providing a hosted, pre-built, customizable authentication UI with automatic handling of:

- Sign up, sign in, password reset, and [email verification](/authkit/email-verification) flows.
- Enterprise [SSO](/authkit/sso) routing and [MFA](/authkit/mfa) enrollment.
- Automatic bot detection and blocking, to protect against brute force attacks.
- Customizable [domain](/custom-domains/authkit) and [branding](/authkit/branding).

![AuthKit sign-in UI](https://images.workoscdn.com/images/4d736ca3-eec8-4a90-bd14-2530c4210415.png?auto=format&fit=clip&q=80)

## Authentication flow

AuthKit is conceptually similar to a [Social Login (OAuth)](/authkit/social-login) experience, but with the added benefit of being able to authenticate users with any identity provider.

AuthKit sits outside of your application code. When a user initiates a sign-in request, your application redirects them to the AuthKit URL. The user then completes the authentication process with WorkOS before being returned to the application.

Your application will exchange the resulting authorization code to retrieve an authenticated [User object](/reference/authkit/user) and handle the session.

![AuthKit authentication flow diagram](https://images.workoscdn.com/images/0b3265fa-a209-4ca7-beaf-7d2514a3e00a.png?auto=format&fit=clip&q=80)[border=false]

> The AuthKit flow abstracts away many of the UX and WorkOS API calling concerns automatically, for more guidance on integrating with AuthKit, see the [Quick Start](/authkit) guide.

AuthKit also provides a signup flow for creating users. Available options are determined by the configured [authentication methods](/authkit/hosted-ui/authentication-methods). If a user’s email address is associated with an SSO connection, they will automatically be redirected to sign up via their IdP.

## Authentication methods

AuthKit's hosted UI supports all of the authentication methods available and will automatically adjust the available options depending on the configured methods in the _Authentication_ section of the [WorkOS Dashboard](https://dashboard.workos.com).

![Dashboard displaying available authentication methods](https://images.workoscdn.com/images/ea3b2c3b-723e-462c-aa10-6b1cec1b635f.png?auto=format&fit=clip&q=80)

Email + Password authentication is enabled by default, though set up may be required to enable additional methods. See the relevant feature section for more information:

- [Single Sign-On](/authkit/sso)
- [Email + Password](/authkit/email-password)
- [Social Login](/authkit/social-login)
- [Multi-Factor Auth](/authkit/mfa)
- [Magic Auth](/authkit/magic-auth)

## Localization

By default, AuthKit's hosted UI is automatically localized into many global languages.
Your users will be served in the locale that closest matches their device's OS preference.
All user-facing text, including error messages and transactional emails, are translated into the user's native tongue.

- ### Supported locales
  | Locale code | Language                | Autonym                 |
  | ----------- | ----------------------- | ----------------------- |
  | `af`        | Afrikaans               | Afrikaans               |
  | `am`        | Amharic                 | አማርኛ                    |
  | `ar`        | Arabic                  | العربية                 |
  | `bg`        | Bulgarian               | Български               |
  | `bn`        | Bengali (Bangla)        | বাংলা                   |
  | `bs`        | Bosnian                 | Bosanski                |
  | `ca`        | Catalan                 | Català                  |
  | `cs`        | Czech                   | Čeština                 |
  | `da`        | Danish                  | Dansk                   |
  | `de`        | German                  | Deutsch                 |
  | `de-DE`     | German (Germany)        | Deutsch (Deutschland)   |
  | `el`        | Greek                   | Ελληνικά                |
  | `en`        | English                 | English                 |
  | `en-AU`     | English (Australia)     | English (Australia)     |
  | `en-CA`     | English (Canada)        | English (Canada)        |
  | `en-GB`     | English (UK)            | English (UK)            |
  | `en-US`     | English (US)            | English (US)            |
  | `es`        | Spanish                 | Español                 |
  | `es-419`    | Spanish (Latin America) | Español (Latinoamérica) |
  | `es-ES`     | Spanish (Spain)         | Español (España)        |
  | `es-US`     | Spanish (US)            | Español (EE.UU.)        |
  | `et`        | Estonian                | Eesti                   |
  | `fa`        | Farsi (Persian)         | فارسی                   |
  | `fi`        | Finnish                 | Suomi                   |
  | `fil`       | Filipino (Tagalog)      | Filipino                |
  | `fr`        | French                  | Français                |
  | `fr-BE`     | French (Belgium)        | Français (Belgique)     |
  | `fr-CA`     | French (Canada)         | Français (Canada)       |
  | `fr-FR`     | French (France)         | Français (France)       |
  | `fy`        | Frisian                 | Frysk                   |
  | `gl`        | Galician                | Galego                  |
  | `gu`        | Gujarati                | ગુજરાતી                 |
  | `ha`        | Hausa                   | هَرْشٜن هَوْس           |
  | `he`        | Hebrew                  | עברית                   |
  | `hi`        | Hindi                   | हिन्दी                  |
  | `hr`        | Croatian                | Hrvatski                |
  | `hu`        | Hungarian               | Magyar                  |
  | `hy`        | Armenian                | Հայերեն                 |
  | `id`        | Indonesian              | Bahasa Indonesia        |
  | `is`        | Icelandic               | Íslenska                |
  | `it`        | Italian                 | Italiano                |
  | `it-IT`     | Italian (Italy)         | Italiano (Italia)       |
  | `ja`        | Japanese                | 日本語                  |
  | `jv`        | Javanese                | ꦧꦱꦗꦮ                    |
  | `ka`        | Georgian                | ქართული                 |
  | `kk`        | Kazakh                  | Қазақ тілі              |
  | `km`        | Khmer                   | ខេមរភាសា                |
  | `kn`        | Kannada                 | ಕನ್ನಡ                   |
  | `ko`        | Korean                  | 한국어                  |
  | `lt`        | Lithuanian              | Lietuvių                |
  | `lv`        | Latvian                 | Latviešu                |
  | `mk`        | Macedonian              | Македонски              |
  | `ml`        | Malayalam               | മലയാളം                  |
  | `mn`        | Mongolian               | Монгол                  |
  | `mr`        | Marathi                 | मराठी                   |
  | `ms`        | Malay                   | Bahasa Melayu           |
  | `my`        | Burmese                 | မြန်မာ                  |
  | `nb`        | Norwegian Bokmål        | Norsk Bokmål            |
  | `ne`        | Nepali                  | नेपाली भाषा             |
  | `nl`        | Dutch                   | Nederlands              |
  | `nl-BE`     | Flemish                 | Vlaams                  |
  | `nl-NL`     | Dutch (Netherlands)     | Nederlands (Nederland)  |
  | `nn`        | Norwegian Nynorsk       | Norsk Nynorsk           |
  | `no`        | Norwegian               | Norsk                   |
  | `pa`        | Punjabi                 | ਪੰਜਾਬੀ                  |
  | `pl`        | Polish                  | Polski                  |
  | `pt`        | Portuguese              | Português               |
  | `pt-BR`     | Portuguese (Brazil)     | Português (Brasil)      |
  | `pt-PT`     | Portuguese (Portugal)   | Português (Portugal)    |
  | `ro`        | Romanian                | Română                  |
  | `ru`        | Russian                 | Русский                 |
  | `sk`        | Slovak                  | Slovenčina              |
  | `sl`        | Slovenian               | Slovenščina             |
  | `sq`        | Albanian                | Shqip                   |
  | `sr`        | Serbian                 | Српски                  |
  | `sv`        | Swedish                 | Svenska                 |
  | `sw`        | Swahili                 | Kiswahili               |
  | `ta`        | Tamil                   | தமிழ்                   |
  | `te`        | Telugu                  | తెలుగు                  |
  | `th`        | Thai                    | ไทย                     |
  | `tr`        | Turkish                 | Türkçe                  |
  | `uk`        | Ukrainian               | Українська              |
  | `ur`        | Urdu                    | اُردُو                  |
  | `uz`        | Uzbek                   | Ózbekça                 |
  | `vi`        | Vietnamese              | Tiếng Việt              |
  | `zh`        | Chinese                 | 中文                    |
  | `zh-CN`     | Chinese (Simplified)    | 中文 (中国)             |
  | `zh-HK`     | Chinese (Hong Kong)     | 中文（香港）            |
  | `zh-TW`     | Chinese (Taiwan)        | 中文（台灣）            |
  | `zu`        | Zulu                    | isiZulu                 |

In cases where a user's browser does not send their preferred locale, or when AuthKit cannot identify a match, the user is served in the environment's **fallback language**. The fallback language can be configured [in the dashboard](https://dashboard.workos.com/environment/authentication/features) in the _Authentication > Features > Localization_ section.

![With localization, you can change the environment's fallback language](https://images.workoscdn.com/images/bb8c017b-f9cd-4882-baa2-b38e01a51875.png?auto=format&fit=clip&q=50)

## Integrating

Integration into your app is quick and easy, though the route you choose varies depending on your specific requirements:

### (A) Integrate with AuthKit's Hosted UI

In just a few lines of code, you can add AuthKit to your app and start authenticating users. See the [quick start](/authkit) guide for more information.

### (B) Build your own authentication flows

While the hosted solution is the fastest way to get started, if you’d prefer to build and manage your own authentication UI, you can do so via the [AuthKit API](/reference/authkit).

Examples of building custom UI are [available on GitHub](https://github.com/workos/authkit).

### Example apps

View sample AuthKit apps.

You can view minimal example apps that demonstrate how to use AuthKit to authenticate users:


  
  
  
  
  
  
  
  
</ExampleApps.Root>

### Email Verification

Learn more about the email verification process.

## Introduction

Email verification is a process in which a new user must validate ownership of their email inbox before they can access the application, ensuring authenticity of inbox ownership.

## The email verification flow

Verification is a two-step process:

- A user signs up to your application and an email is sent with a verification code.
- The user inputs the verification code to complete the signup process.

This applies to all authentication methods including [OAuth](/authkit/social-login) and [SSO](/authkit/sso). This unifying interface simplifies how your application considers the authenticity of your users.

**Email verification is always on** to ensure that verified users are always returned to your application.

## Users with verified email domains

Users signing in with SSO with a [verified domain](/authkit/domain-verification) are automatically considered verified and do not need to complete the email verification process.

## Sending verification emails

[AuthKit](/authkit) automatically handles email verification out of the box. When a user signs up via the hosted signup form, AuthKit will automatically send the verification email, prompt the user to input the code and route them through the authentication process before they gain access to the application. If desired, you can [send these emails yourself](/authkit/custom-emails).

### Email + Password

Configuring email and password authentication and requirements.

## Introduction

Email + Password authentication allows users to sign up and sign in to your application using an email address and password combination. This is one of the most common forms of authentication and is enabled by default.

## Password configuration

In the majority of cases, no additional configuration is required. However, depending on your application's security requirements you may wish to modify the password strength policy.

### Modifying the password strength policy

A strong set of password rules are applied to all users by default. This ensures that:

- All passwords meet a minimum required length
- Low complexity passwords are rejected
- Breached passwords (flagged by [haveibeenpwned](https://haveibeenpwned.com)) are rejected

These defaults are recommended in the majority of cases, however, if you wish to modify the password policy you can do so in the _Authentication_ section of the [WorkOS dashboard](https://dashboard.workos.com).

You can enable password history to prevent password reuse. When modifying your policy, you can reject up to 10 of each user’s most recently used previous passwords. Password history is disabled by default.

AuthKit will enforce your policy within the sign up and password reset flows.

![Dashboard password strength policy](https://images.workoscdn.com/images/d9ab3375-78b8-4dc4-a15c-76af2fad671e.png?auto=format&fit=clip&q=80)

### Disabling Email + Password

Disabling this method entirely will prevent users from signing up or signing in using a password. This is useful when you want to restrict access to your application to only those users who have been provisioned via SSO.

---

## Integrating via the API

If you’d prefer to build and manage your own authentication UI, you can do so via the AuthKit [Authentication API](/reference/authkit/authentication).

Examples of building custom UI are also [available on GitHub](https://github.com/workos/authkit).

### Domain Verification

Verify organization domains for secure authentication and provisioning.

## Introduction

Domain verification allows IT admins to prove they control specific domains. This allows WorkOS to trust actions from users with the verified domain and enables authentication and membership policy enforcement for those users.

Verifying an organization domain enables the following features:

1. Users with the verified domain who sign in with the organization’s SSO connection don't need to [verify their email](/authkit/email-verification).
2. By default, users with the verified domain are managed by the organization's [domain policy](/authkit/organization-policies/domain-policy), allowing for enhanced control over authentication and membership.

## Self-serve domain verification

Domain verification can be delegated to the [Admin Portal domain verification flow](/domain-verification). This out-of-the-box UI guides the IT admin to add a DNS TXT record to prove domain ownership. Once the DNS TXT record is correctly added, the organization domain is automatically verified.

## Manual domain verification

Verified domains may also be added manually via the [WorkOS Dashboard](https://dashboard.workos.com) or [API](/reference/organization/update). This shortcut is useful if the IT admin has already proven domain ownership in another context.

> Manually verified domains can be used to define a domain policy that applies to any users with email addresses on that domain. The organization that defines this [domain policy](/authkit/organization-policies/domain-policy) exerts authentication policy control over that domain across your application. For this reason, it is important to verify ownership of manually added domains. Additionally, WorkOS does not allow addition of common consumer domains, like `gmail.com`.

![Adding a verified domain in the dashboard](https://images.workoscdn.com/images/c015b42d-fc39-453c-a4c9-1be220c88a37.png?auto=format&fit=clip&q=80)

### Directory Provisioning

Manage users and organization memberships via directory sync providers.

> Please reach out to [support@workos.com](mailto:support@workos.com) or via your team’s WorkOS Slack channel if you would like Directory Provisioning enabled.

## Introduction

Directory provisioning gives IT admins full control over user and membership management, eliminating the need for manually adding or removing members. Users from a directory are pre-provisioned and managed by their [Identity Provider](/glossary/idp).

## Initial configuration

A [Directory Sync](/directory-sync) integration will need to be configured for every organization that wants to source users and memberships via directory provisioning. Directories can be set up via the [WorkOS Dashboard](https://dashboard.workos.com/) with [Setup Links](/admin-portal/a-setup-link-from-workos-dashboard). You can also [integrate the Admin Portal with your app](/admin-portal/b-integrate-with-your-app) to generate links to configure directories.

### Supported directory providers

Directory provisioning is supported for all SCIM directory providers, Google Workspace, and SFTP.

## Provision users from a directory

Users provisioned through a directory with an email domain matching a verified organization domain will be automatically added as members of the organization, without needing an invitation.

Other users are created with `pending` memberships and receive an email [invitation](/authkit/invitations) to join the organization. Pending members cannot sign in until the invitation is accepted, at which point they become active organization members.

> [Invitation emails](/authkit/custom-emails/disabling-default-emails) can be disabled if you prefer to manage invitations with a custom approach.

## Manage users from a directory

In addition to provisioning new users, any updates to existing users and de-provisioning events will be reflected in AuthKit.

Users with email addresses matching one of the organization’s verified domains are fully managed by the directory. Updates to their attributes from the directory will override changes made through SSO, the API, or manually in the dashboard.

> If multiple organizations with directory provisioning contain the same verified domain, the user's name will always reflect the most recent directory update.

Other users, with email domains not verified by the organization, will not be fully managed by the directory, so updates made via SSO, API, or manually in the dashboard will persist.

When a user is de-provisioned in the directory, the [status](/reference/authkit/organization-membership) of their corresponding organization membership will be set to `inactive`. While the user will no longer be able to sign in to the organization, the membership and user are not automatically deleted.

If a user is re-provisioned in the directory, their organization membership will be reactivated with its previous role and its [status](/reference/authkit/organization-membership) will be set to `active`.

## Directory provisioning actions

Below is a list of directory provisioning and deprovisioning actions and the corresponding changes triggered in AuthKit. If you're using standalone Directory Sync, refer to the [standalone Directory Sync documentation](/directory-sync/api-overview/directory).

| Directory Action             | Changes triggered in WorkOS                                                                                                                                                                                                                                             | Event(s) Emitted                                                                                 |
| ---------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------ |
| Directory user provisioned   | [User](/reference/authkit/user) and [organization membership](/reference/authkit/organization-membership) objects created.                                                                                                                                              | [user.created](/events/user), [organization_membership.created](/events/organization-membership) |
| Directory user info updated  | If the user's email domain matches one of the organization's verified domains, any updates to the user's name will be reflected on the [user](/reference/authkit/user) object. Otherwise, the user object will not be modified. User email address is always immutable. | [user.updated](/events/user)                                                                     |
| Directory user deprovisioned | Organization membership deactivated and all sessions for the user are revoked. Their role is reset to the default role.                                                                                                                                                 | [organization_membership.updated](/events/organization-membership)                               |
| Directory user reactivated   | Organization membership reactivated.                                                                                                                                                                                                                                    | [organization_membership.updated](/events/organization-membership)                               |

---

## Frequently asked questions

### I am using directory provisioning, but some directory users aren't being provisioned in AuthKit. Why would a directory user not be provisioned in AuthKit?

Directory users need to have a primary email address to be provisioned in AuthKit. If the directory user is missing a primary email, they won't be provisioned. Additionally, if the primary email of a directory user is shared by another directory user, only one will be provisioned in AuthKit, as emails are unique to AuthKit users.

### If a user's email is changed in the directory, will this change be reflected on the user's email in WorkOS?

The email address on the [User object](/reference/authkit/user) is immutable, but the email on the underlying [directory user](/reference/directory-sync/directory-user) object will be modified.

### Custom Emails

Learn how to send your own emails for user lifecycle events.

## Introduction

By default, WorkOS will send emails related to AuthKit for you, such as password reset and Magic Auth. If you'd like to customize email content or have more control over deliverability, you can turn off the default emails and deliver your own emails using the WorkOS API.

---

## Disabling default emails

To change email settings for an environment, navigate to _Authentication_ → _Emails_ and select _Configure emails_. You should have an **Admin** role for to update this setting.

![A screenshot showing the WorkOS Dashboard configuration card for emails](https://images.workoscdn.com/images/01c99f2b-8813-4c72-9480-9898e1dabd4a.png?auto=format&fit=clip&q=80)

![A screenshot showing the WorkOS Dashboard dialog for email settings](https://images.workoscdn.com/images/7b4f4713-3381-49aa-b77c-679c9698b429.png?auto=format&fit=clip&q=80)

---

## Invitations

Once you've turned off the default user invitation emails, use the information below to send custom invitation emails.

**[invitation.created](/events/invitation)**
: Event emitted when an invitation is created, which can be consumed using the events API or webhooks.

**[Get Invitation API](/reference/authkit/invitation/get)**
: Used to retrieve the invitation object from the ID in the invitation created event.

**[Send Invitation API](/reference/authkit/invitation/send)**
: Used to create an invitation via the API without handling the invitation created event.

### Set up your user invitation URL {{ "visibility": "no-quick-nav" }}

Make sure you have the correct user invitation URL set on your _Redirects_ page. The default setting is the AuthKit URL for accepting invitations. If you are using your own authentication UI, make sure the URL path is configured on your end to capture the `invitation_token` query parameter, and [pass it into one of the authenticate methods](/reference/authkit/authentication/code).

![A screenshot showing the WorkOS Dashboard configuration card for user invitation URL](https://images.workoscdn.com/images/540542e4-8fd8-4a0f-8715-b5ae1715d46e.png?auto=format&fit=clip&q=50)

### (A) Handle manually creating invitations {{ "visibility": "no-quick-nav" }}

If you’re creating invitations using the WorkOS dashboard, you’ll need to handle `invitation.created` events using the events API or webhooks.

Due to security concerns, the events do not contain the sensitive information you’ll need to send the email. To retrieve the full invitation object with this information, use the invitation ID from the event to call the Get Invitation API.

You can skip this step if you don't plan to create the invitations manually in the dashboard.

### (B) Handle invitations created via the API {{ "visibility": "no-quick-nav" }}

If you’re creating invites via the Send Invitation API, you can send your own email using the information returned in the invitation object.

If you also plan to create invitations manually in the dashboard, you can just handle `invitation.created` events as described above.

### Send your email {{ "visibility": "no-quick-nav" }}

The recipient of the email should match the `email` attribute in the invitation object retrieved via the API. The body of the email should include a link where the user can accept the invitation. For most use cases, you can use the `accept_invitation_url` as this link.

If you are building your own authentication app, and your invitation acceptance path diverges from this pattern, you may want to construct your own URL with the `token`, rather than using the `accept_invitation_url`.

Additionally, if the invitation object contains an organization ID and/or an inviter user ID, you may want to include that information in the body of the email.

---

## Magic Auth

Once you've turned off the default Magic Auth emails, use the information below to send custom Magic Auth emails.

**[magic_auth.created](/events/magic-auth)**
: Event emitted when a user initiates a Magic Auth authentication, which can be consumed using the events API or webhooks.

**[Get Magic Auth API](/reference/authkit/magic-auth/get)**
: Used to retrieve the Magic Auth object from the ID in the Magic Auth created event.

**[Create Magic Auth API](/reference/authkit/magic-auth/create)**
: Used to create a Magic Auth code via the API without handling the Magic Auth created event.

### (A) Handle Magic Auth codes created via AuthKit {{ "visibility": "no-quick-nav" }}

If you are using AuthKit, you’ll need to handle `magic_auth.created` events, using the events API or webhooks.

Due to security concerns, the events do not contain the sensitive information you’ll need to send the email. To retrieve the full Magic Auth object with this information, use the Magic Auth ID from the event to call the Get Magic Auth API.

You can skip this step if you're building your own authentication app.

### (B) Handle Magic Auth codes created via the API {{ "visibility": "no-quick-nav" }}

If you’re initiating Magic Auth authentication via the Create Magic Auth API, you can send your own email using the information returned in the Magic Auth object.

### Send your email {{ "visibility": "no-quick-nav" }}

The recipient of the email should match the `email` attribute for the Magic Auth object retrieved via the API, and the email should include the `code`. Recipients will input that code into AuthKit, or your own authentication UI, to authenticate into your application via Magic Auth.

---

## Email verification

Once you've turned off the default email verification emails, use the information below to send custom email verification emails.

**[email_verification.created](/events/email-verification)**
: Event emitted when a user requires email verification, which can be consumed using the events API or webhooks.

**[Get Email Verification API](/reference/authkit/email-verification/get)**
: Used to retrieve the email verification object from the ID in the email verification created event.

**[Email Verification Required error](/reference/authkit/authentication-errors/email-verification-required-error)**
: Returned in the API when attempting to authenticate a user that requires email verification.

### (A) Handle email verification codes created via AuthKit {{ "visibility": "no-quick-nav" }}

If you are using AuthKit, you’ll need to handle `email_verification.created` events, using the events API or webhooks.

Due to security concerns, the events do not contain the sensitive information you’ll need to send the email. To retrieve the full email verification object with this information, use the email verification ID from the event to call the Get Email Verification API.

You can skip this step if you're building your own authentication app.

### (B) Handle email verification codes created via the API {{ "visibility": "no-quick-nav" }}

If you are using the [authentication API](/reference/authkit/authentication), an `email_verification_required` error will be returned if the user you're authenticating needs to verify their email. This error contains an `email_verification_id` that can be used to call the Get Email Verification API endpoint which returns the email verification object that contains the information needed to send the email.

### Send your email {{ "visibility": "no-quick-nav" }}

The recipient of the email should match the `email` attribute for the email verification object retrieved via the API, and the email should include the `code`. Recipients will input that code into AuthKit, or your own authentication UI, to verify their email.

---

## Password reset

Once you've turned off the default password reset emails, use the information below to send custom password reset emails.

**[password_reset.created](/events/password-reset)**
: Event emitted when a user requests to reset their password, which can be consumed using the events API or webhooks.

**[Get Password Reset API](/reference/authkit/password-reset/get)**
: Used to retrieve the password reset object from the ID in the password reset created event.

**[Create Password Reset API](/reference/authkit/password-reset/create)**
: Used to create a password reset object via the API without handling the password reset created event.

### Set up your password reset URL {{ "visibility": "no-quick-nav" }}

Make sure you have the correct password reset URL set on your _Redirects_ page. The default setting is the AuthKit URL for resetting passwords. If you are using your own authentication UI, make sure the URL path is configured on your end to capture the `token` query parameter, and [use it to reset the password](/reference/authkit/password-reset/reset-password).

![A screenshot showing the WorkOS Dashboard configuration card for password reset URL](https://images.workoscdn.com/images/2b6abd95-459b-4f33-aab5-8217f412aed2.png?auto=format&fit=clip&q=50)

### (A) Handle password resets created via AuthKit {{ "visibility": "no-quick-nav" }}

If you are using AuthKit, you’ll need to handle `password_reset.created` events, using the events API or webhooks.

Due to security concerns, the events do not contain the sensitive information you’ll need to send the email. To retrieve the full password reset object with this information, use the password reset ID from the event to call the Get Password Reset API.

You can skip this step if you're building your own authentication app.

### (B) Handle password resets created via the API {{ "visibility": "no-quick-nav" }}

If you’re creating password resets via the Create Password Reset API, you can send your own email using the information returned in the password reset object.

### Send your email {{ "visibility": "no-quick-nav" }}

The recipient of the email should be the `email` attribute in the password reset object retrieved via the API. The body of the email should include a link where the user can reset their password. For most use cases, you can use the `password_reset_url` as this link.

If you're building your own authentication app, and your password reset path diverges from this pattern, you may want to construct your own URL with the `password_reset_token`, rather than using the `password_reset_url`.

### Custom Email Providers

Learn how to send emails through your own email service provider.

## Introduction

By default, WorkOS will send emails via our default email service provider, either through our domain or through your own [custom email domain](/custom-domains/email). If you would like to have more control over deliverability, reputation, and compliance, while still offloading the heavy lifting of email handling, you can configure a custom email provider. This option is also ideal if you already have an existing email service provider configuration.

---

## Configure a custom email provider

To configure a custom email provider for an environment, navigate to [_Emails_ → _Providers_](https://dashboard.workos.com/environment/emails/providers) and click _Enable_ next to the provider you would like to use and enter the required information.

> If the email service provider you'd like to use is not listed, please [contact support](mailto:support@workos.com).

![A screenshot showing the WorkOS Dashboard email providers page](https://images.workoscdn.com/images/660bd317-6240-40ce-8cb7-d80dccac294a.png?auto=format&fit=clip&q=80)

### Amazon SES

To connect WorkOS to Amazon SES, you'll need to [create an IAM user](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_users_create.html) with an access key, and [verify email sending identities](https://docs.aws.amazon.com/ses/latest/dg/creating-identities.html). Ensure the IAM user has a policy like the following:

```json
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "SendEmailAccess",
      "Effect": "Allow",
      "Action": "ses:SendEmail",
      "Resource": [
        "arn:aws:ses:::identity/*",
        "arn:aws:ses:::configuration-set/*"
      ]
    },
    {
      "Sid": "IdentityManagementAccess",
      "Effect": "Allow",
      "Action": ["ses:GetIdentityVerificationAttributes", "ses:ListIdentities"],
      "Resource": "*"
    }
  ]
}
```

You'll need to update the resource scope with your Amazon SES region and account ID. For additional ways to restrict access, refer to the [Amazon SES documentation](https://docs.aws.amazon.com/ses/latest/dg/control-user-access.html).

> If you're interested in using temporary security credentials to access Amazon SES, please [contact support](mailto:support@workos.com).

Once you have an IAM user with the necessary permissions and have verified sending identities, you can configure the Amazon SES custom email provider in the [WorkOS Dashboard](https://dashboard.workos.com/):

![A screenshot showing the WorkOS Dashboard Amazon SES custom email provider configuration](https://images.workoscdn.com/images/4df55e43-d68c-410c-9452-32978e0ca874.png?auto=format&fit=clip&q=50)

### Mailgun

Before configuring the Mailgun custom email provider in WorkOS, you'll need to [verify your domain in your Mailgun account](https://help.mailgun.com/hc/en-us/articles/32884700912923-Domain-Verification-Setup-Guide).

Once you've verified a domain, you'll need an API key, which you can create on the [API Security page of the Mailgun dashboard](https://app.mailgun.com/settings/api_security). The API key is used to validate verified domains in your account and send emails.

![A screenshot showing the Mailgun API Security page](https://images.workoscdn.com/images/fc6d06dd-790e-419e-ba0d-5a1783e8748d.png?auto=format&fit=clip&q=80)

Once you've verified your domain and obtained an API key, you can configure the Mailgun custom email provider in the [WorkOS Dashboard](https://dashboard.workos.com/):

![A screenshot showing the Mailgun custom email provider configuration](https://images.workoscdn.com/images/5e287a26-7851-49f5-95b5-bc74ab58b506.png?auto=format&fit=clip&q=80)

### Postmark

Before configuring the Postmark custom email provider in WorkOS, you'll need to [verify sender signatures in your Postmark account](https://postmarkapp.com/developer/user-guide/managing-your-account/managing-sender-signatures).

Once you've verified a sender signature, you'll need an account and server token, which you can find on the [API Tokens page of the Postmark dashboard](https://postmarkapp.com/account/api-tokens).

![A screenshot showing the Postmark API Tokens page](https://images.workoscdn.com/images/e441ca41-2768-4887-83f1-8f298ab25847.png?auto=format&fit=clip&q=50)

The account token is used to validate sender signatures in your account, and the server token is used to send emails.

Once you've verified your sender signature and obtained your account and server token, you can configure the Postmark custom email provider in the [WorkOS Dashboard](https://dashboard.workos.com/):

![A screenshot showing the Postmark custom email provider configuration](https://images.workoscdn.com/images/21f94d07-b661-4a24-9e2b-940cd0bbfdaa.png?auto=format&fit=clip&q=50)

Upon enabling the Postmark custom email provider, a WorkOS transactional message stream with the ID `workos-transactional-s` will be created for you. All WorkOS emails will be sent using this message stream.

### Resend

Before configuring the Resend custom email provider in WorkOS, you'll need to [verify domains in your Resend account](https://resend.com/docs/dashboard/domains/introduction).

Once you've verified your domain, you'll need to create an API key with the "Full access" permission on the [Resend API Keys page](https://resend.com/api-keys).

![A screenshot showing creating an API key in the Resend dashboard](https://images.workoscdn.com/images/ce0ae76c-7ab4-4506-9e48-78a61243a42d.png?auto=format&fit=clip&q=50)

"Full access" permission is required to fetch verified domains and send emails.

Once you've verified your domain and obtained an API key, you can configure the Resend custom email provider in the [WorkOS Dashboard](https://dashboard.workos.com/):

![A screenshot showing the Resend custom email provider configuration](https://images.workoscdn.com/images/25efadf6-405f-416b-aaa0-7e7fb266034a.png?auto=format&fit=clip&q=50)

### SendGrid

Before configuring the SendGrid custom email provider in WorkOS, you'll need to verify your domain in the [Sender Authentication settings in your SendGrid dashboard](https://app.sendgrid.com/settings/sender_auth).

Once you've verified your domain, you'll need to create an API key under _Settings_ → _API Keys_ in the [SendGrid dashboard](https://app.sendgrid.com/settings/api_keys).

![A screenshot showing the SendGrid API Keys side panel](https://images.workoscdn.com/images/61bf31b4-0e7f-4616-988f-69319d542866.png?auto=format&fit=clip&q=50)

For API key permissions, select "Full Access" for Mail Send, and "Read Access" for Sender Authentication.

Once you've verified your domain and obtained an API key, you can configure the SendGrid custom email provider in the [WorkOS Dashboard](https://dashboard.workos.com/):

![A screenshot showing the SendGrid custom email provider configuration](https://images.workoscdn.com/images/4502c8ad-2bdf-481b-82a7-0d8829032da4.png?auto=format&fit=clip&q=50)

---

## Re-enable the WorkOS default provider

At any time when you are using a custom email provider, you can re-enable the WorkOS default provider by navigating to [_Emails_ → _Providers_](https://dashboard.workos.com/environment/emails/providers) and clicking _Enable_ next to the WorkOS provider.

![A screenshot showing re-enabling the WorkOS default provider](https://images.workoscdn.com/images/7b4f4713-3381-49aa-b77c-679c9698b429.png?auto=format&fit=clip&q=80)

Alternatively, you can also remove your current custom email provider, which will automatically re-enable the WorkOS default provider.

![A screenshot showing removing a custom email provider](https://images.workoscdn.com/images/7b4f4713-3381-49aa-b77c-679c9698b429.png?auto=format&fit=clip&q=80)

---

## Frequently asked questions

### What types of emails are sent through custom email providers?

All transactional emails for your users will be sent through your custom email provider when configured. This includes AuthKit invitations and magic codes, Radar challenges, and Admin Portal notification emails.

### If I am using a custom email provider, do I need to set up a custom email domain in WorkOS?

No. When using a custom email provider, you configure the sending domain in that provider, not in WorkOS. Any custom email domain set up in WorkOS will not be used.

### What happens if emails fail to send via my custom email provider?

If emails fail to send via your custom email provider, you will be notified according to your notifications preferences via the [WorkOS Dashboard](https://dashboard.workos.com/), email, or Slack. You can also utilize the [_Emails_ → _Events_ page](https://dashboard.workos.com/environment/emails/events) in the WorkOS Dashboard to track email delivery failures.

### Connect

Enable other applications to access your users and their identities.

## Introduction

Connect is a set of controls and APIs that developers can use to allow different types of applications to access their users' identity and resources. Connect is built on top of industry-standard specifications like OAuth 2.0 and OpenID Connect in order to support many common use-cases out of the box.

Unlike AuthKit's other features that help users sign into **your application**, Connect enables **other applications** to authenticate and access your users' data through secure, managed APIs.

## Common use-cases

**Customer applications**
: Enable your customers to build custom integrations with your platform. This can include allowing them to add a "Sign in with [your app]" button on their login page.

**Auxiliary applications**
: Allow secondary applications that support your primary application, such as support tools or discussion forums, to authenticate using the same user identities in AuthKit.

**Partner integrations**
: Issue credentials for trusted partners to authenticate with when calling your application's API.

## Getting started

Each Connect integration is defined as an Application, which can be created inside of the WorkOS Dashboard.

When creating an application, you first choose the type of integration: **OAuth** or Machine-to-Machine (**M2M**).

### OAuth applications

Select OAuth when building web or mobile applications where the actor being authenticated is a [User](/reference/authkit/user). Integrating with an OAuth application uses the underlying `authorization_code` OAuth flow which is supported by many libraries and frameworks out of the box.

Upon successful authorization, the issued tokens will contain information about the user who signed in.

[Learn more about OAuth applications →](/authkit/connect/oauth)

### M2M applications

Select M2M when the application will be a third-party service, such as one of your customer's applications. Integrating with an M2M application uses the underlying `client_credentials` flow.

Unlike OAuth applications, the actor being authenticated is not an individual user. Instead issued access tokens will contain an `org_id` claim which represents the customer you are granting access to via the M2M application.

The M2M application will use its `client_id` and `client_secret` to authenticate requests to your application's API or services.

[Learn more about M2M applications →](/authkit/connect/m2m)

## Concepts

All Connect applications share the following concepts:

### Participants

When using Connect, there are several actors involved with the integration of each Application:

- **Relying Party**: The application that receives Connect-issued tokens and identity information. It may also use the access token to make requests to your API.
- **Resource server**: The service (generally your app) that allows other clients to authenticate using the Connect-issued tokens.
- **Authorization server**: This is Connect, the issuer of identity and access tokens to requesting clients after authenticating the user.

### Credentials

Applications can have up to 5 credentials. These are only shown once upon creation and do not expire. The application `client_id` and `client_secret` from a credential can be used to authenticate to the [Connect APIs](/reference/workos-connect).

When sharing app credentials with an external party, use a secure method — like encrypted email or file sharing — and make sure the recipient is properly authenticated.

### CLI Auth

Quickly add authentication to your command-line application.

## Introduction

CLI Auth enables your command-line applications to authenticate users through the web via your WorkOS app. Based on the [OAuth 2.0 Device Authorization Flow](https://datatracker.ietf.org/doc/html/rfc8628), this flow is optimized for devices that lack a web browser or have limited input capabilities.

With CLI Auth, your command-line app requests a device authorization from WorkOS, which includes a code for the user and a code for your app. After the user confirms the code, your app can exchanges its device code for tokens.

## (1) Request device authorization

To begin the authentication flow, your CLI application makes a request to the `/authorize/device` endpoint to obtain the necessary codes and URLs for user authentication.



After you get a response, your app can provide next steps to the user.

![Screenshot of a command-line application showing login information](https://images.workoscdn.com/images/89680848-5c40-4ba1-860f-a7212cfbb47b.png?auto=format&fit=clip&q=50)

Your application should display the `user_code` from the response, along with the `verification_uri` in the terminal. If you offer the ability to open in a browser easily like in this screenshot, we suggest using the `verification_uri_complete` for that.

Never display the `device_code` to the user. That is only for the device to poll the token endpoint.

## (2) User confirms the code

Next the user needs to confirm the code in their browser.

### (A) Manual code entry

If the user navigates to the `verification_uri`, they'll be presented with a form to enter the code manually. If they are not logged in they'll be prompted to do that first and then returned to the code entry screen.

![Screenshot of the manual-code-entry form in AuthKit](https://images.workoscdn.com/images/03c1961a-547a-4886-bb9a-2f080dd31ca9.png?auto=format&fit=clip&q=50)

### (B) One-click confirmation

If the user goes to the `verification_uri_complete`, (for example, `https:///device?user_code=ABCD-EFGH`, they'll instead need to confirm that the code matches what is displayed in the terminal.

![Screenshot of the pre-filled code-confirmation form in AuthKit](https://images.workoscdn.com/images/3e02f36c-0dea-4ff4-9dc5-a2f2fa964114.png?auto=format&fit=clip&q=50)

## (3) Request tokens

While the user is completing authentication in their browser, your CLI application should poll the token endpoint to check for authorization completion.

Make requests to the token endpoint using `device_code` from the authorization response from step 1:



### Polling best practices

- Poll at the interval specified in the authorization response (every 5 seconds)
- Respect `slow_down` errors by increasing your polling interval
- Stop polling when you receive `access_denied` or `expired_token` errors
- Implement a reasonable timeout to avoid infinite polling

## Connect

CLI Auth is available for [Connect](/authkit/connect/oauth) applications, allowing you and third-party developers to build CLI tools that integrate with your app's credentials.

The flow is the same but uses Connect endpoints:

- **Authorization**: `https:///oauth2/device_authorization`
- **Token**: `https:///oauth2/token`

Since command-line applications are distributed to end users, you should avoid embedding the client secret in the app. To make this work, set up your Connect app as a [_Public_ application](/authkit/connect/oauth/public-applications).

Third-party Connect applications will require users to [grant consent](/authkit/connect/oauth/first-party-vs-third-party-applications) to the third-party app.

### Branding

Customize AuthKit to fit natively with your app’s unique design.

## Introduction

You can customize the look and feel of AuthKit via the _Branding_ section of the [WorkOS Dashboard](https://dashboard.workos.com/branding).

The brand editor allows you to:

- Upload logos and favicons
- Set brand colors for buttons, links, and backgrounds
- Manage visual properties such as page layouts, corner radius, and dark mode appearance
- Include custom copy, images, and links to your app’s terms-of-service and privacy policy
- Preview auth screens and emails in various languages, and translate custom text into every supported locale

The AuthKit preview will update in real-time as you make changes and accurately reflect the available authentication methods, giving you a clear picture of the authentication experience with AuthKit.

![Branding in the Dashboard](https://images.workoscdn.com/images/fc67ec10-44e1-467c-a094-32ed3ff5bd92.png?auto=format&fit=clip&q=80)

## Custom domains

WorkOS supports custom domains for both email and [ACS URLs](/glossary/acs-url). For for information, see the [custom domains documentation](/custom-domains).

## Appearance

AuthKit supports both light and dark mode; each brand configuration option is split across both so that they can be configured independently. You can enforce a specific appearance, or allow the user’s OS system settings to determine which to use.

The corner radius applied to UI elements can also be configured; a lower value will result in a more formal aesthetic while a higher value has a more rounded, playful feel.

![Appearance options highlighted in the branding editor](https://images.workoscdn.com/images/3465072a-87a2-46cc-8577-4e9d4213009a.png?auto=format&fit=clip&q=50)

## Font Family

You can customize the font family used across AuthKit pages to match your brand's typography. The font family selector allows you to choose from a wide variety of Google Fonts to align with your product's brand. Only Google Fonts are supported for font family customization. This ensures optimal loading performance and reliability across all devices and browsers.

![Font family options in the branding editor](https://images.workoscdn.com/images/4db463c8-4eb6-414d-801b-e7460407b238.png?auto=format&fit=clip&q=80)

## Assets

You can upload custom brand assets to display in AuthKit, transactional emails, and the [Admin Portal](/admin-portal).

![Asset options highlighted in the branding editor](https://images.workoscdn.com/images/2a6e77b6-c1fe-4850-a95b-f3e7b7d8bf87.png?auto=format&fit=clip&q=50)

There are three types of assets you can upload:

1. **Logo:** Your full size brand logo, styles vary but this would typically include the wordmark. Must be at least 160x160 px (JPG, PNG, or SVG. 100 KB max size)
2. **Logo icon:** A smaller, square version of the logo. This is often simply the logomark. Must be at least 160x160 px with a 1:1 aspect ratio (JPG, PNG, or SVG. 100 KB max size)
3. **Favicon:** A small icon that serves as branding for your website. It is often displayed in the browser tab alongside the address bar. Must be at least 32x32 px with a 1:1 aspect ratio (JPG, PNG, GIF, SVG, WebP, AVIF, or ICO. 100 KB max size)

### Logo style

Either the logo or the logo icon can be displayed in AuthKit. To select which to use, click the logo in the AuthKit preview after uploading both assets.

![Logo selection dialog open in the branding editor](https://images.workoscdn.com/images/2fbd9b69-3434-412b-ab53-e73568f3eb9a.png?auto=format&fit=clip&q=50)

## Color

You can control four colors across light and dark mode:

- Page background color
- Button background colors
- Button text color
- Link color

Other colors used in the UI, like the focus outline, hover styles, or borders, are created automatically based on the four colors you provide, ensuring a consistent look and feel.

![Color options in the branding editor](https://images.workoscdn.com/images/b6a2eb40-2510-4e54-bdca-0c91953fb84d.png?auto=format&fit=clip&q=50)

## Localization

You can preview how your auth pages and emails appear in various different languages. AuthKit is [localized](/authkit/hosted-ui/localization) in many languages by default, and users are served in their preferred language automatically.

To preview your brand in different languages, use the language picker in the AuthKit preview pane.

![A preview of a user-facing email, translated in Spanish](https://images.workoscdn.com/images/663ee483-a14c-420b-9ba5-5c4c1f277b2c.png?auto=format&fit=clip&q=50)

## Copy

The page title and alternate action link text on AuthKit pages can be customized to fit your brand’s tone of voice. They can be edited directly inside the AuthKit preview pane.

> An _alternate action_ refers to the action a user will take to navigate between AuthKit pages. For example, if a user is on the sign-in page and they don’t have an account, the alternate action will navigate to the signup page.

Start by selecting the page you want to edit. Then, click on the text you want to change from the preview pane.

![AuthKit page selector in the branding editor](https://images.workoscdn.com/images/6b2c0b3d-9f2f-404f-a893-23c3ec8a2d6f.png?auto=format&fit=clip&q=50)

![Text customization highlighted in the branding editor](https://images.workoscdn.com/images/07734ffb-c639-4abd-8d99-bd1feb9d5eda.png?auto=format&fit=clip&q=50)

When you edit copy in English, it automatically gets translated into [every supported language](/authkit/hosted-ui/localization). A loading indicator appears next to the language picker during this process. After you save, your users will be served the translation that closest matches their locale.

## Page settings

AuthKit pages can optionally display a link to your app’s privacy policy and/or terms-of-service. The link will then appear below the authentication form.

AuthKit also allows you to choose whether or not first name and last names are required when signing up. To toggle either of these, select the _Page Settings_ panel and update the respective field.

![Page settings in the branding editor](https://images.workoscdn.com/images/6b2e6b35-5658-42ab-8c6a-6b6aa0d9b381.png?auto=format&fit=clip&q=80)

## Page layout

The layout for AuthKit pages can be customized to fit your brand’s needs. You can choose between a centered, one-column layout, or a two-column layout using [custom HTML and CSS](#custom-code-details-and-limitations) for the secondary column.

![Page settings in the branding editor](https://images.workoscdn.com/images/70acf0d4-caf8-435e-bedd-1960e8cd27c4.png?auto=format&fit=clip&q=80)

### Split layouts

Split page layouts allow you to add a secondary panel on AuthKit pages that can be customized with HTML and CSS. This can be used to display marketing content or decorative elements on the page.

To enable this feature, select the page you want to customize. Then, select the _Split_ layout option under _Page Settings_. The secondary panel can be displayed to the left or right of the primary panel, and optionally hidden on mobile devices.

![Split layout setting in the branding editor](https://images.workoscdn.com/images/057b92eb-bf5d-4832-b8c3-33f470e766f1.png?auto=format&fit=clip&q=80)

Click on the secondary column from the preview pane. This will open a dialog where you can enter your HTML and CSS.

> Note: content in the content panel will not automatically be [localized](/authkit/hosted-ui/localization).

![Custom code editor dialog in the branding editor](https://images.workoscdn.com/images/e0c512c9-dee0-470b-aa55-7d89c1b44a5c.png?auto=format&fit=clip&q=80)

### Custom code details and limitations

Any HTML and CSS entered into the custom code dialog will only be applied to the secondary column of the selected page. This allows you a high level of flexibility without impacting content elsewhere on the page.

For security purposes, all code input is sanitized and stripped of any potentially harmful elements. This means that you can’t use JavaScript or any other dynamic content in your HTML. This includes `script`, `iframe`, `form`, and `object` elements—as well as inline event handlers for any elements.

For example, the following code will be sanitized from this:

```html
Welcome to SuperApp</h1>

  const onClick = () => alert('Warning!');

```

…to this:

```html
Welcome to SuperApp</h1>
```

HTML `style` elements will also be removed to prevent overriding any content outside of the secondary panel. All custom CSS should be entered into the CSS editor.

CSS selectors will be scoped to the secondary column via [CSS nesting](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_nesting/Using_CSS_nesting). For compatibility with older browsers, we use a light transform step to convert the nested CSS to a flat structure.

For example, the following CSS will be transformed from this:

```css
h1 {
  color: var(--primary-color);
}
```

…to this:

```css
:where([data-hak-custom-html]) h1 {
  color: var(--primary-color);
}
```

## Last used sign-in badge

AuthKit sign-in pages can optionally display a _Last used_ badge on an authentication method. This will indicate the most recent sign-in method for the user. The badge is shown by default and only shown when multiple sign-in methods are available.

![AuthKit Last used sign-in badge](https://images.workoscdn.com/images/2f0e3778-08f3-4eb4-b590-0b39ff92e0d7.png?auto=format&fit=clip&q=50)

## Custom CSS

For more granular control over AuthKit branding, element styles can be overridden using custom CSS. Custom CSS applies globally across all AuthKit pages to ensure consistency across the entire authentication experience. It does not affect emails or Admin Portal.

> AuthKit is powered by [Radix](https://www.radix-ui.com/) which has built-in accessibility and dark mode. If overriding styles, please make sure to test thoroughly, especially if removing original element styles.

![AuthKit Custom CSS in the brand editor](https://images.workoscdn.com/images/0f48c7f3-b99c-417a-bee9-2e54780515df.png?auto=format&fit=clip&q=80)

### Customize a specific page

Target specific pages using the `data-hak-page` attribute selector:

```css
.ak-Header {
  /* focus-start */
  [data-hak-page='sign-up'] & {
    .ak-Heading {
      font-size: 3rem;
      line-height: 1;
    }
  }
  /* focus-end */
}
```

- #### List of all available pages

  **`sign-in`**
  : Main sign-in page

  **`sign-in/password`**
  : Password-based sign-in

  **`sign-in/passkey/enroll`**
  : Passkey enrollment during sign-in

  **`sign-up`**
  : Main signup page

  **`sign-up/password`**
  : Password-based signup

  **`sign-up/passkey`**
  : Passkey-based signup

  **`sign-up/magic-auth`**
  : Magic link signup

  **`sign-up/registration`**
  : Custom registration form

  **`oauth`**
  : OAuth provider selection

  **`magic-code`**
  : Magic code verification

  **`magic-code/send`**
  : Magic code request form

  **`mfa/enrollment`**
  : MFA setup/enrollment

  **`mfa/verification`**
  : MFA code verification

  **`email-verification`**
  : Email verification page

  **`radar-challenge`**
  : Fraud detection challenge

  **`radar-challenge/send`**
  : Phone number input for SMS challenge

  **`radar-challenge/verify`**
  : SMS verification code input

  **`invite`**
  : Invitation acceptance page

  **`reset-password`**
  : Password reset flow

  **`organization-selection`**
  : Organization picker

  **`device`**
  : Device activation page

  **`device/success`**
  : Successful device connection

  **`device/denied`**
  : Device connection denied

  **`application-authorization`**
  : App consent/authorization page

  **`default-redirect`**
  : Default redirect after successful auth

  **`not-found`**
  : 404 error page

  **`auth-disabled`**
  : Authentication disabled message

### Light and dark theme

Use the [light-dark](https://developer.mozilla.org/en-US/docs/Web/CSS/color_value/light-dark) CSS function to easily target both light and dark themes with a single declaration:

```css
.ak-PrimaryButton {
  /* focus-start */
  color: light-dark(#333333, #f0f0f0);
  /* focus-end */
}
```

For more control, target the parent theme selectors directly:

```css
.ak-Background {
  /* focus-start */
  .dark-theme & {
    background: linear-gradient(0deg, #333, #111);
  }

  .light-theme & {
    background: linear-gradient(0deg, #fff, #ccc);
  }
  /* focus-end */
}
```

> Media queries targeting `prefers-color-scheme` are not supported – use only the `.dark-theme` and `.light-theme` selectors.

### Nested selectors

AuthKit provides intelligent autocomplete support for CSS selectors. When you type a period (`.`) in the custom CSS editor, a popover will automatically appear showing available nested selectors for AuthKit elements, making it easier to target specific components and their child elements.

![Nested CSS selectors](https://images.workoscdn.com/images/e8e41033-9e08-40c5-bfc9-0e223b1bd890.png?auto=format&fit=clip&q=80)

### Examples

#### Custom background image

You can use external images as background images by specifying the URL in the `background-image` property.

```css
.ak-Background {
  /* focus-start */
  background-image: url('https://i.imgur.com/HO2EBgR.jpeg');
  background-size: cover;
  /* focus-end */
}
```

#### Reorder OAuth buttons

You can target an individual provider button by its `data-method` attribute.

```css
.ak-AuthButton {
  /* focus-start */
  /* Display Microsoft OAuth button first */
  &[data-method='microsoft'] {
    order: -1;
  }
  /* focus-end */
}
```

#### Adding custom text

Use CSS pseudo-elements to add custom text content.

Custom text content in CSS cannot be [localized](/authkit/hosted-ui/localization). To learn how to automatically localize the text of your custom headings and links, read the [Copy](/authkit/branding/copy) section.

```css
.ak-Header {
  /* focus-start */
  &::after {
    content: 'Sub heading';
    display: block;
  }
  /* focus-end */
}
```

> Some elements may already style the `::before` and `::after` pseudo-elements, so test your changes carefully.

### API Keys

Provide secure, self-service API key management to your customers.

## Introduction

API keys provide a secure way for your application's users to authenticate with your API. With the [API Keys Widget](/widgets/api-keys), your customers can create and revoke [organization](/authkit/users-organizations/organizations)-scoped API keys with a simple component. The WorkOS API and SDKs provide functions for your API code to validate keys.

API keys are one of two ways WorkOS enables you to issue credentials to your customers that they use to programmatically access your application. The other is [M2M applications](/authkit/connect/m2m). The [API Keys vs M2M Applications guide](https://workos.com/blog/api-keys-vs-m2m-applications) can help you decide which is best for your use case.

## Configuring API keys

Before your users can manage API keys, you need to configure your WorkOS environment.

### Setting up role permissions

To enable API key management for your users, ensure at least one role includes the `widgets:api-keys:manage` permission. This permission allows users to access the [API Keys Widget](/widgets/api-keys) and manage keys within their organization.

You can [assign permissions to roles](/authkit/roles-and-permissions/configure-roles-and-permissions/assign-permissions-to-roles) in the WorkOS Dashboard under _Roles & Permissions_.

### Configuring available permissions

You can control which permissions your users can assign to API keys by configuring API key permissions in your environment.

For example, you might create permissions like:

- `posts:read` - Read access to posts
- `posts:write` - Write access to posts
- `users:read` - Read access to user data

By configuring only `posts:read` and `posts:write` as available API key permissions, your users can create API keys with granular access controls, such as read-only keys that only have the `posts:read` permission.

You can configure API key permissions in the WorkOS Dashboard on the _Roles & Permissions_ page under _Organization API Key Permissions_.

## API key management in your application

The easiest way to enable API key management for your users is through the [API Keys Widget](/widgets/api-keys). This widget provides a complete interface for creating, viewing, and revoking API keys.

The widget allows your users to:

- Create new API keys with custom names
- Select specific permissions for each key
- View existing API keys (with obfuscated values for security)
- Revoke API keys when they're no longer needed

The widget interacts with the WorkOS API and renders the user interface in your app, so your customers get full control over their API keys in just a few lines of code.

## Validating API keys

Once your users have created API keys through the widget, your application needs to validate these keys when they're used to authenticate API requests. When an API request includes an API key (typically in the `Authorization` header), your application should validate it with WorkOS to ensure it's legitimate and retrieve the associated permissions.

The [validate API key endpoint](/reference/authkit/api-keys/validate) returns the complete [API key object](/reference/authkit/api-keys), including:

- The organization that owns the key
- The permissions assigned to the key
- Usage metadata like creation and last-used timestamps

This information allows your application to not only authenticate the request but also authorize it based on the specific permissions granted to that API key.



## Viewing organization API keys in the WorkOS Dashboard

You can view and revoke your customers' API keys through the WorkOS Dashboard:

1. Navigate to the **Organizations** section in your WorkOS Dashboard
2. Click on the organization you want to view
3. Select the **API Keys** tab

From this view, you can see all API keys created by the organization, including their names, permissions, creation dates, and last usage information. This provides valuable visibility into how your customers are using API keys.

## Auditing API key usage

API key lifecycle changes are tracked via the [`api_key.created`](/events/api-key) and [`api_key.revoked`](/events/api-key) events. You can view these events in the [events page](https://dashboard.workos.com/environment/events) or listen for them in your application via the [events API](/events).

### Actions

Customize authentication flows with your own logic.

## Introduction

Actions allow you to change the behavior of various flows within the WorkOS platform including user registration and authentication using your own custom logic.

When an action is configured for a particular request type, WorkOS synchronously calls the associated action endpoint and waits for a response that allows or denies the operation. When WorkOS calls an actions endpoint, the request includes contextual metadata such as the profile of the user performing the operation, the organization associated with the operation, or the IP address, all of which you can use for decisioning within the endpoint.

### Action types

WorkOS allows you to configure actions that execute during various user operations:

- **Authentication**: Authentication actions run after a user completes Email + Password, Magic Auth authentication, SSO, or Social Login and before they are redirected to your application.
- **User registration**: User registration actions run after a user attempts to register for your application using Email + Password, Magic Auth sign up, SSO, or Social Login and before they are provisioned.

## Configuring actions

To configure actions, you'll need to:

- Host an actions endpoint that receives requests from WorkOS
- Register your endpoints with WorkOS
- Implement the custom logic of your endpoint
- Test your endpoints

## Set up your endpoint

Create a public endpoint that WorkOS can make requests to. This endpoint should use HTTPS and should accept POST requests with the `workos-signature` header. This header is used for verifying the request's authenticity from WorkOS.



> WorkOS sends the header as `WorkOS-Signature`, but many web servers normalize HTTP request headers to their lowercase variants.

---

## Register your endpoint

Set the actions endpoint URL in the [WorkOS Dashboard](https://dashboard.workos.com/). Set _Enable action_ and choose **Save changes**.

![WorkOS Dashboard Actions UI](https://images.workoscdn.com/images/84c8a62b-d8bc-4c46-8ccd-eda4c245645e.png?auto=format&fit=clip&q=80)

### Choosing error handling behavior

Each actions endpoint must specify its error handling behavior. By default, if there is an issue reaching your endpoint or validating the response, WorkOS will deny the operation. If this is not the desired behavior for your use case, you can choose a different behavior depending on the action endpoint type; for example, for authentication actions, you can choose _Allow authentication_.

---

## Implement your endpoint

Upon receiving a request, you should respond with an `HTTP 200 OK` as well as a valid response body to signal to WorkOS that the request was successfully handled.

### (A) Validate the requests using the SDK

Before processing the request payload, verify the request was sent by WorkOS and not an unknown party.

WorkOS includes a unique signature in each actions request that it sends, allowing you to verify the authenticity of the request. In order to verify this signature, you must obtain the secret that is generated for you when you set up your actions endpoint in the WorkOS dashboard. Ensure that this secret is stored securely on your actions endpoint server as an environment variable.

The SDKs provide a method to validate the timestamp and signature of an actions. Examples using these methods are included below. The parameters are the payload (raw request body), the request header, and the actions secret.



There is an optional parameter, tolerance, that sets the time validation for the actions request in seconds. The SDK methods have default values for tolerance, usually 3–5 minutes.

### (B) Validate the requests manually

If implementing actions request validation yourself, you’ll need to use the following steps:

First, extract the timestamp and signature from the header. There are two values to parse from the `WorkOS-Signature` header, delimited by a `,` character.

| Key                | Value                                                                                           |
| ------------------ | ----------------------------------------------------------------------------------------------- |
| `issued_timestamp` | The number of milliseconds since the epoch time at which the event was issued, prefixed by `t=` |
| `signature_hash`   | The HMAC SHA256 hashed signature for the request, prefixed by `v1=`                             |

To avoid replay attacks, we suggest validating that the `issued_timestamp` does not differ too much from the current time.

Next, construct the expected signature. The expected signature is computed from the concatenation of:

1. `issued_timestamp`
2. The `.` character
3. The request’s body as a utf-8 decoded string

Hash the string using HMAC SHA256, using the actions secret as the key. The expected signature will be the hex digest of the hash. Finally, compare signatures to make sure the actions request is valid.

Once you’ve determined the event request is validly signed, it’s safe to use the event payload in your application’s business logic.

### Create an IP allowlist

WorkOS sends actions requests from a fixed set of IP addresses. It’s recommended to restrict access to your actions endpoint to only these IP addresses:

```plain title="WorkOS IP addresses"
3.217.146.166
23.21.184.92
34.204.154.149
44.213.245.178
44.215.236.82
50.16.203.9
52.1.251.34
52.21.49.187
174.129.36.47
```

---

## Build the endpoint response

The endpoint must respond with a signed JSON object indicating a `verdict` of `Allow` or `Deny` as well as an optional `error_message` in the event the `verdict` is `Deny`.

Based on the payload data received, you can determine whether to allow or deny the operation. Each action type receives a different payload model, so be sure to handle the appropriate data in your handler.

### (A) Respond using the SDK

The SDK provides a method to create the signed response.



### (B) Respond manually

If implementing the construction of the actions response yourself, you’ll need to use the following steps:

First, store the current epoch timestamp to a variable.

Next, construct the JSON response. The JSON response must contain the following:

- `timestamp`: The epoch timestamp you recorded
- `verdict`: Indicates whether to allow or deny the action. Allowed values: `'Allow' | 'Deny' | 'allow' | 'deny'`
- `error_message`: An optional, 500 character maximum string. This should only be provided with a `verdict` of `deny` or `Deny`

Next, construct the signature. The expected signature is computed from the concatenation of:

1. The current epoch timestamp
2. The `.` character
3. The JSON response body as a utf-8 encoded string

Hash the string using HMAC SHA256, using the actions secret as the key. The expected signature will be the hex digest of the hash.

Finally, the endpoint should respond with a JSON object containing the following properties:

- `object`: `'authentication_action_response' | 'user_registration_action_response'`
- `payload`: The JSON response you formed above
- `signature` The hex digest of the hash you created above

## Test your endpoint

From the dashboard, you can send test actions after configuring an endpoint. Go to the actions _Test_ tab and click the **Send test action** button.

![A screenshot showing how to send a test action in the WorkOS dashboard.](https://images.workoscdn.com/images/8d998ae0-2efa-435a-9818-6873fcdc73ac.png?auto=format&fit=clip&q=80)

If you would like to test against your local development environment, we recommend using a tool like [ngrok](https://ngrok.com) to create a secure tunnel to your local machine, and sending test webhooks to the public endpoint generated with ngrok. See our [blog post](https://workos.com/blog/test-workos-webhooks-locally-ngrok) to get more details on how you may want to test webhooks locally with ngrok.

---

### Standalone Connect

Integrate Connect's OAuth API with your existing authentication stack.

## Overview

Standalone Connect allows applications with existing authentication systems to use AuthKit as their OAuth authorization server. You maintain your existing authentication stack while leveraging AuthKit's OAuth infrastructure for token issuance and management.

Unlike standard AuthKit integration, Standalone Connect delegates authentication to your application, then handles the OAuth flow and token issuance for OAuth clients. This feature works with [OAuth applications](/authkit/connect/oauth) only—[M2M applications](/authkit/connect/m2m) use the `client_credentials` flow which doesn't involve user authentication.

## When to use Standalone Connect

Use Standalone Connect when you:

- Have an existing authentication stack in your application.
- Need to enable OAuth-based integrations, like a [CLI](/authkit/cli-auth) app or [MCP](/authkit/mcp) server.
- Want to support third-party applications accessing your users' resources.

## Getting started

### Create an OAuth application

Before testing your Standalone Connect integration, you'll need to [create an OAuth application](/authkit/connect/oauth) in the WorkOS Dashboard. If you need to support MCP auth, you'll need to enable Client ID Metadata Document (CIMD), which you can read more about in the [MCP guide](/authkit/mcp).

### Configure your Login URI

Navigate to _Connect_ → _Configuration_ in the [WorkOS Dashboard](https://dashboard.workos.com) and provide your **Login URI**.

The **Login URI** is where AuthKit will redirect users to authenticate with your existing system. You can only configure one Login URI per environment.

Your Login URI must:

- Use HTTPS in production.
- Accept an `external_auth_id` query parameter.
- Authenticate users with your existing system.
- Call the AuthKit completion API after successful authentication. More on that below.

Example: `https://your-app.example.com/auth/login`

## Authentication flow

### (1) OAuth client initiates flow

OAuth clients start the authorization flow with AuthKit following standard OAuth 2.0. This could be a third-party application, partner integration, or any OAuth-enabled client.

### (2) AuthKit redirects to your application

AuthKit redirects users to your Login URI with an `external_auth_id` parameter:

```txt
https://your-app.example.com/auth/login?external_auth_id=01J3X4Y5Z6A7B8C9D0E1F2G3H4
```

The `external_auth_id` is a temporary identifier used to complete the flow with AuthKit.

### (3) Authenticate the user

Your application authenticates users with your existing system. If users have an active session, skip to the next step.

AuthKit handles OAuth consent separately, so your application doesn't need to display any consent screens.

### (4) Complete authentication with AuthKit

Call the [AuthKit completion API](/reference/workos-connect/standalone/complete) after authenticating the user, passing the `external_auth_id` that your Login URI originally received:

```js
const response = await fetch('https://api.workos.com/authkit/oauth2/complete', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': `Bearer ${process.env.WORKOS_API_KEY}`,
  },
  body: JSON.stringify({
    external_auth_id: externalAuthId,
    user: {
      id: user.id,
      email: user.email,
      first_name: user.firstName,
      last_name: user.lastName,
      metadata: { department: user.department },
    },
  }),
});

const { redirect_uri } = await response.json();
```

### (5) Return control to AuthKit

Redirect users to the `redirect_uri` from the API response. AuthKit displays a consent screen if needed, issues tokens, and completes the OAuth flow.

## Integrating via the API

### Dynamic consent options

You can provide `user_consent_options` to display options during the OAuth consent screen. This is useful when users need to choose specific resources or contexts (like a parent resource in your application) to grant access to.

Each consent option must include:

- `claim`: The name of the access token claim that will hold the user's selected value.
- `type`: The format of the option. Only `enum` is supported currently.
- `label`: Display text for the option.
- `choices`: Array of choices (can be flat or grouped).

```js
const response = await fetch('https://api.workos.com/authkit/oauth2/complete', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': `Bearer ${process.env.WORKOS_API_KEY}`,
  },
  body: JSON.stringify({
    external_auth_id,
    user: { id: user.id, email: user.email },
    user_consent_options: [
      {
        claim: 'urn:example:tenant',
        type: 'enum',
        label: 'Environment',
        choices: [
          {
            group: 'Production',
            choices: [
              { value: 'prod_us', label: 'US-East' },
              { value: 'prod_eu', label: 'EU-West' },
            ],
          },
          {
            group: 'Development',
            choices: [
              { value: 'dev_main', label: 'Development' },
              { value: 'staging', label: 'Staging' },
            ],
          },
        ],
      },
    ],
  }),
});
```

The selected values appear as custom claims in the issued tokens. For example, if the user selects one of the above options, the token will include:

```json
{
  "sub": "user_123",
  "email": "user@example.com",
  "urn:example:tenant": "prod_us"
  // ...
}
```

### Error handling

If the `external_auth_id` is invalid, the call to the AuthKit completion API will fail. Your application is free to choose how to handle this case, but redirecting to your application's homepage is recommended.

### Token verification

Resource servers verify tokens issued by AuthKit using standard Connect token verification:

```js
;

const JWKS = createRemoteJWKSet(new URL('https://authkit_domain/oauth2/jwks'));

async function verifyToken(token) {
  const { payload } = await jwtVerify(token, JWKS, {
    audience: 'client_123456789',
    issuer: 'https://authkit_domain',
  });

  return payload;
}
```

See the [Connect documentation](/authkit/connect/oauth/verifying-tokens) for details on token expiration, refresh tokens, and revocation.

### OAuth Applications

Integrate OAuth applications with WorkOS Connect for web and mobile authentication.

## Overview

OAuth applications are designed for applications where the actor being authenticated is a [User](/reference/authkit/user). These include web applications, mobile, desktop, and CLI tools. OAuth applications use the underlying `authorization_code` OAuth flow which is supported by many libraries and frameworks out of the box.

> For server-to-server requests from a third-party without user interaction, use [M2M applications](/authkit/connect/m2m) instead.

## First-party vs Third-party Applications

When creating OAuth applications, you choose the level of trust: first-party or third-party.

### First-party applications

Select first-party when the application is one that your team controls, such as supporting services that are deployed separately from your main application but still need access to your users' identities. Examples include community forums or customer support portals.

### Third-party applications

Select third-party when the application is one built by your customers or partners, but you do not directly control the integrating application. For this reason, you must also associate third-party applications with an [Organization](/reference/organization) that represents the customer or partner.

A third-party OAuth application will generally have a "Sign in with [your application]" button on their login page, in the same way many sites have a "Sign in with Google" button, allowing you to offer similar functionality to your customers or partners. Unlike first-party applications, your users will be prompted in AuthKit to explicitly authorize the application before their identity is shared.

![Screenshot of the application authorization screen in AuthKit.](https://images.workoscdn.com/images/afde561f-9378-4aa6-995c-cda8f3ec0a63.png)

## Receiving Tokens

After an application has been issued credentials from a Connect Application, it can receive identity and access tokens using the OAuth 2.0 `authorization_code` flow.

Many OAuth and OIDC libraries support Connect applications out of the box, needing only configuration:



## Public Applications

By default, OAuth applications are confidential and must authenticate with a client secret when exchanging authorization codes for tokens. However, certain types of applications cannot securely store client secrets, such as command-line tools or mobile applications.

For these use cases, you can configure an OAuth application as **Public**. Public applications:

- Cannot securely store client secrets
- Must use [Proof Key for Code Exchange (PKCE)](https://datatracker.ietf.org/doc/html/rfc7636) in order to authenticate with the [Token Endpoint](/reference/workos-connect/token)

OAuth applications can be set as _Public_ during creation in the WorkOS Dashboard.

## Verifying Tokens

Your application must verify the tokens sent by OAuth applications using the JWKS for your environment. User information in the token can be used to look up related resources and perform further access control checks.



In addition to fast stateless verification, you can use the [Token Introspection API](/reference/workos-connect/introspection) to synchronously check whether a token is still valid.

## Organization Access

When a user who is a member of multiple Organizations authorizes an OAuth Application, they will be prompted to select one of their Organizations to grant access to. Or, if the user only has a single Organization membership, that Organization will be automatically selected.

The selected Organization will be made available as the `org_id` claim in the issued [access token](/reference/workos-connect/token/authorization-code-grant/access-token), which your app can use to scope the access of requests made using the token.

## Configuration

OAuth applications require the following configuration:

### Redirect URI

This is the final location users will be redirected to after successful authentication. Clients should use the [Token Endpoint](/reference/workos-connect/token) to exchange the `code` for tokens at this location.

### Name and Logo

For third-party OAuth applications, the name and logo will be displayed to your users when they are prompted to authorize access. Both light and dark-mode logos are supported.

### Credentials

OAuth applications use the `client_id` and `client_secret` from a credential to authenticate to the [OAuth-based Connect APIs](/reference/workos-connect).

## Next Steps

- [Connect API Reference](/reference/workos-connect) - Complete API documentation

### M2M Applications

Implement machine-to-machine authentication with WorkOS Connect.

## Overview

Machine-to-machine (M2M) applications are designed for use-cases where clients are other services, such as one of your customer's applications. M2M applications use the underlying `client_credentials` flow for authentication.

M2M access tokens will contain an `org_id` claim which represents the third-party you are granting access to via the M2M application.

> M2M applications can only be configured as third-party.

M2M applications are one of two ways WorkOS enables you to issue credentials to your customers that they use to programmatically access your application. The other is [API keys](/authkit/api-keys). The [API Keys vs M2M Applications guide](https://workos.com/blog/api-keys-vs-m2m-applications) can help you decide which is best for your use case.

## Common Use Cases

M2M applications are commonly used to provide API access credentials to customers or partners, allowing them to programmatically access your APIs and integrate your services into their applications. They're also ideal for partner integrations that run server-to-server without user interaction, where you need to track and control access on a per-organization basis.

## Receiving Tokens

Machine-to-machine applications can use the `client_credentials` grant type with the [Token Endpoint](/reference/workos-connect/token) to obtain an `access_token` to authenticate calls to your API.



In addition to fast stateless verification, you can use the [Token Introspection API](/reference/workos-connect/introspection) to synchronously check whether a token is still valid.

## Configuration

M2M applications require the following configuration:

### Credentials

M2M applications use the `client_id` and `client_secret` from a credential to authenticate to the [Connect APIs](/reference/workos-connect) using the client credentials flow.

### Name and Description

While not displayed to users (since M2M apps don't have user interaction), the name and description help you manage and identify different M2M applications in your dashboard.

## Next Steps

- [Connect API Reference](/reference/workos-connect) - Complete API documentation

### Stripe

Connect your WorkOS account to Stripe to automatically provision access tokens with entitlements and sync organization seat counts.

## Introduction

WorkOS provides two powerful Stripe integrations that help you manage billing and access control for your B2B application:

- **Stripe Entitlements**: Automatically provision access tokens with subscription-based entitlements from Stripe
- **Stripe Seat Sync**: Automatically sync organization member counts to Stripe billing meters for usage-based billing

Both features use [Stripe Connect](https://stripe.com/connect) to connect your WorkOS account to your Stripe account, allowing WorkOS to manage these integrations on your behalf.

---

## Connect to Stripe

To use either Stripe Entitlements or Stripe Seat Sync, you'll first need to connect your WorkOS account to Stripe using Stripe Connect.

Navigate to the _Authentication_ section of the [WorkOS Dashboard](https://dashboard.workos.com/) and click _Add-ons_.

From that page, find the Stripe Add-on and click _Enable_.

![Authentication Add-ons page](https://images.workoscdn.com/images/75c987b7-d946-4e37-b46d-043fa994cfb4.png?auto=format&fit=clip&q=80)

This will open a Dialog to pick the Stripe features you would like to use. When clicking _Continue_ you'll be directed to Stripe where you can approve the connection. Once that's complete, close the tab.

> WorkOS does not currently support connecting to a Stripe Sandbox account. Connect WorkOS to a standard Stripe account, and use Stripe’s test mode for development and testing your integration.

![Stripe Dialog](https://images.workoscdn.com/images/5953bd10-b966-4b1f-8d9f-7cd490bc19d3.png?auto=format&fit=clip&q=80)

In the connection dialog, you can choose to enable one or both features:

- **Use Stripe entitlements**: Include entitlement data in access tokens
- **Sync organization seat counts**: Send member counts to Stripe billing meters

If you decide to disconnect your Stripe account later or toggle features on and off, you can do so from the same section. Click the _Manage_ button to disable features or disconnect entirely.

---

## Set Stripe Customer IDs

To use either of these features, you'll need to set the Stripe customer ID on each WorkOS organization.

Once you have a WorkOS organization ID and a Stripe customer ID, you can set the Stripe customer ID for the organization. One way to handle this is to create a Stripe customer and then set the created Stripe customer ID on the WorkOS organization. This can be done via the WorkOS API or SDK. Here's an example using the SDK:



---

## Stripe Entitlements

Entitlements are a way to provision an account in your application with specific features or functionality based on the subscription plan a user is on. For example, you might have an "Enterprise" plan that allows users to access certain features like [Audit Logs](/audit-logs), and a "Basic" plan that does not.

The WorkOS Entitlements integration makes it easy to get Stripe's entitlement information into your application without needing to listen to Stripe webhooks or explicitly track them in your application.

### Use the entitlements in your application

The access token will now include the `entitlements` claim, containing the user's entitlements. You can use this information to gate access to features in your application.

> Entitlements added mid-cycle will appear in the next Stripe billing cycle or when a new subscription is created, per Stripe’s billing logic.

Entitlements will show up in the access token the next time the user logs in or the session is refreshed. You can manually [refresh the session](reference/authkit/authentication/refresh-token) after the user has completed their subscription purchase. Here's an example in Express:



---

## Stripe Seat Sync

Stripe Seat Sync automatically sends active organization member counts to Stripe as billing meter events under [Usage-based billing](https://docs.stripe.com/billing/subscriptions/usage-based), enabling usage-based billing based on the number of seats (members) in each organization.

### How it works

When Stripe Seat Sync is enabled:

- WorkOS creates a billing meter in your Stripe account called **"User Seat Count"** with the event name `workos_seat_count`
- Whenever a member is added, removed, deactivated, or activated from an organization, WorkOS automatically sends a meter event to Stripe with the updated seat count
- You can use this meter in Stripe to create usage-based pricing based on the number of active seats

The meter uses Stripe's ["last" aggregation method](https://docs.stripe.com/billing/subscriptions/usage-based/meters/configure), which means Stripe will bill based on the most recent seat count reported during each billing period.

### Using the seat count meter in Stripe

Once enabled, WorkOS will automatically send meter events to Stripe whenever organization memberships change. You can:

- View the meter events in your Stripe Dashboard under **Billing → Meters**
- Create pricing models that bill based on the `workos_seat_count` meter
- Use the meter in subscription items to charge customers based on their current seat count

The meter event includes:

- **Event name**: `workos_seat_count`
- **Customer ID**: The Stripe customer ID associated with the organization
- **Value**: The current number of active members in the organization

No additional code is required in your application—WorkOS handles all meter event reporting automatically as members join or leave organizations.

### Segment

Send AuthKit events to your Segment destinations.

## Introduction

The Segment AuthKit Add-on allows you to register AuthKit as a Segment source and receive events about logins, sign ups, and many other AuthKit activities. You can forward that data to your Segment destinations, allowing you to better understand the effectiveness of your marketing campaigns and your users' full journeys across your website and AuthKit.

---

## Configuring the Add-on

### (1) Confirm your domain

To use the Add-on, your [Authentication API Domain](./custom-domains/auth-api) must share the same root as the domain where you set up Segment through Analytics.js. This gives the Add-on access to your users' anonymous IDs, which the Add-on uses to identify users.

For example, if your Segment Analytics.js script lives at www.example.com:

- **Valid:** auth.example.com is a valid Authentication API Domain
- **Invalid:** auth.workos.com is not a valid Authentication API Domain

### (2) Visit the Add-ons page

In the WorkOS Dashboard, click the _Authentication_ icon in the sidebar. Then click _Add-ons_.

![Add-ons page in the Authentication section of the WorkOS Dashboard](https://images.workoscdn.com/images/d5b867bb-8f04-440b-8443-4f9a1026a0cb.png?auto=format&fit=clip&q=50)

### (3) Enable the Add-on

Click _Enable_ next to _Segment._

![Segment modal containing a Write Key field in the WorkOS Dashboard](https://images.workoscdn.com/images/18d3da6b-0b59-4973-8a71-851e1ad4e5dc.png?auto=format&fit=clip&q=50)

In another browser tab, visit Segment to get your credentials. Click _Connections_ in the left sidebar.

![Connections link in Segment](https://images.workoscdn.com/images/78899ff2-be15-4fc0-a9d4-1482af04703e.png?auto=format&fit=clip&q=50)

Next to _Sources_, click _Add more_.

![Add more link on the Connections page in Segment](https://images.workoscdn.com/images/5feab1ce-89d3-4998-b16e-9ffdb48b4b0f.png?auto=format&fit=clip&q=50)

Under _Choose a Source_, search "HTTP API." Below, click _HTTP API_. Then, in the lower right corner, click _Next._

![HTTP API source in Segment](https://images.workoscdn.com/images/c6be5d31-307f-4371-82de-9486a034f478.png?auto=format&fit=clip&q=50)

Under _Create your source_, give your source a name, like _WorkOS AuthKit Add-on_. Click _Create Source_. Under _Configure this source in your HTTP API codebase_, find your _Write Key_, and click the _Copy_ button next to it.

![Copy button next to a new Write Key for an HTTP API source in Segment](https://images.workoscdn.com/images/82093be1-5a27-4985-bdef-ce3899832ee6.png?auto=format&fit=clip&q=50)

Paste your write key in the _Write Key_ field in the WorkOS Dashboard. Click _Save changes_. The Segment AuthKit Add-on is enabled and will begin sending AuthKit events to Segment.

---

## Events sent to Segment

The Add-on sends events to Segment when certain [WorkOS Events](/events) occur:

- _Authentication_ events
- _Email verification_ events
- _Magic Auth_ events
- _Password reset_ events
- `session.created`
- `user.created`

All names of events in Segment will match the names of the [WorkOS Events](/events).

---

## Verifying events

After enabling the Add-on, visit your website, click your sign in button, and sign in to your application. Visit Segment and click _Connections_ in the sidebar. Click the source you created. Then click the _Debugger_ tab. You should see an identify call including your anonymous ID and a track call with an authentication event.

If you do not see an authentication event, visit the Add-ons page in the WorkOS Dashboard to verify your Write Key matches the value from Segment. If after confirming the values match you still need support, please reach out to us in Slack.

### Google Analytics

Track user activity on AuthKit pages in Google Analytics.

## Introduction

The Google Analytics AuthKit Add-on gives you data about logins, sign ups, and many other AuthKit activities inside of Google Analytics. You can use that data to better understand the effectiveness of your marketing campaigns and your users' full journeys across your website and AuthKit.

---

## Configuring the Add-on

### (1) Confirm your domain

To use the Add-on, your [Authentication API Domain](./custom-domains/auth-api) must share the same root as the domain where you set up Google Analytics through Google Tag Manager or gtag.js. This gives the Add-on access to your users' Google Analytics client IDs, which the Add-on uses to associate events in AuthKit with users from your website.

For example, if your Google Analytics script lives at www.example.com:

- **Valid:** auth.example.com is a valid Authentication API Domain
- **Invalid:** auth.workos.com is not a valid Authentication API Domain

### (2) Visit the Add-ons page

In the WorkOS Dashboard, click the _Authentication_ icon in the sidebar. Then click _Add-ons_.

![Add-ons page in the Authentication section of the WorkOS Dashboard](https://images.workoscdn.com/images/d5b867bb-8f04-440b-8443-4f9a1026a0cb.png?auto=format&fit=clip&q=50)

### (3) Enable the Add-on

Click _Enable_ next to _Google Analytics._

![Google Analytics modal with Measurement ID and Measurement Protocol API Secret fields in the WorkOS Dashboard](https://images.workoscdn.com/images/40f61f05-32d2-4439-ae68-3c3b8f8aaf87.png?auto=format&fit=clip&q=50)

In another browser tab, visit Google Analytics to get your credentials. Click the _Admin_ icon in the bottom left corner.

![Admin icon on the Google Analytics website](https://images.workoscdn.com/images/21267275-4fde-431b-95d6-8e1ec3cc8190.png?auto=format&fit=clip&q=50)

Under _Data collection and modification_, click _Data streams._

![Data steams link on the Google Analytics website](https://images.workoscdn.com/images/8f967c62-d63f-4130-8510-dc97a249d0b4.png?auto=format&fit=clip&q=50)

Click the data stream that you used to set up Google Analytics on your website. Under _Measurement ID_, click the copy icon. Paste your Measurement ID in the Measurement ID field in the WorkOS Dashboard.

![Measurement ID on the Google Analytics website](https://images.workoscdn.com/images/68dbf576-dae1-4940-b3d4-6840086f94b5.png?auto=format&fit=clip&q=50)

Under _Events_, click _Measurement Protocol API secrets_.

![Measurement Protocol API Secrets section on the Google Analytics website](https://images.workoscdn.com/images/cdf958e3-6714-4700-9c77-71e46af96bc5.png?auto=format&fit=clip&q=50)

Click _Create_. Give your new secret a name, like _WorkOS AuthKit_ _Add-on_. Copy the _Secret value._ The secret value may be cut off on narrower windows, so try double clicking the value before copying it to ensure you have selected the full value.

![Measurement Protocol API Secret value on the Google Analytics website](https://images.workoscdn.com/images/8bd84bdf-eb85-4987-97a2-c13c863508a3.png?auto=format&fit=clip&q=50)

Paste the secret value in the API Secret field in the WorkOS Dashboard. Click _Save changes_. The Google Analytics AuthKit Add-on is enabled and will begin sending AuthKit events to Google Analytics.

![Google Analytics AuthKit Add-on enabled in the WorkOS Dashboard](https://images.workoscdn.com/images/0e47d2b4-29ad-4a47-9ab6-470dee7cc63c.png?auto=format&fit=clip&q=50)

---

## Events sent to Google Analytics

The Add-on sends events to Google Analytics when certain [WorkOS Events](/events) occur:

- `user.created` sends Google a [`sign_up` recommended event](https://developers.google.com/analytics/devguides/collection/ga4/reference/events?client_type=gtag#sign_up)
- `authentication.magic_auth_succeeded`, `authentication.mfa_succeeded`, `authentication.oauth_succeeded`, `authentication.passkey_succeeded`, `authentication.password_succeeded`, and `authentication.sso_succeeded` send Google a [`login` recommended event](https://developers.google.com/analytics/devguides/collection/ga4/reference/events?client_type=gtag#login) including a `method` parameter matching the login method
- The remaining _Authentication_ events, _Email verification_ events, _Magic Auth_ events, _Password reset_ events, and `session.created` send Google events based on the WorkOS event name, with underscores replacing periods
- `authentication.email_verification_succeeded` is shortened to `auth_email_verification_succeeded` to meet Google's requirement that event names be 40 characters or shorter

---

## Verifying events

After enabling the Add-on, visit your website, click your sign in button, and sign in to your application. Visit Google Analytics and click _Reports_ in the sidebar. Then click _Realtime overview_. Within five minutes, you should see a `login` event under _Event count by Event name_.

If you do not see a login event, visit the Add-ons page in the WorkOS Dashboard to verify your Measurement ID and API Secret match the values from Google Analytics. If after confirming the values match you still need support, please reach out to us in Slack.


## Audit Logs {#audit-logs}

### Metadata Schema

Define strict JSON Schema for validating event metadata.

## Metadata Schema

Audit Log Events can contain arbitrary metadata for adding additional details to your events. Normally this data can take any shape. However, custom metadata schemas can be defined when configuring the event for additional type safety and data consistency. When an event is emitted that does not match the provided schema, an error will be returned.

When first creating an event schema, check the “Require metadata schema validation” checkbox. You will then be navigated to the schema editor where you can modify the underlying [JSON Schema](https://json-schema.org/) for all `metadata` objects.

![A screenshot showing how to require metadata schema validation in the WorkOS Dashboard.](https://images.workoscdn.com/images/24a410e1-72aa-4f5b-8854-98a4307602ff.png?auto=format&fit=clip&q=50)

There are `metadata` objects located at the root of the event, and within `actor` and `targets` objects. Each can contain a unique JSON Schema. To add to a `metadata` object, click the "+" sign.

> Metadata objects have a limit of 50 keys. Key names can be up to 40 characters long, and values can be up to 500 characters long.

![A screenshot showing the schema editor in the WorkOS Dashboard.](https://images.workoscdn.com/images/7d9e37a3-2e8d-4910-b85d-34c224e375be.png?auto=format&fit=clip&q=50)

### Log Streams

Stream Audit Log Events to your customers’ SIEM providers.

## Understanding Log Streams

Log Streams allow your customers to stream Audit Logs directly to their Security Incident and Event Management (SIEM) providers like Datadog or Splunk and object storage solutions like AWS S3 or Google Cloud Storage. There is also a generic provider (HTTP POST) available to stream logs to any configured endpoint.

This gives your customers greater control over their Audit Logs by allowing them to apply custom indexing and monitoring of their events in the SIEM provider along with events from other cloud services they use.

Log Streams can be created by either configuring the Log Stream through your WorkOS Dashboard or by allowing your customer's IT admin to configure it themselves through the WorkOS Admin Portal.

### IP allowlist

WorkOS streams audit logs from a fixed set of IP addresses. If audit logs are being streamed to a host that restricts access based on IP address, the following IP addresses should be allowed:

```plain title="WorkOS IP addresses"
3.217.146.166
23.21.184.92
34.204.154.149
44.213.245.178
44.215.236.82
50.16.203.9
52.1.251.34
52.21.49.187
174.129.36.47
```

## Dashboard

To configure a Log Stream through the WorkOS Dashboard, navigate to an organization and click “Configure”.

![A screenshot showing where to find "Configure" in the WorkOS Dashboard.](https://images.workoscdn.com/images/b555ad16-fce2-4014-997d-3d75b85f7860.png?auto=format&fit=clip&q=50)

You will be prompted to select a destination from a dropdown, click “Save connection”. You will then be prompted to provide specific configuration for the selected destination.

![A screenshot showing "Save connection" in the WorkOS Dashboard.](https://images.workoscdn.com/images/75ced694-5dbd-48c3-9784-5fdaf81e0420.png?auto=format&fit=clip&q=50)

## Admin Portal

The Admin Portal can be accessed via a Setup Link found in the Organization page within the Dashboard. Click “Generate” and select “Log Streams”. Copy the link and send it to the organization's IT admin who will be configuring Log Streams.

![A screenshot showing where the "Generate" button is located in the WorkOS Dashboard.](https://images.workoscdn.com/images/f6410460-40e4-478c-b663-5920ac15b8a8.png?auto=format&fit=clip&q=50)

You can also guide users to the Admin Portal by redirecting them to a programmatically generated Admin Portal link directly from your application.



Once redirected to the Admin Portal, the user will be prompted to select a destination and will be provided with step-by-step configuration instructions for the selected destination.

![A screenshot showing log stream destination options in the WorkOS Admin Portal.](https://images.workoscdn.com/images/a6249873-d221-49eb-9c6a-c7706b2b4f77.png?auto=format&fit=clip&q=50)

## Streaming Destinations and Payload Formats

WorkOS supports streaming audit log events to five different types of destinations, each with its own payload format and configuration requirements:

### Datadog

Events are sent to Datadog's HTTP Log Intake API with regional endpoint support.

**Example Payload:**

```json
[
  {
    "message": {
      "id": "01HY123456ABCDEFGHIJK",
      "action": "user.signed_in",
      "targets": [
        {
          "id": "user_123",
          "type": "user"
        }
      ],
      "actor": {
        "id": "user_456",
        "type": "user"
      },
      "context": {
        "location": "192.168.1.1",
        "user_agent": "Chrome/91.0"
      },
      "occurred_at": "2024-01-15T10:30:00.000Z"
    },
    "ddsource": "team-name",
    "service": "audit-logs"
  }
]
```

**Configuration:**

- API Key authentication
- Regional endpoints (US1, US3, US5, EU1, US1-FED, AP1)
- Optional team name as source identifier

### Splunk

Events are sent to Splunk's HTTP Event Collector (HEC) endpoint.

**Example Payload:**

```json
[
  {
    "event": {
      "id": "01HY123456ABCDEFGHIJK",
      "action": "user.signed_in",
      "targets": [
        {
          "id": "user_123",
          "type": "user"
        }
      ],
      "actor": {
        "id": "user_456",
        "type": "user"
      },
      "context": {
        "location": "192.168.1.1",
        "user_agent": "Chrome/91.0"
      },
      "occurred_at": "2024-01-15T10:30:00.000Z"
    },
    "time": 1705314600000,
    "source": "team-name"
  }
]
```

**Configuration:**

- HEC Token authentication
- Custom Splunk instance URL
- Optional source identifier

### AWS S3

Events are stored as individual JSON files in an S3 bucket. We use a cross-account IAM role with an external ID
([details](https://docs.aws.amazon.com/IAM/latest/UserGuide/tutorial_cross-account-with-roles.html))
to authenticate to the destination bucket. We upload S3 objects with a `ContentMD5` header to support
[uploading objects to Object Lock enabled buckets](https://docs.aws.amazon.com/AmazonS3/latest/userguide/object-lock-managing.html#object-lock-put-object).

| Property            | Description                                                    |
| ------------------- | -------------------------------------------------------------- |
| File Format         | Individual JSON files per event with pretty-printed formatting |
| File Naming Pattern | `YYYY-MM-DD/{timestamp}_{keySuffix}.json`                      |
| Example Filename    | `2024-01-15/2024-01-15T10:30:00.123Z_abc123def456.json`        |

**Example File Content:**

```json
{
  "id": "01HY123456ABCDEFGHIJK",
  "action": "user.signed_in",
  "targets": [
    {
      "id": "user_123",
      "type": "user"
    }
  ],
  "actor": {
    "id": "user_456",
    "type": "user"
  },
  "context": {
    "location": "192.168.1.1",
    "user_agent": "Chrome/91.0"
  },
  "occurred_at": "2024-01-15T10:30:00.000Z"
}
```

**Configuration:**

WorkOS authenticates to the destination S3 bucket using an AWS cross-account IAM role delegation with an external ID for enhanced security. This requires the following configuration:

| Field Name     | Code         | Description                                                              |
| -------------- | ------------ | ------------------------------------------------------------------------ |
| AWS Account ID | `accountId`  | Destination AWS account ID where the S3 bucket is located                |
| AWS Region     | `region`     | The AWS region for the destination S3 bucket (defaults to `us-east-1`)   |
| IAM Role Name  | `roleName`   | The name of the IAM role WorkOS will assume to access destination bucket |
| S3 Bucket Name | `bucketName` | The name of the destination S3 bucket                                    |
| Bucket Path    | `bucketPath` | Optional path prefix within the bucket where logs will be stored         |

**Authentication Flow:**

1. WorkOS uses AWS Security Token Service (STS) to assume a role in the destination AWS account
2. The role must be configured to trust WorkOS' AWS account ID (`workosAccountId`) as an external trusted entity
3. The role must require an External ID (`externalId`) that matches the unique value provided by WorkOS
4. The role must have an attached IAM policy granting `s3:PutObject` permissions on the bucket (and optional path prefix)
5. WorkOS receives temporary credentials from STS and uses them to upload audit log events to the destination S3 bucket

**IAM Policy Requirements:**

The IAM role must include a policy that allows `s3:PutObject` actions on the destination bucket. The policy resource should target destination bucket and optional path prefix: `arn:aws:s3:::bucket-name/optional-path/*`.

Example policy that you need to create in the destination AWS account:

```json
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "LogStreamBucketPolicy",
      "Effect": "Allow",
      "Action": ["s3:PutObject"],
      "Resource": ["arn:aws:s3:::bucket-name/optional-path/*"]
    }
  ]
}
```

### Google Cloud Storage

Events are stored as individual JSON files using Google Cloud Storage's S3-compatible API.

| Property            | Description                                         |
| ------------------- | --------------------------------------------------- |
| File Format         | Individual JSON files per event (same format as S3) |
| File Naming Pattern | `{timestamp}_{keySuffix}.json`                      |

**Example File Content:**

```json
{
  "id": "01HY123456ABCDEFGHIJK",
  "action": "user.signed_in",
  "targets": [
    {
      "id": "user_123",
      "type": "user"
    }
  ],
  "actor": {
    "id": "user_456",
    "type": "user"
  },
  "context": {
    "location": "192.168.1.1",
    "user_agent": "Chrome/91.0"
  },
  "occurred_at": "2024-01-15T10:30:00.000Z"
}
```

**Configuration:**

- Access Key ID and Secret Access Key are required when configuring a log stream to GCS
- GCS bucket with S3-compatible access

### Generic HTTPS

Events are sent to custom HTTP endpoints with configurable authentication and format options.

**JSON Format Example:**

```json
[
  {
    "event": {
      "id": "01HY123456ABCDEFGHIJK",
      "action": "user.signed_in",
      "targets": [
        {
          "id": "user_123",
          "type": "user"
        }
      ],
      "actor": {
        "id": "user_456",
        "type": "user"
      },
      "context": {
        "location": "192.168.1.1",
        "user_agent": "Chrome/91.0"
      },
      "occurred_at": "2024-01-15T10:30:00.000Z"
    },
    "keySuffix": "abc123def456",
    "timestamp": "2024-01-15T10:30:00.123Z",
    "source": "team-name"
  }
]
```

**NDJSON Format Example:**

```json
{"event":{"id":"01HY123456ABCDEFGHIJK","action":"user.signed_in",...},"keySuffix":"abc123def456","timestamp":"2024-01-15T10:30:00.123Z"}
```

**Configuration:**

- Custom HTTP endpoint
- Configurable authentication headers
- Support for JSON or NDJSON formats
- Content-Type handling (application/json or application/x-ndjson)

## Stream States and Management

Audit log streams can be in one of four states that determine their operational status:

### Stream States

| State        | Description                                               |
| ------------ | --------------------------------------------------------- |
| **Active**   | Stream is functioning normally and delivering events      |
| **Inactive** | Stream is incomplete, manually disabled or paused         |
| **Error**    | Stream encountered a retry-able error and will be retried |
| **Invalid**  | Stream has invalid credentials or configuration           |

### State Transitions

Streams automatically transition between states based on delivery outcomes:

- **Active → Error**: When a retry-able error occurs during event delivery
- **Active → Invalid**: When authentication or authorization fails
- **Error → Active**: When retry succeeds after a previous error
- **Invalid → Active**: When credentials are fixed and validation succeeds
- **Any → Inactive**: When manually disabled through Dashboard or Admin Portal

### Updating Stream Configuration

Stream configurations can be updated through:

1. **WorkOS Dashboard**: Navigate to the organization and modify the log stream configuration
2. **Admin Portal**: Generate a setup link for the organization's IT admin to update settings

### Audit Logs

Ingest and export Audit Log Events from your application.

## Introduction

Audit Logs are a collection of events that contain information relevant to notable actions taken by users in your application. Every event in the collection contains details regarding what kind of action was taken (`action`), who performed the action (`actor`), what resources were affected by the action (`targets`), and additional details of when and where the action took place.

```json
{
  "action": "user.signed_in",
  "occurred_at": "2022-08-29T19:47:52.336Z",
  "actor": {
    "type": "user",
    "id": "user_01GBNJC3MX9ZZJW1FSTF4C5938"
  },
  "targets": [
    {
      "type": "team",
      "id": "team_01GBNJD4MKHVKJGEWK42JNMBGS"
    }
  ],
  "context": {
    "location": "123.123.123.123",
    "user_agent": "Chrome/104.0.0.0"
  }
}
```

These events are similar to application logs and analytic events, but are fundamentally different in their intent. They aren’t typically used for active monitoring/alerting, rather they exist as a paper trail of potentially sensitive actions taken by members of an organization for compliance and security reasons.

## What you’ll build

This guide will show you how to:

1. Configure and emit Audit Log Events
2. Export Audit Log Events
3. Create custom metadata schemas for Audit Log Events
4. Create new versions of Audit Log Event schemas

## Before getting started

To get the most out of this guide, you’ll need:

- A [WorkOS account](https://dashboard.workos.com/)

## API object definitions

[Audit Log Event](/reference/audit-logs/create-event)
: An individual event that represents an action taken by an actor within your app.

[Audit Log Export](/reference/audit-logs/audit-log-export)
: A collection of Audit Log Events that are exported from WorkOS as a CSV file.

[Organization](/reference/organization)
: Describes a customer where Audit Log Events originate from.

## Emit an Audit Log Event

### Install the WorkOS SDK

WorkOS offers native SDKs in several popular programming languages. Choose a language below to see instructions in your application’s language.



### Set secrets

To make calls to WorkOS, provide the API key and, in some cases, the client ID. Store these values as managed secrets, such as `WORKOS_API_KEY` and `WORKOS_CLIENT_ID`, and pass them to the SDKs either as environment variables or directly in your app's configuration based on your preferences.

```plain title="Environment variables"
WORKOS_API_KEY='sk_example_123456789'
WORKOS_CLIENT_ID='client_123456789'
```

### Sign in to your WorkOS Dashboard account and configure Audit Log Event schemas

Before you can emit any Audit Log Events you must configure the allowed event schemas. To start, click “Create an event” and enter `user.signed_in` for action, `team` for targets, and click “Save event”.

![A screenshot showing how to create an audit log event in the WorkOS dashboard.](https://images.workoscdn.com/images/7658a3b2-1467-4c38-a98f-f99f933c5969.png?auto=format&fit=clip&q=50)

### Get an Organization ID

All events are scoped to an Organization, so you will need the ID of an Organization in order to emit events.

![A screenshot showing where to find an Organization ID in the WorkOS dashboard.](https://images.workoscdn.com/images/b76c7593-1d85-4f28-951e-24f177b8c233.png?auto=format&fit=clip&q=50)

### Emit Events

Using the ID from the Organization, emit an Audit Log Event with the `action` and `targets` previously configured.



#### Idempotency

WorkOS Audit Logs supports idempotency to ensure events are not duplicated when retrying requests. You can provide an `idempotency-key` header with your event creation request. If you don't provide one, WorkOS will automatically generate one based on the event content.

When you provide an idempotency key:

- WorkOS creates a hashed key combining your provided key with the event data
- Subsequent requests with the same idempotency key and event data will return the same response
- This prevents duplicate events from being created due to network retries or other issues

When you don't provide an idempotency key:

- WorkOS automatically generates one using the event content
- This provides basic duplicate protection based on event data alone

### View ingested events in the Dashboard

Once you have successfully emitted events with the WorkOS SDK, you can view them in the Dashboard under the Organization that the events are associated with.

![A screenshot showing Audit Log events for an organization in the WorkOS dashboard.](https://images.workoscdn.com/images/b03dfaa4-c76a-4d08-a322-53458ba8b24d.png?auto=format&fit=clip&q=50)

### Exporting Events

Export Audit Log Events through the WorkOS Dashboard and API.

## Exporting Events

You may need to export Audit Log Events in large chunks. WorkOS supports exporting events as CSV files through both the Dashboard and API.

Exports are scoped to a single organization within a specified date range. Events from the past three months can be included in the export. You may define additional filters such as `actions`, `actors`, and `targets`.

### Creating an export through the Dashboard

Exports can be manually created under the Organization page when viewing Audit Log Events by selecting "Export CSV" from the "Actions" dropdown. Set your filters and select "Generate CSV file".

![A screenshot showing how to generate an Audit Log export in the WorkOS Dashboard.](https://images.workoscdn.com/images/a5386939-652f-4cbb-aa88-7159e2ffc1dd.png?auto=format&fit=clip&q=50)

### Creating an export through the API



Once the export has been created, fetch the export at a later time to access the `url` of the generated CSV file.

> The URL will expire after 10 minutes. If the export is needed again at a later time, refetching the export will regenerate the URL.



If the `state` of the export is still `pending`, poll the export until it is ready for download.

### Editing Events

Modify existing event configuration with backwards compatibility.

## Editing Events

Once you’ve successfully configured Audit Logs in the WorkOS Dashboard and begun emitting events, how do you go about modifying an event schema without breaking your existing integrations? This is where versioning comes into place. When you make a modification to an existing schema it will create a new version rather than overwriting the existing schema.

The reason for this behavior is to ensure backwards compatibility. Schema configuration is immutable to prevent you from accidentally making changes that are incompatible with events that are already being emitted from your application. Rather you must first create a new version of the schema, and then explicitly emit events for that version leveraging the event `version` field.

### Creating a new event version

In the WorkOS Dashboard navigate to the Audit Logs configuration page. Locate the event that you would like to modify the schema for and click the “Edit Event” item under the context menu.

![A screenshot showing the "Edit Event" option in the WorkOS Dashboard.](https://images.workoscdn.com/images/8ee56828-fc59-4c18-ae64-008a754cd2a6.png?auto=format&fit=clip&q=50)

You will be navigated to a page where you can edit both the `targets` associated with the event, and optionally the metadata JSON schema. Once you’re done making changes, clicking save will create a new version of the event schema.

![A screenshot showing the "Save as new version" button in the schema editor in the WorkOS Dashboard.](https://images.workoscdn.com/images/65d234a3-f530-4051-95a0-0162cfef122e.png?auto=format&fit=clip&q=50)

### Emitting event with version

Now that a schema exists with a new version, the `version` field must be provided when emitting an event so that WorkOS knows which version to use for validation.

### Admin Portal

View Audit Log events for an organization in the WorkOS Admin Portal.

## Creating Admin Portal Link

Audit Log events can be viewed in the WorkOS [Admin Portal](/admin-portal). Links can be generated through the WorkOS API and sent to your customers for viewing events associated with their Organization.

When creating a link for an Admin Portal session, you must provide the Organization ID whose events will be displayed in the Admin Portal, and specify the intent as `audit_logs`.



Navigating to the provided link will result in the following view. Users will be able to view and export Audit Log events just as can be done through the WorkOS Dashboard.

![A screenshot showing Audit Log events in the WorkOS Admin Portal.](https://images.workoscdn.com/images/e08a07dd-4539-4c5a-9802-63d7c774b2c9.png?auto=format&fit=clip&q=50)


## Admin Portal {#admin-portal}

### Admin Portal

A first-class Single Sign-On and Directory Sync onboarding experience for organization admins.

## Introduction

The Admin Portal provides an out-of-the-box UI for IT admins to verify domains, configure SSO and Directory Sync connections, and more. Designed to remove friction, custom walk-through documentation for each identity provider means that organization admins can onboard their organizations without high-touch support from your team. Easy to integrate and fully maintained and hosted by WorkOS, the Admin Portal makes Domain Verification, SSO, and Directory Sync setup simple, fast, and secure.

![A screenshot showing the IdP selection in the WorkOS Admin Portal.](https://images.workoscdn.com/images/dd00d92d-2810-484a-a3c7-4e0fdb8703a7.png?auto=format&fit=clip&q=50)

## Workflow Options

There are two main workflows for initiating an Admin Portal session for IT admins. You can either share a link to the Admin Portal from the WorkOS dashboard, or you can seamlessly integrate Admin Portal into your application through WorkOS SDKs or APIs.

![A screenshot showing the different workflows for creating an Admin Portal shareable link.](https://images.workoscdn.com/images/33851982-5baf-4ffe-8b41-71054b95948b.png?auto=format&fit=clip&q=50)

If you want to provide an IT admin with a link to the Admin Portal, in a email for example, then you would need to create that link in the WorkOS dashboard.

However, if you are adding a button to open the Admin Portal from within your application, then you would need to use the API.

| Workflow                        | Use cases                    | Security                                                                         | Return URL and Success URLs                                                                                                                |
| ------------------------------- | :--------------------------- | :------------------------------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------- |
| Share a link from the dashboard | Setup only                   | Can be revoked; Automatically revoked on setup completion; Expires after 30 days | Not applicable                                                                                                                             |
| Generate a link via the API     | Setup and post-configuration | Cannot be revoked; Expires after 5 minutes                                       | Can be configured on the [Redirects](https://dashboard.workos.com/redirects) page in the dashboard or specified as a parameter for the API |

---

## Before getting started

To get the most out of these guides, you’ll need:

- A [WorkOS account](https://dashboard.workos.com/)

## API object definitions

[Connection](/reference/sso/connection)
: Represents the method by which users of an organization sign in to your application.

[Organization](/reference/organization)
: Describes an organization whose users sign in with an SSO connection, or whose users are synced with a Directory Sync connection.

[Portal link](/reference/admin-portal/portal-link)
: A temporary link to initiate an Admin Portal session.

## (A) Setup link from WorkOS dashboard

The Admin Portal setup link gives your customer access to a guided configuration experience through our Admin Portal. It instructs them how to verify a domain, configure their identity or directory provider, and more. If successfully configured, no other action is required and you’ll see verified domains and/or active connections appear under the organization.

First decide whether your customer will be configuring Domain Verification, Single Sign-On, Directory Sync, Log Streams, or all of the above. Once you generate a link, the customer will have access for 30 days or until configured.

You’ll need a [WorkOS dashboard account](https://dashboard.workos.com/) to create an organization that will represent the enterprise you are onboarding.

### Create organization

Sign in to your WorkOS dashboard account and create a new organization.

![WorkOS dashboard UI showing organization creation](https://images.workoscdn.com/images/1c69fd98-01be-491d-9255-58363bc6a983.png?auto=format&fit=clip&q=50)

### Generate a setup link

Click the “Invite admin” button, select the features to include and then click “Next." Enter the email of the IT admin for the organization to automatically send them a setup link, or click "Copy setup link." Only one link can be active at a time. After creating the initial link, you can click the “Manage” button to revoke the existing link before creating a new one.

### Sharing a setup link

If you chose to copy the setup link you can share it over email, Slack or direct message. We also recommend including details on what the link does and how long the link is active.

## (B) Integrate with your app

In this guide, we’ll walk you through the full end-to-end integration of the Admin Portal into your application.

> [Sign in](https://dashboard.workos.com/) to your WorkOS dashboard account to see code examples pre-filled with your test API keys and resource IDs.

### Configure Admin Portal redirect links

In order to integrate, you must configure your app's default return URI in the production environment. A button in the Admin Portal will use this value to allow users to return to your app unless otherwise specified when generating the Admin Portal link.

![A screenshot showing the Admin Portal Redirect Links tab to set redirect URIs in the WorkOS dashboard.](https://images.workoscdn.com/images/9df0f214-0350-466c-b54b-8a1e0be6b678.png?auto=format&fit=clip&q=50)

Additionally, you can configure success URIs to redirect users upon successfully setting up Single Sign-On, Directory Sync, or Log Streams.

![A screenshot showing the Admin Portal redirect URI variations in the WorkOS dashboard.](https://images.workoscdn.com/images/3d75975c-b36a-4bfc-b05d-d745b8ba916b.png?auto=format&fit=clip&q=50)

> All redirect links must use HTTPS.

You can configure these links in the [dashboard](https://dashboard.workos.com/).

### Install the WorkOS SDK

WorkOS offers native SDKs in several popular programming languages. Choose a language below to see instructions in your application’s language.



### Set secrets

To make calls to WorkOS, provide the API key and, in some cases, the client ID. Store these values as managed secrets, such as `WORKOS_API_KEY` and `WORKOS_CLIENT_ID`, and pass them to the SDKs either as environment variables or directly in your app's configuration based on your preferences.

```plain title="Environment variables"
WORKOS_API_KEY='sk_example_123456789'
WORKOS_CLIENT_ID='client_123456789'
```

### Create a new organization

Each Admin Portal session is scoped to a specific organization resource, meaning a session is only capable of managing a connection that belongs to its associated organization. Organizations may only have one connection.

For every customer in your application that would like access to the Admin Portal, you must create an organization and maintain a reference to its ID.

> Create an organization when onboarding a new customer.



### Redirect an IT admin to the Admin Portal

A Portal link is your enterprise user's gateway to accessing the Admin Portal, where they can set up and manage resources scoped to their organization. To generate a Portal link using the API, you must provide the organization ID and specify one of the following intents: [`sso`](admin-portal/c-using-admin-portal/managing-sso-connections), [`dsync`](admin-portal/c-using-admin-portal/managing-directories), [`audit_logs`](audit-logs/admin-portal/creating-admin-portal-link), [`log_streams`](audit-logs/log-streams/admin-portal), [`domain_verification`](domain-verification/admin-portal-domain-verification), or [`certificate_renewal`](sso/signing-certificates/saml-response-signing-certificate/renewing-certificates).

For security reasons, Portal links expire 5 minutes after they’re created, so we recommend redirecting users immediately (i.e. don’t email the user Portal links).

> The endpoint that redirects a user to the Admin Portal should be guarded by auth in your application and only available to IT admins.



An [optional return_url parameter](/reference/admin-portal/portal-link/generate) can be used to describe exactly where a user should be sent when they are finished in the Admin Portal. If one is not provided, the success URL configured on the [Redirects](https://dashboard.workos.com/redirects) page of the dashboard will be used.

## (C) Using Admin Portal

In this guide, we’ll review some of the features of Admin Portal from an IT admin's perspective.

### Verifying a domain

In the Admin Portal [Domain Verification](/domain-verification/admin-portal-domain-verification) flow, you can view instructions on adding a DNS TXT record to prove ownership of your organization's domain(s).

> Unless an organization [allows any domain](/sso/domains/allowing-any-domain), a verified domain is required in order to activate SSO. Domains can also be [manually verified](authkit/domain-verification/self-serve-domain-verification) outside of the self-serve Admin Portal flow if the IT admin has already proven domain ownership in another context.

### Managing SSO connections

On the Admin Portal SSO screen, you can view the identity provider details and connection status, metadata configuration details, and a list of recent connection sessions. You may test your SSO connection from the Admin Portal by using the “Test sign-in” button.

![A screenshot showing the Admin Portal SSO screen and where to click the “Test Single Sign-On” button.](https://images.workoscdn.com/images/b9ab3cc1-c524-4eae-9a25-f4c6a4059683.png?auto=format&fit=clip&q=50)

You may also edit your metadata configuration from the Admin Portal.

![A screenshot showing the Admin Portal SSO screen and where to "Edit Metadata Configuration".](https://images.workoscdn.com/images/2ada73aa-dfa4-45c8-afc8-2e69a3a9b4fc.png?auto=format&fit=clip&q=50)

The Sessions section displays a list of recent sessions by timestamp, and can be sorted by `state`.

![A screenshot showing the Admin Portal SSO screen and how to sort "Sessions" by "state".](https://images.workoscdn.com/images/0b1b05a0-8c18-4c99-8d02-22f2b4f2bf46.png?auto=format&fit=clip&q=50)

Click on a session in the list to see session details, such as the request made to the IdP, and the response.

![A screenshot showing the "Session Details" within the Admin Portal SSO screen.](https://images.workoscdn.com/images/6655ae75-c0c2-4b0c-be7b-b21dbfb6aadd.png?auto=format&fit=clip&q=50)

If you wish to reset your SSO connection and set it up from scratch, select “Reset Connection” and follow the prompts.

![A screenshot showing how to "Reset Connection" within the Admin Portal SSO screen.](https://images.workoscdn.com/images/b0f2919d-07f2-457d-8499-1b617235c485.png?auto=format&fit=clip&q=50)

### Managing directories

On the Admin Portal Directory Sync screen, you can view the directory provider details and connection status, user and group counts, and last sync time. There is also an option to reset the directory.

![A screenshot showing Admin Portal Directory Sync screen details.](https://images.workoscdn.com/images/13be17b8-46da-4c2c-801f-029be3686101.png?auto=format&fit=clip&q=50)

You may also view and edit the attribute map from the synced directory by clicking "Edit Attribute Map".

![A screenshot showing editing the attribute map in the Admin Portal Directory Sync screen.](https://images.workoscdn.com/images/c6dd347f-15dc-4918-9820-946f38361c2e.png?auto=format&fit=clip&q=50)

If you wish to update the groups that are being synced, select "Edit groups sync” and follow the prompts on the next page.

![A screenshot showing editing user groups in the Admin Portal Directory Sync screen.](https://images.workoscdn.com/images/05fc69da-3688-4c90-873b-505be64779b9.png?auto=format&fit=clip&q=50)

You can also view a complete list of users from all selected groups in your synced directory.

![A screenshot showing the user list in the Admin Portal Directory Sync screen.](https://images.workoscdn.com/images/f581e97e-2052-4635-805c-fad3c47f792e.png?auto=format&fit=clip&q=50)

### Example Apps

View sample Admin Portal apps for each SDK.

You can view minimal example apps that demonstrate how to use the WorkOS SDKs to power the Admin Portal:


  
  
  
  
  
  
  
  
  
</ExampleApps.Root>

### Custom Branding

Use your own logo and colors in the Admin Portal.

## Introduction

You can customize the look and feel of the Admin Portal from the WorkOS Dashboard. Go to [Branding settings](https://dashboard.workos.com/branding) where you can:

- Upload your logos
- Set your brand color
- View your custom domains

Custom branding can be tested for free in the staging environment. You need the [WorkOS Enterprise plan](https://workos.com/pricing) to enable custom branding in the production environment

![A screenshot showing the WorkOS Dashboard branding settings page.](https://images.workoscdn.com/images/fc67ec10-44e1-467c-a094-32ed3ff5bd92.png?auto=format&fit=clip&q=80)

## Logos and icons

You can upload custom branding assets which will be used in the Admin Portal as well as emails.

### Logo

Image with your company's logo and wordmark.

- **Usage:** Shown in the top left corner of the Admin Portal.
- **Requirements:** At least 160x160 px (JPG, PNG, or SVG. 100 KB max size)

![A screenshot showing the WorkOS Admin Portal and highlighting the custom logo.](https://images.workoscdn.com/images/ca579b3c-6a50-41aa-ad3e-62b32740b6c8.png?auto=format&fit=clip&q=50)

### Logo icon

A smaller, square version of the logo. This is often simply the logomark.

{`
 .tight-bullets p {
    margin-bottom: var(--space-2);
  }
  .tight-bullets .rt-Box:has(.rt-Box) {
    margin-bottom: var(--space-2);
  }
`}



- **Usage:**

  - Displayed in Admin Portal emails
  - Available for IT admins to
    download from the Admin Portal for configuring IdP-initiated SSO in Okta,
    Entra ID and Google Workspace.

- **Requirements:** At least 160x160 px with a
  1:1 aspect ratio (JPG, PNG, or SVG. 100 KB max size)



![A screenshot showing an email from WorkOS and highlighting the custom logo icon.](https://images.workoscdn.com/images/df8b54e6-a3fc-431b-8086-a01fb03388c7.png?auto=format&fit=clip&q=50)
![A screenshot showing the WorkOS Admin Portal and highlighting the custom logo icon download.](https://images.workoscdn.com/images/d07c701e-a051-4444-ae66-fc35eecb6791.png?auto=format&fit=clip&q=50)

### Favicon

A small icon that serves as branding for your website.

- **Usage:** Displayed in the address bar of the Admin Portal website.
- **Requirements:** At least 32x32 px with a 1:1 aspect ratio (JPG, PNG, GIF, SVG, WebP, AVIF, or ICO. 100 KB max size)

![A screenshot showing the WorkOS Admin Portal and highlighting the custom favicon.](https://images.workoscdn.com/images/15dd6370-961d-4d8b-97b1-6bfc3ef80143.png?auto=format&fit=clip&q=50)

### Brand color

Hex value used as the background color for buttons in the Admin Portal and emails.

![A screenshot showing the WorkOS Admin Portal and highlighting the custom button color.](https://images.workoscdn.com/images/080c0c45-08ee-44c4-b153-7b419174fc5e.png?auto=format&fit=clip&q=50)

![A screenshot showing an email from WorkOS and highlighting the custom button color.](https://images.workoscdn.com/images/6da61ac8-82f1-44e6-ab55-33d699841739.png?auto=format&fit=clip&q=50)

## Custom domains

WorkOS allows you to customize the domain you send API calls to, the domain used by the Admin Portal, and the from address used in emails sent to your customer's IT admin.

![A screenshot showing the WorkOS Dashboard branding settings page highlighting the custom domains section](https://images.workoscdn.com/images/b6afe130-219c-4e0e-8209-49b1a0fb6098.png?auto=format&fit=clip&q=80)

### Auth link domain

Controls the domain you send API calls to and the domain used in URL’s that face your customer’s IT admin such as ACS URL and SP Entity ID.

### Admin Portal domain

Controls the domain of Admin Portal setup links and Admin Portal sessions opened by your customer’s IT admin.

### Email domain

Controls the from address for emails sent to your customer's IT admin.

To configure custom domains, please reach out to [customer support](mailto:support@workos.com) for assistance.

## Team name

Your team name is visible in the Admin Portal and in the emails received by your customers’ organization admins.

![A screenshot showing the WorkOS Admin Portal and highlighting the page title.](https://images.workoscdn.com/images/0fd29434-3a5a-43ed-a446-45aec90f75da.png?auto=format&fit=clip&q=50)

![A screenshot showing an email by WorkOS and highlighting the sender name.](https://images.workoscdn.com/images/8d679e97-ceaa-4b94-bb75-290619c94085.png?auto=format&fit=clip&q=50)

## Support email address

Add a support email in the Dashboard to streamline communication with your customers’ organization admins. This email will be included in the footer of messages sent to them and set as the reply-to address, making it easy for admins to reach out with any questions.

![A screenshot showing an email with a custom support email address](https://images.workoscdn.com/images/8eb036ab-5fd0-44fe-81f3-6a69a8c38530.png?auto=format&fit=clip&q=50)