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

> Describes which Auth0 features can help you comply with the Conditions for Consent GDPR requirements.

# GDPR: Conditions for Consent

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

According to Article 7 of GDPR, you must ask users to consent on the processing of their personal data in a clear and easily accessible form. You must also show that the user has consented, and provide an easy way to withdraw consent at any time.

This article explains how you can use Auth0 features to implement these requirements.

<Callout icon="file-lines" color="#0EA5E9" iconType="regular">
  The contents of these documents are not intended to be legal advice, nor should they be considered a substitute for legal assistance. The final responsibility for understanding and complying with GDPR resides with you, though Auth0 will assist you in meeting GDPR requirements where possible.
</Callout>

## Ask for consent

Upon signup you have to ask your users for consent. With Auth0, you can save this information with the user metadata. There are several available options depending on how you use Auth0 to authenticate your users. Before you design your solution using metadata make sure you are aware of the restrictions. Auth0 limits the total size of the `user_metadata` to **16 MB**. To learn more, read [Metadata Field Names and Data Types](/docs/manage-users/user-accounts/metadata/metadata-fields-data).

<Warning>
  Auth0 metadata is not a secure data store and should not be used to store sensitive information, such as high-risk secrets and Personally Identifiable Information (PII) like social security numbers or credit card numbers. Auth0 customers are strongly encouraged to evaluate the data stored in metadata and only store that which is necessary for identity and access management purposes.
</Warning>

### Use Lock

You can customize the Lock UI to display links to your terms and conditions and/or privacy statement pages, and a consent checkbox that the user has to check in order to sign up. This can be done with the `mustAcceptTerm`s Lock option. This property, when set to `true`, displays a checkbox alongside the terms and conditions that must be checked before signing up. The terms and conditions can be specified using the languageDictionary option. To learn more, read [Lock Configuration Options](/docs/libraries/lock/lock-configuration).

Once the user accepts and signs up, save the consent information at the `user_metadata` using a rule that will run upon first login. To learn more about rules, read [Auth0 Rules](/docs/customize/rules).

If you want to get more information from the users during signup, and you authenticate users with a database connection, you can add custom fields to the Lock UI. This can be done with the additionalSignUpFields Lock option. Any custom fields are automatically added to the `user_metadata`.

If you are using social logins, adding custom fields is not an option, but you can redirect the user to another page where you ask for consent and any additional info, and then redirect back to finish the authentication transaction. This can be done with redirect rules. To learn more, read [Redirect Users from Within Rules](/docs/customize/rules/redirect-users). Once the signup process is complete, save the consent information at the `user_metadata` by calling 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> [**Update User** endpoint](https://auth0.com/docs/api/management/v2#!/Users/patch_users_by_id).

To learn how to implement any of these scenarios, read [GDPR: Track Consent with Lock](/docs/secure/data-privacy-and-compliance/gdpr/gdpr-track-consent-with-lock).

### Use custom UI

If you use a custom signup form with a database connection, you have to add an extra field to the signup screen in order to capture the user's consent. Afterward, call the Authentication API [**Signup** endpoint](https://auth0.com/docs/api/authentication#signup) in order to create the user in Auth0. At this point, you can set the consent information as part of the `user_metadata`.

Alternatively, if you use Auth0.js from a SPA, you can [use the `signup` method](/docs/libraries/auth0js) to create the user in Auth0 and set the consent info as part of the `user_metadata`.

If you use a custom signup form with social providers, you cannot set the user's consent information upon signup but you can update it as soon as the user is created. Save the consent information at the `user_metadata` by calling the Management API [**Update User** endpoint](https://auth0.com/docs/api/management/v2#!/Users/patch_users_by_id).

To learn how to implement any of these scenarios, read [GDPR: Track Consent with Custom UI](/docs/secure/data-privacy-and-compliance/gdpr/gdpr-track-consent-with-custom-ui).

### Re-consent and user migration

If you need to ask for consent from existing users and you decide to migrate your users from an existing database to Auth0, you can [use our Automatic User Migration](/docs/manage-users/user-migration/configure-automatic-migration-from-your-database) feature. By activating this, each time a user logs in for the first time (since this was activated), they will be created in Auth0 without having to reset their password. To do this you must:

* Write up the notification users will see around how users' data is being used, how long data will be used, users' rights, etc. as well as customize the UI sign-up box.
* Determine if re-consent is required for your users, depending on your old terms and conditions and previous privacy certifications.

Note that every time your Terms and Conditions change, you **must** ask the users for consent again.

## Track consent

According to GDPR, you should be able to show that the user has consented to the processing of their personal data.

With Auth0 you can save the user's consent information as part of the `user_metadata`. You can either save only a flag, showing if the user has consented or not, or a set of consent information and preferences (including, for example, the day the user provided consent, the terms they consented to, etc). Afterward, you can access and manipulate this information using the Management API.

The Management API also offers several options when it comes to user search and endpoints to update user metadata or batch export users.

To access the Management API, you will need an <Tooltip tip="Access Token: Authorization credential, in the form of an opaque string or JWT, used to access an API." cta="View Glossary" href="/docs/glossary?term=access+token">access token</Tooltip>. To learn how to get an access token for the Management API, read [Management API Access Tokens](/docs/secure/tokens/access-tokens/management-api-access-tokens).

### Search for users by email address

To search for a user using their email address, use the [**Search User by Email** endpoint](/docs/manage-users/user-search/user-search-best-practices).

Set the **fields** request parameter to `user_metadata` in order to limit the fields returned. This way, only the user\_metadata will be returned instead of the complete user profile.

Sample request:

<AuthCodeGroup>
  ```bash cURL theme={null}
  curl --request GET \
    --url 'https://{yourDomain}/api/v2/users-by-email?email=USER_EMAIL_ADDRESS&fields=user_metadata' \
    --header 'authorization: Bearer YOUR_MGMT_API_ACCESS_TOKEN'
  ```

  ```csharp C# theme={null}
  var client = new RestClient("https://{yourDomain}/api/v2/users-by-email?email=USER_EMAIL_ADDRESS&fields=user_metadata");
  var request = new RestRequest(Method.GET);
  request.AddHeader("authorization", "Bearer YOUR_MGMT_API_ACCESS_TOKEN");
  IRestResponse response = client.Execute(request);
  ```

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

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

  func main() {

  	url := "https://{yourDomain}/api/v2/users-by-email?email=USER_EMAIL_ADDRESS&fields=user_metadata"

  	req, _ := http.NewRequest("GET", url, nil)

  	req.Header.Add("authorization", "Bearer YOUR_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 response = Unirest.get("https://{yourDomain}/api/v2/users-by-email?email=USER_EMAIL_ADDRESS&fields=user_metadata")
    .header("authorization", "Bearer YOUR_MGMT_API_ACCESS_TOKEN")
    .asString();
  ```

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

  var options = {
    method: 'GET',
    url: 'https://{yourDomain}/api/v2/users-by-email',
    params: {email: 'USER_EMAIL_ADDRESS', fields: 'user_metadata'},
    headers: {authorization: 'Bearer YOUR_MGMT_API_ACCESS_TOKEN'}
  };

  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/users-by-email?email=USER_EMAIL_ADDRESS&fields=user_metadata",
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_ENCODING => "",
    CURLOPT_MAXREDIRS => 10,
    CURLOPT_TIMEOUT => 30,
    CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
    CURLOPT_CUSTOMREQUEST => "GET",
    CURLOPT_HTTPHEADER => [
      "authorization: Bearer YOUR_MGMT_API_ACCESS_TOKEN"
    ],
  ]);

  $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("")

  headers = { 'authorization': "Bearer YOUR_MGMT_API_ACCESS_TOKEN" }

  conn.request("GET", "/{yourDomain}/api/v2/users-by-email?email=USER_EMAIL_ADDRESS&fields=user_metadata", headers=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/users-by-email?email=USER_EMAIL_ADDRESS&fields=user_metadata")

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

  request = Net::HTTP::Get.new(url)
  request["authorization"] = 'Bearer YOUR_MGMT_API_ACCESS_TOKEN'

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

Sample response:

```json lines theme={null}
[
  {},
  {
    "user_metadata": {
      "consent": {
	    "given": true,
	    "date": "01/23/2018",
	    "text_details": "some-url"
	  }
    }
  }
]
```

### Search for users by ID

To search for a user using their ID, use the [**Get a User** endpoint](/docs/manage-users/user-search/user-search-best-practices).

Set the **fields** request parameter to `user_metadata` in order to limit the fields returned. This way, only the `user_metadata` will be returned instead of the complete user profile.

Sample request:

<AuthCodeGroup>
  ```bash cURL theme={null}
  curl --request GET \
    --url 'https://{yourDomain}/api/v2/users/%7ByourUserID%7D?fields=user_metadata' \
    --header 'authorization: Bearer {yourMgmtApiAccessToken}'
  ```

  ```csharp C# theme={null}
  var client = new RestClient("https://{yourDomain}/api/v2/users/%7ByourUserID%7D?fields=user_metadata");
  var request = new RestRequest(Method.GET);
  request.AddHeader("authorization", "Bearer {yourMgmtApiAccessToken}");
  IRestResponse response = client.Execute(request);
  ```

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

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

  func main() {

  	url := "https://{yourDomain}/api/v2/users/%7ByourUserID%7D?fields=user_metadata"

  	req, _ := http.NewRequest("GET", url, nil)

  	req.Header.Add("authorization", "Bearer {yourMgmtApiAccessToken}")

  	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 response = Unirest.get("https://{yourDomain}/api/v2/users/%7ByourUserID%7D?fields=user_metadata")
    .header("authorization", "Bearer {yourMgmtApiAccessToken}")
    .asString();
  ```

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

  var options = {
    method: 'GET',
    url: 'https://{yourDomain}/api/v2/users/%7ByourUserID%7D',
    params: {fields: 'user_metadata'},
    headers: {authorization: 'Bearer {yourMgmtApiAccessToken}'}
  };

  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/users/%7ByourUserID%7D?fields=user_metadata",
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_ENCODING => "",
    CURLOPT_MAXREDIRS => 10,
    CURLOPT_TIMEOUT => 30,
    CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
    CURLOPT_CUSTOMREQUEST => "GET",
    CURLOPT_HTTPHEADER => [
      "authorization: Bearer {yourMgmtApiAccessToken}"
    ],
  ]);

  $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("")

  headers = { 'authorization': "Bearer {yourMgmtApiAccessToken}" }

  conn.request("GET", "/{yourDomain}/api/v2/users/%7ByourUserID%7D?fields=user_metadata", headers=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/users/%7ByourUserID%7D?fields=user_metadata")

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

  request = Net::HTTP::Get.new(url)
  request["authorization"] = 'Bearer {yourMgmtApiAccessToken}'

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

Sample response:

```json lines theme={null}
{
  "user_metadata": {
    "consent": {
	    "given": true,
	    "date": "01/23/2018",
	    "text_details": "some-url"
  	}
  }
}
```

### Update consent information

To update a user's `user_metadata`, use the [**Update a User** endpoint](https://auth0.com/docs/api/management/v2#!/Users/patch_users_by_id).

How you structure your request depends on how you have structured your metadata: as root or as inner properties.

If your metadata are stored as root properties:

```json lines theme={null}
{
  "consentGiven": true,
  "consentDetails": "some-url"
}
```

If your metadata are stored as inner properties:

```json lines theme={null}
{
  "consent": {
    "given": true,
    "text_details": "some-url"
  }
}
```

#### Update root property

Updates to root-level properties are merged, so you only need to send the value for the field you want to update. For example, let's say we want to add a consent date and set it to `01/23/2018`.

<AuthCodeGroup>
  ```bash cURL theme={null}
  curl --request PATCH \
    --url 'https://{yourDomain}/api/v2/users/USER_ID' \
    --header 'authorization: Bearer YOUR_MGMT_API_ACCESS_TOKEN' \
    --header 'content-type: application/json' \
    --data '{"user_metadata":{"consentDate":"01/24/2018"}}'
  ```

  ```csharp C# theme={null}
  var client = new RestClient("https://{yourDomain}/api/v2/users/USER_ID");
  var request = new RestRequest(Method.PATCH);
  request.AddHeader("authorization", "Bearer YOUR_MGMT_API_ACCESS_TOKEN");
  request.AddHeader("content-type", "application/json");
  request.AddParameter("application/json", "{"user_metadata":{"consentDate":"01/24/2018"}}", 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/users/USER_ID"

  	payload := strings.NewReader("{"user_metadata":{"consentDate":"01/24/2018"}}")

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

  	req.Header.Add("authorization", "Bearer YOUR_MGMT_API_ACCESS_TOKEN")
  	req.Header.Add("content-type", "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 response = Unirest.patch("https://{yourDomain}/api/v2/users/USER_ID")
    .header("authorization", "Bearer YOUR_MGMT_API_ACCESS_TOKEN")
    .header("content-type", "application/json")
    .body("{"user_metadata":{"consentDate":"01/24/2018"}}")
    .asString();
  ```

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

  var options = {
    method: 'PATCH',
    url: 'https://{yourDomain}/api/v2/users/USER_ID',
    headers: {
      authorization: 'Bearer YOUR_MGMT_API_ACCESS_TOKEN',
      'content-type': 'application/json'
    },
    data: {user_metadata: {consentDate: '01/24/2018'}}
  };

  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/users/USER_ID",
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_ENCODING => "",
    CURLOPT_MAXREDIRS => 10,
    CURLOPT_TIMEOUT => 30,
    CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
    CURLOPT_CUSTOMREQUEST => "PATCH",
    CURLOPT_POSTFIELDS => "{"user_metadata":{"consentDate":"01/24/2018"}}",
    CURLOPT_HTTPHEADER => [
      "authorization: Bearer YOUR_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_metadata":{"consentDate":"01/24/2018"}}"

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

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

  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["authorization"] = 'Bearer YOUR_MGMT_API_ACCESS_TOKEN'
  request["content-type"] = 'application/json'
  request.body = "{"user_metadata":{"consentDate":"01/24/2018"}}"

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

This will add a new property to the user profile, the **user\_metadata.consentDate**, which will hold the date the customer consented. The response will be the full user profile. The updated metadata will look like this:

```json lines theme={null}
{
  "consentGiven": true,
  "consentDate": "01/23/2018",
  "consentDetails": "some-url"
}
```

#### Update inner property

To update an inner property, you must send the whole metadata object, even if you are not updating more than one property. If you do not include the entire object, Auth0 will remove your existing properties.

Let's add an inner property for the consent date and set it to `01/23/2018`.

<AuthCodeGroup>
  ```bash cURL theme={null}
  curl --request PATCH \
    --url 'https://{yourDomain}/api/v2/users/USER_ID' \
    --header 'authorization: Bearer YOUR_MGMT_API_ACCESS_TOKEN' \
    --header 'content-type: application/json' \
    --data '{"user_metadata":{"consent": {"given":true, "date":"01/23/2018", "text_details":"some-url"}}}'
  ```

  ```csharp C# theme={null}
  var client = new RestClient("https://{yourDomain}/api/v2/users/USER_ID");
  var request = new RestRequest(Method.PATCH);
  request.AddHeader("authorization", "Bearer YOUR_MGMT_API_ACCESS_TOKEN");
  request.AddHeader("content-type", "application/json");
  request.AddParameter("application/json", "{"user_metadata":{"consent": {"given":true, "date":"01/23/2018", "text_details":"some-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/users/USER_ID"

  	payload := strings.NewReader("{"user_metadata":{"consent": {"given":true, "date":"01/23/2018", "text_details":"some-url"}}}")

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

  	req.Header.Add("authorization", "Bearer YOUR_MGMT_API_ACCESS_TOKEN")
  	req.Header.Add("content-type", "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 response = Unirest.patch("https://{yourDomain}/api/v2/users/USER_ID")
    .header("authorization", "Bearer YOUR_MGMT_API_ACCESS_TOKEN")
    .header("content-type", "application/json")
    .body("{"user_metadata":{"consent": {"given":true, "date":"01/23/2018", "text_details":"some-url"}}}")
    .asString();
  ```

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

  var options = {
    method: 'PATCH',
    url: 'https://{yourDomain}/api/v2/users/USER_ID',
    headers: {
      authorization: 'Bearer YOUR_MGMT_API_ACCESS_TOKEN',
      'content-type': 'application/json'
    },
    data: {
      user_metadata: {consent: {given: true, date: '01/23/2018', text_details: 'some-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/users/USER_ID",
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_ENCODING => "",
    CURLOPT_MAXREDIRS => 10,
    CURLOPT_TIMEOUT => 30,
    CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
    CURLOPT_CUSTOMREQUEST => "PATCH",
    CURLOPT_POSTFIELDS => "{"user_metadata":{"consent": {"given":true, "date":"01/23/2018", "text_details":"some-url"}}}",
    CURLOPT_HTTPHEADER => [
      "authorization: Bearer YOUR_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_metadata":{"consent": {"given":true, "date":"01/23/2018", "text_details":"some-url"}}}"

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

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

  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["authorization"] = 'Bearer YOUR_MGMT_API_ACCESS_TOKEN'
  request["content-type"] = 'application/json'
  request.body = "{"user_metadata":{"consent": {"given":true, "date":"01/23/2018", "text_details":"some-url"}}}"

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

This will add a new property to the user profile, the **user\_metadata.consent.date,** which will hold the date the customer consented. The response will be the full user profile. The updated metadata will look like this:

```json lines theme={null}
{
  "consent": {
    "given": true,
    "date": "01/23/2018",
    "text_details": "some-url"
  }
}
```

### Export consent information

To export a list of your users using the Management API, use the [**User Export** endpoint](/docs/manage-users/user-search/user-search-best-practices).

This endpoint creates a job that exports all users associated with a connection. You will need the ID of the connection. To find this ID, use the [**Get Connections** endpoint](https://auth0.com/docs/api/management/v2#!/Connections/get_connections) (you can set the **name** parameter to the name of the connection to retrieve only this one).

Once you have the connection ID and an access token for the Management API, you are ready to start exporting users. To see a sample request and response, read [Import and Export Users](/docs/manage-users/user-migration). To learn how to get an access token for the Management API, read [Management API Access Tokens](/docs/secure/tokens/access-tokens/management-api-access-tokens).

You also must:

* Determine how you want to track consent. We recommend including information on not just the date the user consented, but the version of terms and conditions to which the user agreed. We also recommend including an array to hold information about users that withdraw their permission (remember that the user can consent and withdraw multiple times).
* Choose where you want to store consent: in Auth0's database or elsewhere.

## Withdraw consent

The user should have the option to withdraw consent using your app. This option should be easily accessible, and clearly distinguishable. Once the user decides to withdraw their consent, you should take action.

First, you have to decide how you will handle the withdrawal of consent: will you delete the users or flag them as deleted?

### Delete user

To delete a user, use the [**Delete a User** endpoint](https://auth0.com/docs/api/management/v2#!/Users/delete_users_by_id).

<AuthCodeGroup>
  ```bash cURL theme={null}
  curl --request DELETE \
    --url 'https://{yourDomain}/api/v2/users/USER_ID' \
    --header 'authorization: Bearer YOUR_MGMT_API_ACCESS_TOKEN'
  ```

  ```csharp C# theme={null}
  var client = new RestClient("https://{yourDomain}/api/v2/users/USER_ID");
  var request = new RestRequest(Method.DELETE);
  request.AddHeader("authorization", "Bearer YOUR_MGMT_API_ACCESS_TOKEN");
  IRestResponse response = client.Execute(request);
  ```

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

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

  func main() {

  	url := "https://{yourDomain}/api/v2/users/USER_ID"

  	req, _ := http.NewRequest("DELETE", url, nil)

  	req.Header.Add("authorization", "Bearer YOUR_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 response = Unirest.delete("https://{yourDomain}/api/v2/users/USER_ID")
    .header("authorization", "Bearer YOUR_MGMT_API_ACCESS_TOKEN")
    .asString();
  ```

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

  var options = {
    method: 'DELETE',
    url: 'https://{yourDomain}/api/v2/users/USER_ID',
    headers: {authorization: 'Bearer YOUR_MGMT_API_ACCESS_TOKEN'}
  };

  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/users/USER_ID",
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_ENCODING => "",
    CURLOPT_MAXREDIRS => 10,
    CURLOPT_TIMEOUT => 30,
    CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
    CURLOPT_CUSTOMREQUEST => "DELETE",
    CURLOPT_HTTPHEADER => [
      "authorization: Bearer YOUR_MGMT_API_ACCESS_TOKEN"
    ],
  ]);

  $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("")

  headers = { 'authorization': "Bearer YOUR_MGMT_API_ACCESS_TOKEN" }

  conn.request("DELETE", "/{yourDomain}/api/v2/users/USER_ID", headers=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/users/USER_ID")

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

  request = Net::HTTP::Delete.new(url)
  request["authorization"] = 'Bearer YOUR_MGMT_API_ACCESS_TOKEN'

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

The response body for this endpoint is empty, so if you want to confirm that the user was successfully deleted try to retrieve the user using their email. If the endpoint returns an error, then your call to delete the user was successful.

### Flag user as deleted

If you don't want to delete the user, flag their profile as deleted using the [**app\_metadata** endpoint](https://auth0.com/docs/api/management/v2#!/Users/patch_users_by_id). Then, add some code that will make the authentication process to fail for any user with their profile flagged as such.

This allows you to keep a record of deleted users for future use.

#### Flag the profile

To flag a user as deleted, use the app\_metadata. In the following example, we will show you how to add a property called **deleted** to the **app\_metadata** field. This allows you to configure the authentication process to treat all uses with this property set to true as deleted.

To update a user's metadata, use the [**Update a User** endpoint](https://auth0.com/docs/api/management/v2#!/Users/patch_users_by_id).

<AuthCodeGroup>
  ```bash cURL theme={null}
  curl --request PATCH \
    --url 'https://{yourDomain}/api/v2/users/USER_ID' \
    --header 'authorization: Bearer YOUR_MGMT_API_ACCESS_TOKEN' \
    --header 'content-type: application/json' \
    --data '{"app_metadata":{"deleted":true}}'
  ```

  ```csharp C# theme={null}
  var client = new RestClient("https://{yourDomain}/api/v2/users/USER_ID");
  var request = new RestRequest(Method.PATCH);
  request.AddHeader("authorization", "Bearer YOUR_MGMT_API_ACCESS_TOKEN");
  request.AddHeader("content-type", "application/json");
  request.AddParameter("application/json", "{"app_metadata":{"deleted":true}}", 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/users/USER_ID"

  	payload := strings.NewReader("{"app_metadata":{"deleted":true}}")

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

  	req.Header.Add("authorization", "Bearer YOUR_MGMT_API_ACCESS_TOKEN")
  	req.Header.Add("content-type", "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 response = Unirest.patch("https://{yourDomain}/api/v2/users/USER_ID")
    .header("authorization", "Bearer YOUR_MGMT_API_ACCESS_TOKEN")
    .header("content-type", "application/json")
    .body("{"app_metadata":{"deleted":true}}")
    .asString();
  ```

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

  var options = {
    method: 'PATCH',
    url: 'https://{yourDomain}/api/v2/users/USER_ID',
    headers: {
      authorization: 'Bearer YOUR_MGMT_API_ACCESS_TOKEN',
      'content-type': 'application/json'
    },
    data: {app_metadata: {deleted: true}}
  };

  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/users/USER_ID",
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_ENCODING => "",
    CURLOPT_MAXREDIRS => 10,
    CURLOPT_TIMEOUT => 30,
    CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
    CURLOPT_CUSTOMREQUEST => "PATCH",
    CURLOPT_POSTFIELDS => "{"app_metadata":{"deleted":true}}",
    CURLOPT_HTTPHEADER => [
      "authorization: Bearer YOUR_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 = "{"app_metadata":{"deleted":true}}"

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

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

  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["authorization"] = 'Bearer YOUR_MGMT_API_ACCESS_TOKEN'
  request["content-type"] = 'application/json'
  request.body = "{"app_metadata":{"deleted":true}}"

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

#### Disable login for flagged users

Next, you must disable login for users flagged as deleted. To do so, you will add a rule (a JavaScript snippet that runs as part of the authentication pipeline).

1. Go to [Auth0 Dashboard > Auth Pipeline > Rules](https://manage.auth0.com/#/rules) and create a rule.
2. Copy the script below:

   ```javascript lines theme={null}
   function (user, context, callback) {
     user.app_metadata = user.app_metadata || {};
     if (user.app_metadata.deleted){
     	return callback(new UnauthorizedError('Access denied (deleted user)'));
     }
     callback(null, user, context);
   }
   ```

   The script does the following:

   1. Checks the value of the **deleted** metadata property (`user.app_metadata.deleted`).
   2. Returns an `Access denied (deleted user)` error to your app if `user.app_metadata.deleted = true`
3. Give a name to your rule and save your changes.

You also must:

* Ensure the consent withdrawal piece is granular enough.
* Configure into the app, the area where customers will withdraw consent.

## Learn more

* [GDPR: Data Minimization](/docs/secure/data-privacy-and-compliance/gdpr/gdpr-data-minimization)
* [GDPR: Data Portability](/docs/secure/data-privacy-and-compliance/gdpr/gdpr-data-portability)
* [GDPR: Protect and Secure User Data](/docs/secure/data-privacy-and-compliance/gdpr/gdpr-protect-and-secure-user-data)
* [GDPR: Right to Access, Correct, and Erase Data](/docs/secure/data-privacy-and-compliance/gdpr/gdpr-right-to-access-correct-and-erase-data)
