> ## Documentation Index
> Fetch the complete documentation index at: https://docs-dev-actions-triggers-prototype.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Multiple Custom Domains (MCD)

> Multiple custom domains Beta capability enables you to configure up to 100 custom domains within a single Auth0 tenant.

export const AuthCodeGroup = ({children, dropdown}) => {
  const [processedChildren, setProcessedChildren] = useState(children);
  useEffect(() => {
    let unsubscribe = null;
    function init() {
      unsubscribe = window.autorun(() => {
        const processChildren = node => {
          if (typeof node === "string") {
            let processedNode = node;
            for (const [key, value] of window.rootStore.variableStore.values.entries()) {
              const escapedKey = key.replaceAll(/[.*+?^${}()|[\]\\]/g, (String.raw)`\$&`);
              processedNode = processedNode.replaceAll(new RegExp(escapedKey, "g"), value);
            }
            return processedNode;
          } else if (Array.isArray(node)) {
            return node.map(processChildren);
          } else if (node && node.props && node.props.children) {
            return {
              ...node,
              props: {
                ...node.props,
                children: processChildren(node.props.children)
              }
            };
          }
          return node;
        };
        setProcessedChildren(processChildren(children));
      });
    }
    if (window.rootStore) {
      init();
    } else {
      window.addEventListener("adu:storeReady", init);
    }
    return () => {
      window.removeEventListener("adu:storeReady", init);
      unsubscribe?.();
    };
  }, [children]);
  return <CodeGroup dropdown={dropdown}>{processedChildren}</CodeGroup>;
};

export const AuthCodeBlock = ({filename, icon, language, highlight, children}) => {
  const [displayText, setDisplayText] = useState(children);
  const [copyText, setCopyText] = useState(children);
  const wrapperRef = React.useRef(null);
  useEffect(() => {
    let unsubscribe = null;
    function init() {
      if (!window.autorun || !window.rootStore) {
        return;
      }
      unsubscribe = window.autorun(() => {
        let processedChildrenForDisplay = children;
        let processedChildrenForCopy = children;
        for (const [key, value] of window.rootStore.variableStore.values.entries()) {
          const escapedKey = key.replaceAll(/[.*+?^${}()|[\]\\]/g, (String.raw)`\$&`);
          let displayValue = value;
          if (key === "{yourClientSecret}" && value !== "{yourClientSecret}") {
            displayValue = value.substring(0, 3) + "*****MASKED*****";
          }
          processedChildrenForDisplay = processedChildrenForDisplay.replaceAll(new RegExp(escapedKey, "g"), displayValue);
          processedChildrenForCopy = processedChildrenForCopy.replaceAll(new RegExp(escapedKey, "g"), value);
        }
        setDisplayText(processedChildrenForDisplay);
        setCopyText(processedChildrenForCopy);
      });
    }
    if (window.rootStore) {
      init();
    } else {
      window.addEventListener("adu:storeReady", init);
    }
    return () => {
      window.removeEventListener("adu:storeReady", init);
      unsubscribe?.();
    };
  }, [children]);
  useEffect(() => {
    if (!wrapperRef.current) return;
    const originalWriteText = navigator.clipboard.writeText.bind(navigator.clipboard);
    let isOverriding = false;
    const handleClick = e => {
      const button = e.target.closest('[data-testid="copy-code-button"]');
      if (!button || !wrapperRef.current.contains(button)) return;
      isOverriding = true;
      navigator.clipboard.writeText = text => {
        if (isOverriding) {
          isOverriding = false;
          navigator.clipboard.writeText = originalWriteText;
          return originalWriteText(copyText);
        }
        return originalWriteText(text);
      };
      setTimeout(() => {
        if (isOverriding) {
          isOverriding = false;
          navigator.clipboard.writeText = originalWriteText;
        }
      }, 100);
    };
    const wrapper = wrapperRef.current;
    wrapper.addEventListener('click', handleClick, true);
    return () => {
      wrapper.removeEventListener('click', handleClick, true);
      if (navigator.clipboard.writeText !== originalWriteText) {
        navigator.clipboard.writeText = originalWriteText;
      }
    };
  }, [copyText]);
  return <div ref={wrapperRef}>
      <CodeBlock filename={filename} icon={icon} language={language} lines highlight={highlight}>
        {displayText}
      </CodeBlock>
    </div>;
};

<Warning>
  Multiple Custom Domains (MCD) support in a single tenant is currently available in [Early Access](/docs/ja-jp/troubleshoot/product-lifecycle/product-release-stages). This capability is available only for Enterprise customers. If you are interested in using this feature, please reach out to your Sales exec/Technical Account Manager or open a support ticket through the [Auth0 Support Center](https://support.auth0.com/). By using this feature, you agree to the applicable Free Trial terms in Okta’s [Master Subscription Agreement](https://www.okta.com/legal/?_gl=1*agihqh*_gcl_au*NjM2NjA1MDg4LjE3NTM5ODE4NjY.*_ga*MTgyNDA4MjM2Ny4xNzE1MTAyMjQy*_ga_QKMSDV5369*czE3NTQ0NzQ3NTAkbzM1MyRnMSR0MTc1NDQ3NjU5MCRqNiRsMCRoMA..).

  To learn more about Auth0's product release cycle, review Product Release Stages.
</Warning>

Within Auth0, a <Tooltip data-tooltip-id="react-containers-DefinitionTooltip-0" href="/docs/ja-jp/glossary?term=custom-domain" tip="カスタムドメイン: 特殊な名前、またはバニティ名を持つサードパーティのドメイン。" cta="用語集の表示">custom domain</Tooltip> allows you to unify the login experience with your own brand and products. Multiple custom domains EA capability enables you to configure up to 1000 custom domains within a single Auth0 tenant. Please see the prerequisites below, including the applicable limits and conditions at General Availability (GA).

## Prerequisites

Before getting started with MCD, review the requirements below:

* Your tenant is on an Enterprise plan ([Public Cloud or Private Cloud deployments](/docs/ja-jp/deploy-monitor/deployment-options)). For more information, see
  Manage Subscriptions.
* Your Enterprise plan will provide a base entitlement of up to 20 custom domains per tenant in the MCD General Availability (GA) release.
* EA allows up to 1,000 custom domains per tenant for experimentation and validation. Once MCD GA launches, the base entitlement of up to 20 custom domains will apply. Any domains beyond the base entitlement will require an additional add-on SKU.
* You must be able to prove ownership of the configured custom domains.

## Configure Multiple Custom Domains

Auth0 will enable the MCD feature on tenants requested by EA participants. You can then add MCD to a tenant with the <Tooltip data-tooltip-id="react-containers-DefinitionTooltip-0" href="/docs/ja-jp/glossary?term=management-api" tip="Management API: 顧客が管理タスクを実行できるようにするための製品。" cta="用語集の表示">Management API</Tooltip> or in the <Tooltip data-tooltip-id="react-containers-DefinitionTooltip-0" href="/docs/ja-jp/glossary?term=auth0-dashboard" tip="Auth0 Dashboard: サービスを構成するためのAuth0の主製品。" cta="用語集の表示">Auth0 Dashboard</Tooltip>.

<Tabs>
  <Tab title="Auth0 Dashboard">
    To create a custom domain in the Auth0 Dashboard:

    1. Navigate to **Auth0 Dashboard** > **Branding > Custom Domains** .
    2. Select **+Add custom domain** .
    3. In the configuration form, provide the following information:

       * **Domain:** A [fully-qualified domain name](https://en.wikipedia.org/wiki/Fully_qualified_domain_name) that you own. For example: `my.custom-domain.com`.
       * **Certificate type:** Choose [\*\* Auth0-managed certificates\*\* ](/docs/ja-jp/customize/custom-domains/auth0-managed-certificates) or [**Self-managed certificates** ](/docs/ja-jp/customize/custom-domains/self-managed-certificates).
       * **Metadata (Key/Value):** Add optional metadata like `region`, `client_name`, or `client_id`, to help organize and filter your domains.
    4. Once you have configured the custom domain details, select **Save** .

    Your newly added domain name will show as `pending` until verification is complete.
  </Tab>

  <Tab title="Management API">
    To create a custom domain with the Management API, you need a machine-to-machine (M2M) application that is  authorized to use the  Management API. For more information, review. For more information, review [Management API Access Tokens](/docs/ja-jp/secure/tokens/access-tokens/management-api-access-tokens).

    1. Navigate to [Dashboard > Applications > Applications](https://manage.auth0.com/#/applications) and select **Create Application** .
    2. Enter a descriptive name for your application and choose **Machine to Machine Applications** . Then, select **Create** .
    3. Switch to the **APIs** view and then enable toggle for the `Auth0 Management API`.
    4. Expand the row to configure API’s permissions. For MCD, you must select the following:

       * `read:custom_domains`
       * `create:custom_domains`
       * `update:custom_domains`
       * `delete:custom_domains`
    5. Select **Update** .
    6. Navigate to the **Settings** tab to gather your **Client ID** , **Client Secret** , and **Domain** .
    7. Review [Get Management API Access Tokens](/docs/ja-jp/secure/tokens/access-tokens/management-api-access-tokens/get-management-api-access-tokens-for-production) to retrieve and store your access token.

    #### Create a Custom Domain

    You can create a new custom domain by sending a `POST` request to the `/api/v2/custom-domains` endpoint. See the example below to create a new custom domain using the Management API explorer, using the credentials retrieved above:

    <AuthCodeGroup>
      ```bash cURL theme={null}
      curl --request POST \
        --url 'https://{yourDomain}/api/v2/custom-domains' \
        --header 'accept: application/json' \
        --header 'authorization: Bearer {yourMgmtApiAccessToken}' \
        --header 'content-type: application/json' \
        --data '{ 
       "domain": "your.example-custom-domain.com", 
       "type": "auth0_managed_certs", 
       "tls_policy": "recommended", 
       "custom_client_ip_header": "true-client-ip", 
       "domain_metadata": {
          "environment": "development"} 
      }'
      ```

      ```csharp C# theme={null}
      var client = new RestClient("https://{yourDomain}/api/v2/custom-domains");
      var request = new RestRequest(Method.POST);
      request.AddHeader("authorization", "Bearer {yourMgmtApiAccessToken}");
      request.AddHeader("content-type", "application/json");
      request.AddHeader("accept", "application/json");
      request.AddParameter("application/json", "{ 
       "domain": "your.example-custom-domain.com", 
       "type": "auth0_managed_certs", 
       "tls_policy": "recommended", 
       "custom_client_ip_header": "true-client-ip", 
       "domain_metadata": {
       "environment": "development"} 
      }", ParameterType.RequestBody);
      IRestResponse response = client.Execute(request);
      ```

      ```go Go theme={null}
      package main

      import (
      	"fmt"
      	"strings"
      	"net/http"
      	"io/ioutil"
      )

      func main() {

      	url := "https://{yourDomain}/api/v2/custom-domains"

      	payload := strings.NewReader("{ 
       "domain": "your.example-custom-domain.com", 
       "type": "auth0_managed_certs", 
       "tls_policy": "recommended", 
       "custom_client_ip_header": "true-client-ip", 
       "domain_metadata": {
          "environment": "development"} 
      }")

      	req, _ := http.NewRequest("POST", url, payload)

      	req.Header.Add("authorization", "Bearer {yourMgmtApiAccessToken}")
      	req.Header.Add("content-type", "application/json")
      	req.Header.Add("accept", "application/json")

      	res, _ := http.DefaultClient.Do(req)

      	defer res.Body.Close()
      	body, _ := ioutil.ReadAll(res.Body)

      	fmt.Println(res)
      	fmt.Println(string(body))

      }
      ```

      ```java Java theme={null}
      HttpResponse<String> response = Unirest.post("https://{yourDomain}/api/v2/custom-domains")
        .header("authorization", "Bearer {yourMgmtApiAccessToken}")
        .header("content-type", "application/json")
        .header("accept", "application/json")
        .body("{ 
       "domain": "your.example-custom-domain.com", 
       "type": "auth0_managed_certs", 
       "tls_policy": "recommended", 
       "custom_client_ip_header": "true-client-ip", 
       "domain_metadata": {
          "environment": "development"} 
      }")
        .asString();
      ```

      ```javascript Node.JS theme={null}
      var axios = require("axios").default;

      var options = {
        method: 'POST',
        url: 'https://{yourDomain}/api/v2/custom-domains',
        headers: {
          authorization: 'Bearer {yourMgmtApiAccessToken}',
          'content-type': 'application/json',
          accept: 'application/json'
        },
        data: {
          domain: 'your.example-custom-domain.com',
          type: 'auth0_managed_certs',
          tls_policy: 'recommended',
          custom_client_ip_header: 'true-client-ip',
          domain_metadata: {environment: 'development'}
        }
      };

      axios.request(options).then(function (response) {
        console.log(response.data);
      }).catch(function (error) {
        console.error(error);
      });
      ```

      ```objc Obj-C theme={null}
      #import <Foundation/Foundation.h>

      NSDictionary *headers = @{ @"authorization": @"Bearer {yourMgmtApiAccessToken}",
                                 @"content-type": @"application/json",
                                 @"accept": @"application/json" };
      NSDictionary *parameters = @{ @"domain": @"your.example-custom-domain.com",
                                    @"type": @"auth0_managed_certs",
                                    @"tls_policy": @"recommended",
                                    @"custom_client_ip_header": @"true-client-ip",
                                    @"domain_metadata": @{ @"environment": @"development" } };

      NSData *postData = [NSJSONSerialization dataWithJSONObject:parameters options:0 error:nil];

      NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:@"https://{yourDomain}/api/v2/custom-domains"]
                                                             cachePolicy:NSURLRequestUseProtocolCachePolicy
                                                         timeoutInterval:10.0];
      [request setHTTPMethod:@"POST"];
      [request setAllHTTPHeaderFields:headers];
      [request setHTTPBody:postData];

      NSURLSession *session = [NSURLSession sharedSession];
      NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request
                                                  completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
                                                      if (error) {
                                                          NSLog(@"%@", error);
                                                      } else {
                                                          NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *) response;
                                                          NSLog(@"%@", httpResponse);
                                                      }
                                                  }];
      [dataTask resume];
      ```

      ```php PHP theme={null}
      $curl = curl_init();

      curl_setopt_array($curl, [
        CURLOPT_URL => "https://{yourDomain}/api/v2/custom-domains",
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_ENCODING => "",
        CURLOPT_MAXREDIRS => 10,
        CURLOPT_TIMEOUT => 30,
        CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
        CURLOPT_CUSTOMREQUEST => "POST",
        CURLOPT_POSTFIELDS => "{ 
       "domain": "your.example-custom-domain.com", 
       "type": "auth0_managed_certs", 
       "tls_policy": "recommended", 
       "custom_client_ip_header": "true-client-ip", 
       "domain_metadata": {
          "environment": "development"} 
      }",
        CURLOPT_HTTPHEADER => [
          "accept: application/json",
          "authorization: Bearer {yourMgmtApiAccessToken}",
          "content-type: application/json"
        ],
      ]);

      $response = curl_exec($curl);
      $err = curl_error($curl);

      curl_close($curl);

      if ($err) {
        echo "cURL Error #:" . $err;
      } else {
        echo $response;
      }
      ```

      ```python Python theme={null}
      import http.client

      conn = http.client.HTTPSConnection("")

      payload = "{ 
       "domain": "your.example-custom-domain.com", 
       "type": "auth0_managed_certs", 
       "tls_policy": "recommended", 
       "custom_client_ip_header": "true-client-ip", 
       "domain_metadata": {
          "environment": "development"} 
      }"

      headers = {
          'authorization': "Bearer {yourMgmtApiAccessToken}",
          'content-type': "application/json",
          'accept': "application/json"
          }

      conn.request("POST", "/{yourDomain}/api/v2/custom-domains", payload, headers)

      res = conn.getresponse()
      data = res.read()

      print(data.decode("utf-8"))
      ```

      ```ruby Ruby theme={null}
      require 'uri'
      require 'net/http'
      require 'openssl'

      url = URI("https://{yourDomain}/api/v2/custom-domains")

      http = Net::HTTP.new(url.host, url.port)
      http.use_ssl = true
      http.verify_mode = OpenSSL::SSL::VERIFY_NONE

      request = Net::HTTP::Post.new(url)
      request["authorization"] = 'Bearer {yourMgmtApiAccessToken}'
      request["content-type"] = 'application/json'
      request["accept"] = 'application/json'
      request.body = "{ 
       "domain": "your.example-custom-domain.com", 
       "type": "auth0_managed_certs", 
       "tls_policy": "recommended", 
       "custom_client_ip_header": "true-client-ip", 
       "domain_metadata": {
          "environment": "development"} 
      }"

      response = http.request(request)
      puts response.read_body
      ```

      ```swift Swift theme={null}
      import Foundation

      let headers = [
        "authorization": "Bearer {yourMgmtApiAccessToken}",
        "content-type": "application/json",
        "accept": "application/json"
      ]
      let parameters = [
        "domain": "your.example-custom-domain.com",
        "type": "auth0_managed_certs",
        "tls_policy": "recommended",
        "custom_client_ip_header": "true-client-ip",
        "domain_metadata": ["environment": "development"]
      ] as [String : Any]

      let postData = JSONSerialization.data(withJSONObject: parameters, options: [])

      let request = NSMutableURLRequest(url: NSURL(string: "https://{yourDomain}/api/v2/custom-domains")! as URL,
                                              cachePolicy: .useProtocolCachePolicy,
                                          timeoutInterval: 10.0)
      request.httpMethod = "POST"
      request.allHTTPHeaderFields = headers
      request.httpBody = postData as Data

      let session = URLSession.shared
      let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in
        if (error != nil) {
          print(error)
        } else {
          let httpResponse = response as? HTTPURLResponse
          print(httpResponse)
        }
      })

      dataTask.resume()
      ```
    </AuthCodeGroup>

    If successful, you should see the following returned with the details of your new custom domain, including it's verification status. The `status` is initially returned as `pending`.

    ```json lines theme={null}
    {
        "custom_domain_id": "cd_abc123def456",
        "domain": "your.example-custom-domain.com",
        "primary": false,
        "status": "pending",
        "type": "auth0_managed_certs",
        "verification": {
            "methods": [
                {
                    "name": "CNAME",
                    "record": "yourtenant.auth0.com",
                    "domain": "your.example-custom-domain.com"
                }
            ]
        },
        "tls_policy": "recommended",
        "domain_metadata": {
            "environment": "development"
        }
    }
    ```
  </Tab>
</Tabs>

## MCD features

MCD offers many key features and functionalities to more effectively manage your Auth0 implementation and improve user experience. You are responsible for owning and registering your desired custom domains with a domain name registrar.

The [Auth0 Management API](https://auth0.com/docs/api/management/v2/custom-domains/get-custom-domains) provides comprehensive support for **Create, Read, Update, Delete, and Verify** operations for these custom domains, offering full programmatic control over their lifecycle.

MCD EA supports the following Auth0 SDKs: [Node.js](https://github.com/auth0/node-auth0) and [Go](https://github.com/auth0/go-auth0).

### Domain verification

The method for verifying domain name ownership depends on your chosen management type:

| Domain Type                                                                                           | Verification Method | Details                                                                     |
| ----------------------------------------------------------------------------------------------------- | ------------------- | --------------------------------------------------------------------------- |
| **[Auth0-Managed](https://auth0.com/docs/ja-jp/customize/custom-domains/auth0-managed-certificates)** | CNAME DNS record    | Configure this record to confirm domain ownership and activate your domain. |
| **[Self-Managed](https://auth0.com/docs/ja-jp/customize/custom-domains/self-managed-certificates)**   | TXT DNS record      | Specific TXT record details are provided in the Create API response.        |

After your custom domain has been verified by Auth0, you can use it immediately to configure Auth0 features for your users. For more information, read [Configure Features to Use Custom Domains](/docs/ja-jp/customize/custom-domains/configure-features-to-use-custom-domains).

### Metadata for enhanced management

You can provision up to 10 metadata fields per custom domain for easier organization and future customization. In upcoming releases, these metadata fields will enable advanced customization of email templates, <Tooltip data-tooltip-id="react-containers-DefinitionTooltip-0" href="/docs/ja-jp/glossary?term=universal-login" tip="ユニバーサルログイン: アプリケーションは、Auth0の認可サーバーでホストされているユニバーサルログインにリダイレクトして、ユーザーのアイデンティティを確認します。" cta="用語集の表示">Universal Login</Tooltip>, and authentication logic.

### Customize email templates

Leverage your custom domain information to personalize and brand your email templates, ensuring a consistent user experience.  To facilitate this, MCD provides the `custom_domain.domain` variable for use in Liquid Syntax.

For example, you could set the **From Address** of your email template to `support@{{ custom_domain.domain }}` , which would render as `support@my.custom-domain.com`. This variable is available through Liquid Syntax in the **From Address** , **Subject** , and **Message** fields. To learn more, read [Customize Email Templates](/docs/ja-jp/customize/email/email-templates).

#### Customize email handling using the Management API

If you configured Multiple Custom Domains and enabled [Use Custom Domains in Emails](/docs/ja-jp/customize/custom-domains/configure-features-to-use-custom-domains#use-custom-domains-in-emails), the `auth0-custom-domain` HTTP header is available when using the Auth0 Management API. The header is passed as the value for the `domain object` in email templates.

<Callout icon="file-lines" color="#0EA5E9" iconType="regular">
  The `auth0-custom-domain` HTTP header is required if MCD is configured with at least 1 domain in `ready` state.
  If you are moving from a single custom domain to MCD, you must update your existing Management API email requests to include the `auth0-custom-domain` HTTP header .
</Callout>

The following Management API endpoints accept the `auth0-custom-domain` HTTP header:

* [Send an email address verification email](https://auth0.com/docs/api/management/v2#!/Jobs/post_verification_email)
* [Create an email verification ticket](https://auth0.com/docs/api/management/v2/tickets/post-email-verification)
* [Create invitations to an organization](https://auth0.com/docs/api/management/v2/organizations/post-invitations)
* [Create a user](https://auth0.com/docs/api/management/v2/users/post-users)
* [Create a multi-factor authentication enrollment ticket](https://auth0.com/docs/api/management/v2/guardian/post-ticket)
* [Create a password change ticket](https://auth0.com/docs/api/management/v2/tickets/post-password-change)

For example: To create a password change ticket using Auth0 SDK for [Node.js](https://github.com/auth0/node-auth0).

```javascript lines theme={null}
const { ManagementClient } = require('auth0');

const auth0 = new ManagementClient({
    domain: '{yourDomain}',
    clientId: '{yourClientId}',
    clientSecret: '{yourClientSecret}',
    scope: 'create:passwords_tickets',
    headers: {
        'auth0-custom-domain': 'my-custom-domain.com'
    }
});

(async () => {
    try {
        const ticket = await auth0.tickets.changePassword({
            user_id: 'auth0|abc123',
            result_url: 'https://example.com/success'
        });
        console.log('Password change ticket created:', ticket.data.ticket);
    } catch (err) {
        console.error('Error creating password change ticket:', err);
    }
})();
```

Sample response: The custom domain is passed in the header used to generate the ticket URL.

```json lines theme={null}
{
    "ticket": "https://my-custom-domain.com/u/reset-verify?ticket=abc123"
}
```

##### Response messages

When you provide the `auth0-custom-domain` HTTP header, the following additional response types are possible:

| HTTP status code | Message                                                      |
| ---------------- | ------------------------------------------------------------ |
| `409`            | The tenant has multiple verified custom domains.             |
| `400`            | The custom domain does not exist for the tenant.             |
| `400`            | The `auth0-custom-domain` HTTP header has an invalid format. |

<Callout icon="file-lines" color="#0EA5E9" iconType="regular">
  If you enable MCD and use the Auth0 Dashboard to configure Email templates, the **Try** feature will not allow you to test with a custom domain.
</Callout>

### Multiple Custom Domains with Actions

Auth0 [Actions](/docs/ja-jp/customize/actions) allows you to create custom logic handling of your different transactions based on the custom domain.

For example, you could create an Action that directs a user to an associated [Organization](/docs/ja-jp/manage-users/organizations), or enforce a specific [access control policy](/docs/ja-jp/customize/actions/use-cases#api-authorization).

To facilitate this, post-login Actions features the object `event.request.hostname`, which provides the hostname being used for the authentication flow.

#### Use case: Restrict user access to an Organization based on custom domain

Store a domain allowlist and denylist (for example, `allow_domains` and `deny_domains`) in your Organization’s metadata.

Create an Action that:

1. Gets the user’s domain through the `event.request.hostname` property
2. Compares that domain with both lists
3. Allows or denies the user access accordingly

```js lines expandable theme={null}
exports.onExecutePostLogin = async (event, api) => {

    console.log(`org ${event?.organization?.name} accessed from domain ${event?.request?.hostname}`);

    if (event?.organization?.metadata?.deny_domains && event?.organization?.metadata?.allow_domains) {
        console.warn(`[WARNING] configuration issue. org ${event?.organization?.name} has both deny_domains and allow_domains`);
    }

    // Check either deny (A) allow (B) not both

    // (A) checks org's deny_list
    const isDomainDenied = () =>
        (event?.organization?.metadata?.deny_domains ? event?.organization?.metadata?.deny_domains.split(',').map(d => d.trim()).includes(event?.request?.hostname) : false);

    if (isDomainDenied()) {
        return api.access.deny(`access to org ${event?.organization?.name} not allowed on domain ${event?.request?.hostname}`);
    }

    // (B) checks org's allow_list
    const isDomainAllowed = () =>
        (event?.organization?.metadata?.allow_domains ? event?.organization?.metadata?.allow_domains.split(',').map(d => d.trim()).includes(event?.request?.hostname) : false);

    if (!isDomainAllowed()) {
        return api.access.deny(`access to org ${event?.organization?.name} not allowed on domain ${event?.request?.hostname}`);
    }

};
```

<Callout icon="file-lines" color="#0EA5E9" iconType="regular">
  MCD GA will introduce a new custom domain Action object, which will include custom domain metadata. Your Action code will need to be updated at GA if you use the new object.
</Callout>

### Custom domain attributes

The MCD EA release introduces the following attributes related to custom domain verification and SSL/TLS certificate management. These additions provide granular insights into the provisioning and operational status of custom domains. Please be aware that the attributes and their descriptions outlined in this document are subject to change in future releases.

#### Updated attributes

| Attribute | Description                                                                                                                                                                                                                                                              |
| --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `status`  | A new enumeration value, `failed`, has been added to the `status` attribute. This value indicates that the custom domain verification process has encountered an error and was unsuccessful. This is in addition to the existing supported values `pending` and `ready`. |

#### New attributes

The following attributes are supported for Auth0-managed domains only:

| Attribute                           | Description                                                                                                                                                                                                   |
| ----------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `verification.status`               | Status of the DNS record verification process. Possible values are: `verified`, `pending`, and `failed`.                                                                                                      |
| `verification.error_msg`            | In the event that `verification.status` indicates a failure, this string attribute will contain a human-readable error message providing context for the verification failure.                                |
| `verification.last_verified_at`     | This timestamp attribute records the date and time of the last successful verification of the custom domain. The format of this timestamp will adhere to ISO 8601.                                            |
| `certificate`                       | This object encapsulates information related to the SSL/TLS certificate associated with the custom domain.                                                                                                    |
| `certificate.status`                | This attribute indicates the current provisioning status of the SSL/TLS certificate. Possible values will include states such as `provisioning`, `provisioned`, `provisioning_failed`, and `renewing_failed`. |
| `certificate.error_msg`             | If the `certificate.status` is `provisioning_failed` or `renewing_failed`, this string attribute will provide a user-friendly error message detailing the reason for the failure.                             |
| `certificate.certificate_authority` | This string attribute specifies the Certificate Authority that issued the SSL/TLS certificate for the custom domain.                                                                                          |
| `certificate.renews_before`         | For Auth0-managed custom domains, this new timestamp attribute indicates the date and time before which the SSL/TLS certificate must be renewed. The format of this timestamp will adhere to ISO 8601.        |
