> ## Documentation Index
> Fetch the complete documentation index at: https://auth0.com/llms.txt
> Use this file to discover all available pages before exploring further.

> Learn how to configure your tenant's and application's default login routes.

# Configure Default Login Routes

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>;
};

In certain cases (described below), Auth0 may need to redirect back to the application's Login Initiation endpoint, using OIDC third-party initiated login. To learn more, read [Initiating Login from a Third Party](https://openid.net/specs/openid-connect-core-1_0.html#ThirdPartyInitiatedLogin) at the [OpenID Foundation](https://openid.net).

You can configure these URIs with the Dashboard in [Application Settings](https://manage.auth0.com/#/applications/settings) or [Tenant Advanced Settings](https://manage.auth0.com/#/tenant/advanced) or with the <Tooltip tip="Management API: A product to allow customers to perform administrative tasks." cta="View Glossary" href="/docs/glossary?term=Management+API">Management API</Tooltip>.

<Tabs>
  <Tab title="Application level">
    <AuthCodeGroup>
      ```bash cURL theme={null}
      curl --request PATCH \
        --url 'https://{yourDomain}/api/v2/clients/{yourClientId}' \
        --header 'authorization: Bearer API2_ACCESS_TOKEN' \
        --header 'cache-control: no-cache' \
        --header 'content-type: application/json' \
        --data '{"initiate_login_uri": "<login_url>"}'
      ```

      ```csharp C# theme={null}
      var client = new RestClient("https://{yourDomain}/api/v2/clients/{yourClientId}");
      var request = new RestRequest(Method.PATCH);
      request.AddHeader("content-type", "application/json");
      request.AddHeader("authorization", "Bearer API2_ACCESS_TOKEN");
      request.AddHeader("cache-control", "no-cache");
      request.AddParameter("application/json", "{"initiate_login_uri": "<login_url>"}", 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/clients/{yourClientId}"

      	payload := strings.NewReader("{"initiate_login_uri": "<login_url>"}")

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

      	req.Header.Add("content-type", "application/json")
      	req.Header.Add("authorization", "Bearer API2_ACCESS_TOKEN")
      	req.Header.Add("cache-control", "no-cache")

      	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.patch("https://{yourDomain}/api/v2/clients/{yourClientId}")
        .header("content-type", "application/json")
        .header("authorization", "Bearer API2_ACCESS_TOKEN")
        .header("cache-control", "no-cache")
        .body("{"initiate_login_uri": "<login_url>"}")
        .asString();
      ```

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

      var options = {
        method: 'PATCH',
        url: 'https://{yourDomain}/api/v2/clients/{yourClientId}',
        headers: {
          'content-type': 'application/json',
          authorization: 'Bearer API2_ACCESS_TOKEN',
          'cache-control': 'no-cache'
        },
        data: {initiate_login_uri: '<login_url>'}
      };

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

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

      curl_setopt_array($curl, [
        CURLOPT_URL => "https://{yourDomain}/api/v2/clients/{yourClientId}",
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_ENCODING => "",
        CURLOPT_MAXREDIRS => 10,
        CURLOPT_TIMEOUT => 30,
        CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
        CURLOPT_CUSTOMREQUEST => "PATCH",
        CURLOPT_POSTFIELDS => "{"initiate_login_uri": "<login_url>"}",
        CURLOPT_HTTPHEADER => [
          "authorization: Bearer API2_ACCESS_TOKEN",
          "cache-control: no-cache",
          "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 = "{"initiate_login_uri": "<login_url>"}"

      headers = {
          'content-type': "application/json",
          'authorization': "Bearer API2_ACCESS_TOKEN",
          'cache-control': "no-cache"
          }

      conn.request("PATCH", "/{yourDomain}/api/v2/clients/{yourClientId}", 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/clients/{yourClientId}")

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

      request = Net::HTTP::Patch.new(url)
      request["content-type"] = 'application/json'
      request["authorization"] = 'Bearer API2_ACCESS_TOKEN'
      request["cache-control"] = 'no-cache'
      request.body = "{"initiate_login_uri": "<login_url>"}"

      response = http.request(request)
      puts response.read_body
      ```
    </AuthCodeGroup>
  </Tab>

  <Tab title="Tenant level">
    <AuthCodeGroup>
      ```bash cURL theme={null}
      curl --request PATCH \
        --url 'https://{yourDomain}/api/v2/tenants/settings' \
        --header 'authorization: Bearer API2_ACCESS_TOKEN' \
        --header 'cache-control: no-cache' \
        --header 'content-type: application/json' \
        --data '{"default_redirection_uri": "<login_url>"}'
      ```

      ```csharp C# theme={null}
      var client = new RestClient("https://{yourDomain}/api/v2/tenants/settings");
      var request = new RestRequest(Method.PATCH);
      request.AddHeader("content-type", "application/json");
      request.AddHeader("authorization", "Bearer API2_ACCESS_TOKEN");
      request.AddHeader("cache-control", "no-cache");
      request.AddParameter("application/json", "{"default_redirection_uri": "<login_url>"}", 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/tenants/settings"

      	payload := strings.NewReader("{"default_redirection_uri": "<login_url>"}")

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

      	req.Header.Add("content-type", "application/json")
      	req.Header.Add("authorization", "Bearer API2_ACCESS_TOKEN")
      	req.Header.Add("cache-control", "no-cache")

      	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.patch("https://{yourDomain}/api/v2/tenants/settings")
        .header("content-type", "application/json")
        .header("authorization", "Bearer API2_ACCESS_TOKEN")
        .header("cache-control", "no-cache")
        .body("{"default_redirection_uri": "<login_url>"}")
        .asString();
      ```

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

      var options = {
        method: 'PATCH',
        url: 'https://{yourDomain}/api/v2/tenants/settings',
        headers: {
          'content-type': 'application/json',
          authorization: 'Bearer API2_ACCESS_TOKEN',
          'cache-control': 'no-cache'
        },
        data: {default_redirection_uri: '<login_url>'}
      };

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

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

      curl_setopt_array($curl, [
        CURLOPT_URL => "https://{yourDomain}/api/v2/tenants/settings",
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_ENCODING => "",
        CURLOPT_MAXREDIRS => 10,
        CURLOPT_TIMEOUT => 30,
        CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
        CURLOPT_CUSTOMREQUEST => "PATCH",
        CURLOPT_POSTFIELDS => "{"default_redirection_uri": "<login_url>"}",
        CURLOPT_HTTPHEADER => [
          "authorization: Bearer API2_ACCESS_TOKEN",
          "cache-control: no-cache",
          "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 = "{"default_redirection_uri": "<login_url>"}"

      headers = {
          'content-type': "application/json",
          'authorization': "Bearer API2_ACCESS_TOKEN",
          'cache-control': "no-cache"
          }

      conn.request("PATCH", "/{yourDomain}/api/v2/tenants/settings", 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/tenants/settings")

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

      request = Net::HTTP::Patch.new(url)
      request["content-type"] = 'application/json'
      request["authorization"] = 'Bearer API2_ACCESS_TOKEN'
      request["cache-control"] = 'no-cache'
      request.body = "{"default_redirection_uri": "<login_url>"}"

      response = http.request(request)
      puts response.read_body
      ```
    </AuthCodeGroup>
  </Tab>
</Tabs>

***

The `login_url` should point to a route in the application that ends up redirecting to Auth0's `/authorize` endpoint, e.g. `https://mycompany.org/login`. Note that it requires `https` and it cannot point to `localhost`. `login_url` can include query parameters and a URI fragment.

As per the OIDC Third Party Initiated Login specification, the `iss` parameter containing Issuer Identifier will be added as a query string parameter to `login_url` before redirecting.

## Dynamic login URIs with metadata placeholders

If you use [Multiple Custom Domains](/docs/customize/custom-domains/multiple-custom-domains) or [Organizations](/docs/manage-users/organizations/organizations-overview), you are able to configure the application-level `initiate_login_uri` to include metadata placeholders. These metadata placeholders resolve dynamically at runtime.

### Supported placeholders

| Placeholder                    | Source                                                     | Example                                                   |
| ------------------------------ | ---------------------------------------------------------- | --------------------------------------------------------- |
| `{custom_domain.metadata.KEY}` | Metadata from the custom domain used in the request        | `https://{custom_domain.metadata.public_app_host}/login`  |
| `{organization.metadata.KEY}`  | Metadata from the Organization associated with the request | `https://{organization.metadata.public_login_host}/login` |

Metadata keys must start with `public_` (for example, `public_app_host`). Both placeholder types can be combined in the same URI if the request includes both a custom domain and an Organization context.

To review the full list of validation rules and restrictions, read [Custom domain URL placeholders](/docs/get-started/applications/wildcards-for-subdomains#custom-domain-url-placeholders).

<Note>
  Metadata placeholders are only supported on the application-level `initiate_login_uri`, not on the tenant-level `default_redirection_uri`.
</Note>

### Fallback behavior

Auth0 falls back to tenant-level `default_redirection_uri` if a placeholder cannot be resolved. For example, if the Organization is unknow or the metadata key does not exist. If the `default_redirection_uri` is also not configured, Auth0 renders an error page. We recommend configuring a tenant-level `default_redirection_uri` as a reliable fallback.

## Redirect default login route scenarios

### Users bookmark login page

When an application initiates the login process, it navigates to `https://{yourDomain}/authorize` with a set of [required parameters](https://auth0.com/docs/api/authentication#login). Auth0 then redirects end-users to an `https://{yourDomain}/login` page, with a URL that looks like:

`https://{yourDomain}/login?state=g6Fo2SBjNTRyanlVa3ZqeHN4d1htTnh&...`

The `state` parameter points to a record in an internal database where we track the status of the authorization transaction. Whenever the transaction completes, or after a set time passes, the record is deleted from the internal database.

If you are using Organizations and the end-user bookmarks the organization login prompt, Auth0 also includes the `organization` parameter when it redirects the user to the default login route.

Sometimes users bookmark the login page, and when they navigate to the bookmarked `/login` URL, the transaction record is no longer there and Auth0 cannot continue with the login flow. In that case, Auth0 will redirect to the default client URL if configured, or the tenant level URL if not. If no default login URL is set, Auth0 will render an error page.

### Complete password reset flow

After completing the password reset flow and the default URI for the application or tenant is configured, users will see a button that will let them navigate back to the login page.

This behavior only happens when you enable the <Tooltip tip="Universal Login: Your application redirects to Universal Login, hosted on Auth0's Authorization Server, to verify a user's identity." cta="View Glossary" href="/docs/glossary?term=Universal+Login">Universal Login</Tooltip> experience. With Classic Login, you need to configure the redirect URL in the Change Password template. To learn more, read [Customize Email Templates](/docs/customize/email/email-templates).

For tenants using Universal Login, the [`/post-password-change`](https://auth0.com/docs/api/management/v2/#!/Tickets/post_password_change) endpoint supports redirecting users back to a specific application. When `client_id` is specified and the application's login URI is set, users will see a button sending them back to the application after completing a password reset.

<AuthCodeGroup>
  ```bash cURL theme={null}
  curl --request POST \
    --url 'https://{yourDomain}/api/v2/tickets/password-change' \
    --header 'authorization: Bearer MGMT_API_ACCESS_TOKEN' \
    --header 'content-type: application/json' \
    --data '{ "user_id": "A_USER_ID", "client_id": "A_CLIENT_ID" }'
  ```

  ```csharp C# theme={null}
  var client = new RestClient("https://{yourDomain}/api/v2/tickets/password-change");
  var request = new RestRequest(Method.POST);
  request.AddHeader("content-type", "application/json");
  request.AddHeader("authorization", "Bearer MGMT_API_ACCESS_TOKEN");
  request.AddParameter("application/json", "{ "user_id": "A_USER_ID", "client_id": "A_CLIENT_ID" }", 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/tickets/password-change"

  	payload := strings.NewReader("{ "user_id": "A_USER_ID", "client_id": "A_CLIENT_ID" }")

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

  	req.Header.Add("content-type", "application/json")
  	req.Header.Add("authorization", "Bearer MGMT_API_ACCESS_TOKEN")

  	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/tickets/password-change")
    .header("content-type", "application/json")
    .header("authorization", "Bearer MGMT_API_ACCESS_TOKEN")
    .body("{ "user_id": "A_USER_ID", "client_id": "A_CLIENT_ID" }")
    .asString();
  ```

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

  var options = {
    method: 'POST',
    url: 'https://{yourDomain}/api/v2/tickets/password-change',
    headers: {
      'content-type': 'application/json',
      authorization: 'Bearer MGMT_API_ACCESS_TOKEN'
    },
    data: {user_id: 'A_USER_ID', client_id: 'A_CLIENT_ID'}
  };

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

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

  curl_setopt_array($curl, [
    CURLOPT_URL => "https://{yourDomain}/api/v2/tickets/password-change",
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_ENCODING => "",
    CURLOPT_MAXREDIRS => 10,
    CURLOPT_TIMEOUT => 30,
    CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
    CURLOPT_CUSTOMREQUEST => "POST",
    CURLOPT_POSTFIELDS => "{ "user_id": "A_USER_ID", "client_id": "A_CLIENT_ID" }",
    CURLOPT_HTTPHEADER => [
      "authorization: Bearer MGMT_API_ACCESS_TOKEN",
      "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 = "{ "user_id": "A_USER_ID", "client_id": "A_CLIENT_ID" }"

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

  conn.request("POST", "/{yourDomain}/api/v2/tickets/password-change", 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/tickets/password-change")

  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["content-type"] = 'application/json'
  request["authorization"] = 'Bearer MGMT_API_ACCESS_TOKEN'
  request.body = "{ "user_id": "A_USER_ID", "client_id": "A_CLIENT_ID" }"

  response = http.request(request)
  puts response.read_body
  ```
</AuthCodeGroup>

### Complete email verification flow

As part of the signup process, users who choose email as their identifier receive an email to verify their email address. If they click on the link, they will land on a page that says that the email was verified, with a button to go back to the application. When clicked, users will be redirected to the login page, and if they already have a valid session, they'll end up being redirected to the application.

This behavior only happens when the Universal Login experience is enabled. With Classic Login, you need to configure the redirect URL in the Verification Email template.

### Invite organization members

When users are invited to join an [Organization](/docs/manage-users/organizations/organizations-overview), they receive an invitation link by email. If they select the link, they are redirected to the configured default login route with invitation-specific parameters appended.

For example, if you have an organization-enabled application with an **Application Login URI** set to `https://myapp.com/login`, then the link sent in the email invitation that an end-user receives will be: `https://myapp.com/login?invitation={invitation_ticket_id}&organization={organization_id}&organization_name={organization_name}`.

Thus, the route in your application must accept `invitation` and `organization` parameters through the query string. To start the invitation acceptance transaction, it should forward both parameters along with the end-user to your Auth0 `/authorize` endpoint.

### Disabled cookies

If a user navigates to `https://{yourDomain}/authorize` with cookies disabled in their browser, Auth0 redirects the user to the application login URI. If the application login URI is not set, the redirect is sent to the tenant login URI instead.

Sending the user back to the login page can potentially cause a redirect loop. To avoid this issue, use a landing page to check the availability of cookies; if disabled, warn the user to enable them if they wish to continue.

## Learn more

* [Customize Email Templates](/docs/customize/email/email-templates)
* [Manage Authentication Factors with Authentication API](/docs/secure/multi-factor-authentication/manage-mfa-auth0-apis/manage-authenticator-factors-mfa-api)
