GDPR: Conditions for Consent

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.

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.

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.

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 mustAcceptTerms 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.

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.

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. Once the signup process is complete, save the consent information at the user_metadata by calling the Management API Update User endpoint.

To learn how to implement any of these scenarios, read 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 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  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.

To learn how to implement any of these scenarios, read GDPR: Track Consent with Custom UI.

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 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.

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 he consented to, etc). Afterward, you can access and manipulate this information using our 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 access token. To learn how to get an access token for the Management API, read 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.

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:


curl --request GET \
  --url 'https://YOUR_DOMAIN/api/v2/users-by-email?email=USER_EMAIL_ADDRESS&fields=user_metadata' \
  --header 'authorization: Bearer YOUR_MGMT_API_ACCESS_TOKEN'
var client = new RestClient("https://YOUR_DOMAIN/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);
package main

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

func main() {

	url := "https://YOUR_DOMAIN/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))

}
HttpResponse<String> response = Unirest.get("https://YOUR_DOMAIN/api/v2/users-by-email?email=USER_EMAIL_ADDRESS&fields=user_metadata")
  .header("authorization", "Bearer YOUR_MGMT_API_ACCESS_TOKEN")
  .asString();
var axios = require("axios").default;

var options = {
  method: 'GET',
  url: 'https://YOUR_DOMAIN/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);
});
#import <Foundation/Foundation.h>

NSDictionary *headers = @{ @"authorization": @"Bearer YOUR_MGMT_API_ACCESS_TOKEN" };

NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:@"https://YOUR_DOMAIN/api/v2/users-by-email?email=USER_EMAIL_ADDRESS&fields=user_metadata"]
                                                       cachePolicy:NSURLRequestUseProtocolCachePolicy
                                                   timeoutInterval:10.0];
[request setHTTPMethod:@"GET"];
[request setAllHTTPHeaderFields:headers];

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];
$curl = curl_init();

curl_setopt_array($curl, [
  CURLOPT_URL => "https://YOUR_DOMAIN/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;
}
import http.client

conn = http.client.HTTPSConnection("")

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

conn.request("GET", "/YOUR_DOMAIN/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"))
require 'uri'
require 'net/http'
require 'openssl'

url = URI("https://YOUR_DOMAIN/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
import Foundation

let headers = ["authorization": "Bearer YOUR_MGMT_API_ACCESS_TOKEN"]

let request = NSMutableURLRequest(url: NSURL(string: "https://YOUR_DOMAIN/api/v2/users-by-email?email=USER_EMAIL_ADDRESS&fields=user_metadata")! as URL,
                                        cachePolicy: .useProtocolCachePolicy,
                                    timeoutInterval: 10.0)
request.httpMethod = "GET"
request.allHTTPHeaderFields = headers

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

Sample response:

[
  {},
  {
    "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.

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:


curl --request GET \
  --url 'https://YOUR_DOMAIN/api/v2/users/YOUR_USER_ID?fields=user_metadata' \
  --header 'authorization: Bearer YOUR_MGMT_API_ACCESS_TOKEN'
var client = new RestClient("https://YOUR_DOMAIN/api/v2/users/YOUR_USER_ID?fields=user_metadata");
var request = new RestRequest(Method.GET);
request.AddHeader("authorization", "Bearer YOUR_MGMT_API_ACCESS_TOKEN");
IRestResponse response = client.Execute(request);
package main

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

func main() {

	url := "https://YOUR_DOMAIN/api/v2/users/YOUR_USER_ID?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))

}
HttpResponse<String> response = Unirest.get("https://YOUR_DOMAIN/api/v2/users/YOUR_USER_ID?fields=user_metadata")
  .header("authorization", "Bearer YOUR_MGMT_API_ACCESS_TOKEN")
  .asString();
var axios = require("axios").default;

var options = {
  method: 'GET',
  url: 'https://YOUR_DOMAIN/api/v2/users/YOUR_USER_ID',
  params: {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);
});
#import <Foundation/Foundation.h>

NSDictionary *headers = @{ @"authorization": @"Bearer YOUR_MGMT_API_ACCESS_TOKEN" };

NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:@"https://YOUR_DOMAIN/api/v2/users/YOUR_USER_ID?fields=user_metadata"]
                                                       cachePolicy:NSURLRequestUseProtocolCachePolicy
                                                   timeoutInterval:10.0];
[request setHTTPMethod:@"GET"];
[request setAllHTTPHeaderFields:headers];

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];
$curl = curl_init();

curl_setopt_array($curl, [
  CURLOPT_URL => "https://YOUR_DOMAIN/api/v2/users/YOUR_USER_ID?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;
}
import http.client

conn = http.client.HTTPSConnection("")

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

conn.request("GET", "/YOUR_DOMAIN/api/v2/users/YOUR_USER_ID?fields=user_metadata", headers=headers)

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

print(data.decode("utf-8"))
require 'uri'
require 'net/http'
require 'openssl'

url = URI("https://YOUR_DOMAIN/api/v2/users/YOUR_USER_ID?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
import Foundation

let headers = ["authorization": "Bearer YOUR_MGMT_API_ACCESS_TOKEN"]

let request = NSMutableURLRequest(url: NSURL(string: "https://YOUR_DOMAIN/api/v2/users/YOUR_USER_ID?fields=user_metadata")! as URL,
                                        cachePolicy: .useProtocolCachePolicy,
                                    timeoutInterval: 10.0)
request.httpMethod = "GET"
request.allHTTPHeaderFields = headers

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

Sample response:

{
  "user_metadata": {
    "consent": {
	    "given": true,
	    "date": "01/23/2018",
	    "text_details": "some-url"
  	}
  }
}


To update a user's user_metadata, use the Update a User endpoint.

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:

{
  "consentGiven": true,
  "consentDetails": "some-url"
}


If your metadata are stored as inner properties:

{
  "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.


curl --request PATCH \
  --url 'https://YOUR_DOMAIN/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"}}'
var client = new RestClient("https://YOUR_DOMAIN/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);
package main

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

func main() {

	url := "https://YOUR_DOMAIN/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))

}
HttpResponse<String> response = Unirest.patch("https://YOUR_DOMAIN/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();
var axios = require("axios").default;

var options = {
  method: 'PATCH',
  url: 'https://YOUR_DOMAIN/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);
});
#import <Foundation/Foundation.h>

NSDictionary *headers = @{ @"authorization": @"Bearer YOUR_MGMT_API_ACCESS_TOKEN",
                           @"content-type": @"application/json" };
NSDictionary *parameters = @{ @"user_metadata": @{ @"consentDate": @"01/24/2018" } };

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

NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:@"https://YOUR_DOMAIN/api/v2/users/USER_ID"]
                                                       cachePolicy:NSURLRequestUseProtocolCachePolicy
                                                   timeoutInterval:10.0];
[request setHTTPMethod:@"PATCH"];
[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];
$curl = curl_init();

curl_setopt_array($curl, [
  CURLOPT_URL => "https://YOUR_DOMAIN/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;
}
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", "/YOUR_DOMAIN/api/v2/users/USER_ID", payload, headers)

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

print(data.decode("utf-8"))
require 'uri'
require 'net/http'
require 'openssl'

url = URI("https://YOUR_DOMAIN/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
import Foundation

let headers = [
  "authorization": "Bearer YOUR_MGMT_API_ACCESS_TOKEN",
  "content-type": "application/json"
]
let parameters = ["user_metadata": ["consentDate": "01/24/2018"]] as [String : Any]

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

let request = NSMutableURLRequest(url: NSURL(string: "https://YOUR_DOMAIN/api/v2/users/USER_ID")! as URL,
                                        cachePolicy: .useProtocolCachePolicy,
                                    timeoutInterval: 10.0)
request.httpMethod = "PATCH"
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()

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:

{
  "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.


curl --request PATCH \
  --url 'https://YOUR_DOMAIN/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"}}}'
var client = new RestClient("https://YOUR_DOMAIN/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);
package main

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

func main() {

	url := "https://YOUR_DOMAIN/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))

}
HttpResponse<String> response = Unirest.patch("https://YOUR_DOMAIN/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();
var axios = require("axios").default;

var options = {
  method: 'PATCH',
  url: 'https://YOUR_DOMAIN/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);
});
#import <Foundation/Foundation.h>

NSDictionary *headers = @{ @"authorization": @"Bearer YOUR_MGMT_API_ACCESS_TOKEN",
                           @"content-type": @"application/json" };
NSDictionary *parameters = @{ @"user_metadata": @{ @"consent": @{ @"given": @YES, @"date": @"01/23/2018", @"text_details": @"some-url" } } };

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

NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:@"https://YOUR_DOMAIN/api/v2/users/USER_ID"]
                                                       cachePolicy:NSURLRequestUseProtocolCachePolicy
                                                   timeoutInterval:10.0];
[request setHTTPMethod:@"PATCH"];
[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];
$curl = curl_init();

curl_setopt_array($curl, [
  CURLOPT_URL => "https://YOUR_DOMAIN/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;
}
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", "/YOUR_DOMAIN/api/v2/users/USER_ID", payload, headers)

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

print(data.decode("utf-8"))
require 'uri'
require 'net/http'
require 'openssl'

url = URI("https://YOUR_DOMAIN/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
import Foundation

let headers = [
  "authorization": "Bearer YOUR_MGMT_API_ACCESS_TOKEN",
  "content-type": "application/json"
]
let parameters = ["user_metadata": ["consent": [
      "given": true,
      "date": "01/23/2018",
      "text_details": "some-url"
    ]]] as [String : Any]

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

let request = NSMutableURLRequest(url: NSURL(string: "https://YOUR_DOMAIN/api/v2/users/USER_ID")! as URL,
                                        cachePolicy: .useProtocolCachePolicy,
                                    timeoutInterval: 10.0)
request.httpMethod = "PATCH"
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()

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:

{
  "consent": {
    "given": true,
    "date": "01/23/2018",
    "text_details": "some-url"
  }
}


To export a list of your users using the Management API, use the User Export.

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 (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. To learn how to get an access token for the Management API, read 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.

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.


curl --request DELETE \
  --url 'https://YOUR_DOMAIN/api/v2/users/USER_ID' \
  --header 'authorization: Bearer YOUR_MGMT_API_ACCESS_TOKEN'
var client = new RestClient("https://YOUR_DOMAIN/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);
package main

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

func main() {

	url := "https://YOUR_DOMAIN/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))

}
HttpResponse<String> response = Unirest.delete("https://YOUR_DOMAIN/api/v2/users/USER_ID")
  .header("authorization", "Bearer YOUR_MGMT_API_ACCESS_TOKEN")
  .asString();
var axios = require("axios").default;

var options = {
  method: 'DELETE',
  url: 'https://YOUR_DOMAIN/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);
});
#import <Foundation/Foundation.h>

NSDictionary *headers = @{ @"authorization": @"Bearer YOUR_MGMT_API_ACCESS_TOKEN" };

NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:@"https://YOUR_DOMAIN/api/v2/users/USER_ID"]
                                                       cachePolicy:NSURLRequestUseProtocolCachePolicy
                                                   timeoutInterval:10.0];
[request setHTTPMethod:@"DELETE"];
[request setAllHTTPHeaderFields:headers];

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];
$curl = curl_init();

curl_setopt_array($curl, [
  CURLOPT_URL => "https://YOUR_DOMAIN/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;
}
import http.client

conn = http.client.HTTPSConnection("")

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

conn.request("DELETE", "/YOUR_DOMAIN/api/v2/users/USER_ID", headers=headers)

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

print(data.decode("utf-8"))
require 'uri'
require 'net/http'
require 'openssl'

url = URI("https://YOUR_DOMAIN/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
import Foundation

let headers = ["authorization": "Bearer YOUR_MGMT_API_ACCESS_TOKEN"]

let request = NSMutableURLRequest(url: NSURL(string: "https://YOUR_DOMAIN/api/v2/users/USER_ID")! as URL,
                                        cachePolicy: .useProtocolCachePolicy,
                                    timeoutInterval: 10.0)
request.httpMethod = "DELETE"
request.allHTTPHeaderFields = headers

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

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. 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.


curl --request PATCH \
  --url 'https://YOUR_DOMAIN/api/v2/users/USER_ID' \
  --header 'authorization: Bearer YOUR_MGMT_API_ACCESS_TOKEN' \
  --header 'content-type: application/json' \
  --data '{"app_metadata":{"deleted":true}}'
var client = new RestClient("https://YOUR_DOMAIN/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);
package main

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

func main() {

	url := "https://YOUR_DOMAIN/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))

}
HttpResponse<String> response = Unirest.patch("https://YOUR_DOMAIN/api/v2/users/USER_ID")
  .header("authorization", "Bearer YOUR_MGMT_API_ACCESS_TOKEN")
  .header("content-type", "application/json")
  .body("{\"app_metadata\":{\"deleted\":true}}")
  .asString();
var axios = require("axios").default;

var options = {
  method: 'PATCH',
  url: 'https://YOUR_DOMAIN/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);
});
#import <Foundation/Foundation.h>

NSDictionary *headers = @{ @"authorization": @"Bearer YOUR_MGMT_API_ACCESS_TOKEN",
                           @"content-type": @"application/json" };
NSDictionary *parameters = @{ @"app_metadata": @{ @"deleted": @YES } };

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

NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:@"https://YOUR_DOMAIN/api/v2/users/USER_ID"]
                                                       cachePolicy:NSURLRequestUseProtocolCachePolicy
                                                   timeoutInterval:10.0];
[request setHTTPMethod:@"PATCH"];
[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];
$curl = curl_init();

curl_setopt_array($curl, [
  CURLOPT_URL => "https://YOUR_DOMAIN/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;
}
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", "/YOUR_DOMAIN/api/v2/users/USER_ID", payload, headers)

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

print(data.decode("utf-8"))
require 'uri'
require 'net/http'
require 'openssl'

url = URI("https://YOUR_DOMAIN/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
import Foundation

let headers = [
  "authorization": "Bearer YOUR_MGMT_API_ACCESS_TOKEN",
  "content-type": "application/json"
]
let parameters = ["app_metadata": ["deleted": true]] as [String : Any]

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

let request = NSMutableURLRequest(url: NSURL(string: "https://YOUR_DOMAIN/api/v2/users/USER_ID")! as URL,
                                        cachePolicy: .useProtocolCachePolicy,
                                    timeoutInterval: 10.0)
request.httpMethod = "PATCH"
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()

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 and create a rule.

  2. Copy the script below:

    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