GDPR Compliance: 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.

Upon signup you have to ask your users for consent. With Auth0, you can save this information at the user metadata. There are several available options here, depending on how you use Auth0 to authenticate your users.

Before you design your solution using user_metadata make sure you have reviewed the custom fields limitations.

Lock with database connection

If you use Lock and authenticate users with a database connection, add an extra field to the signup screen using the additionalSignUpFields Lock option. This extra field will be automatically added to the user_metadata.

Alternatively, you can use the mustAcceptTerms Lock option. This, 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. Once the user accepts and signs up, save the consent information at the user_metadata using the additionalSignUpFields Lock option.

Lock with social login

If you use Lock and authenticate users with social providers, you cannot add a custom field to the signup screen, but you can redirect the user to another page where you ask for consent and then redirect back to finish the authentication transaction. Implement the redirection using redirect rules. Once you get the user's consent and the signup process is complete, save the consent information at the user_metadata by calling the Management API's Update User endpoint.

Custom UI with database connection

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. Afterwards, call the Authentication API's 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. For a sample request, refer to Custom Signup > Send the Form Data.

Alternatively, if you use Auth0.js, you can use the signup method in order to create the user in Auth0 and set the consent info as part of the user_metadata.

Custom UI with social login

If you use a custom signup form with social providers, you have to add an extra field to the signup screen in order to capture the user's consent. Save the consent information at the user_metadata by calling the Management API's Update User endpoint.

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.


What else do I have to do?

  • 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
  • You must determine if re-consent is required for your users, depending on your old terms and conditions and previous privacy certifications

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). Afterwards, you can access and manipilate this information using our Management API.

To access the Management API you will need an Access Token, for information on how to get one refer to the Auth0 Management API token.

The Management API offers several offers several options when it comes to user search (search by email, ID, or other fields) and endpoints to update user_metadata or batch export users.

Search for a user using their email address

To search for a user using their email address, use the Search user by email endpoint.

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_AUTH0_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_AUTH0_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_AUTH0_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_AUTH0_DOMAIN/api/v2/users-by-email?email=USER_EMAIL_ADDRESS&fields=user_metadata")
  .header("authorization", "Bearer YOUR_MGMT_API_ACCESS_TOKEN")
  .asString();
var settings = {
  "async": true,
  "crossDomain": true,
  "url": "https://YOUR_AUTH0_DOMAIN/api/v2/users-by-email?email=USER_EMAIL_ADDRESS&fields=user_metadata",
  "method": "GET",
  "headers": {
    "authorization": "Bearer YOUR_MGMT_API_ACCESS_TOKEN"
  }
}

$.ajax(settings).done(function (response) {
  console.log(response);
});
var request = require("request");

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

request(options, function (error, response, body) {
  if (error) throw new Error(error);

  console.log(body);
});
#import <Foundation/Foundation.h>

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

NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:@"https://YOUR_AUTH0_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, array(
  CURLOPT_URL => "https://YOUR_AUTH0_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 => array(
    "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_AUTH0_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'

url = URI("https://YOUR_AUTH0_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"]

var request = NSMutableURLRequest(URL: NSURL(string: "https://YOUR_AUTH0_DOMAIN/api/v2/users-by-email?email=USER_EMAIL_ADDRESS&fields=user_metadata")!,
                                        cachePolicy: .UseProtocolCachePolicy,
                                    timeoutInterval: 10.0)
request.HTTPMethod = "GET"
request.allHTTPHeaderFields = headers

let session = NSURLSession.sharedSession()
let dataTask = session.dataTaskWithRequest(request, completionHandler: { (data, response, error) -> Void in
  if (error != nil) {
    println(error)
  } else {
    let httpResponse = response as? NSHTTPURLResponse
    println(httpResponse)
  }
})

dataTask.resume()

Sample response:

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

Search for a user using their ID

To search for a user using their ID, use the Get a user endpoint.

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_AUTH0_DOMAIN/api/v2/users/YOUR_USER_ID?fields=user_metadata' \
  --header 'authorization: Bearer YOUR_MGMT_API_ACCESS_TOKEN'
var client = new RestClient("https://YOUR_AUTH0_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_AUTH0_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_AUTH0_DOMAIN/api/v2/users/YOUR_USER_ID?fields=user_metadata")
  .header("authorization", "Bearer YOUR_MGMT_API_ACCESS_TOKEN")
  .asString();
var settings = {
  "async": true,
  "crossDomain": true,
  "url": "https://YOUR_AUTH0_DOMAIN/api/v2/users/YOUR_USER_ID?fields=user_metadata",
  "method": "GET",
  "headers": {
    "authorization": "Bearer YOUR_MGMT_API_ACCESS_TOKEN"
  }
}

$.ajax(settings).done(function (response) {
  console.log(response);
});
var request = require("request");

var options = { method: 'GET',
  url: 'https://YOUR_AUTH0_DOMAIN/api/v2/users/YOUR_USER_ID',
  qs: { fields: 'user_metadata' },
  headers: { authorization: 'Bearer YOUR_MGMT_API_ACCESS_TOKEN' } };

request(options, function (error, response, body) {
  if (error) throw new Error(error);

  console.log(body);
});
#import <Foundation/Foundation.h>

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

NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:@"https://YOUR_AUTH0_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, array(
  CURLOPT_URL => "https://YOUR_AUTH0_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 => array(
    "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_AUTH0_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'

url = URI("https://YOUR_AUTH0_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"]

var request = NSMutableURLRequest(URL: NSURL(string: "https://YOUR_AUTH0_DOMAIN/api/v2/users/YOUR_USER_ID?fields=user_metadata")!,
                                        cachePolicy: .UseProtocolCachePolicy,
                                    timeoutInterval: 10.0)
request.HTTPMethod = "GET"
request.allHTTPHeaderFields = headers

let session = NSURLSession.sharedSession()
let dataTask = session.dataTaskWithRequest(request, completionHandler: { (data, response, error) -> Void in
  if (error != nil) {
    println(error)
  } else {
    let httpResponse = response as? NSHTTPURLResponse
    println(httpResponse)
  }
})

dataTask.resume()

Sample response:

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

Search for a set of users

To search for a set of users, use the List or search users endpoint.

This endpoint is eventually consistent (that is, the response might not reflect the results of a recently-complete write operation) and only specific fields are available for search.

Information regarding consent that is saved to user_metadata are not searchable.

For a sample request and response see Search Users. For more examples, see Example Queries.

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 a 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_AUTH0_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_AUTH0_DOMAIN/api/v2/users/USER_ID");
var request = new RestRequest(Method.PATCH);
request.AddHeader("content-type", "application/json");
request.AddHeader("authorization", "Bearer YOUR_MGMT_API_ACCESS_TOKEN");
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_AUTH0_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_AUTH0_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 settings = {
  "async": true,
  "crossDomain": true,
  "url": "https://YOUR_AUTH0_DOMAIN/api/v2/users/USER_ID",
  "method": "PATCH",
  "headers": {
    "authorization": "Bearer YOUR_MGMT_API_ACCESS_TOKEN",
    "content-type": "application/json"
  },
  "processData": false,
  "data": "{\"user_metadata\":{\"consentDate\":\"01/24/2018\"}}"
}

$.ajax(settings).done(function (response) {
  console.log(response);
});
var request = require("request");

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

request(options, function (error, response, body) {
  if (error) throw new Error(error);

  console.log(body);
});
#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_AUTH0_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, array(
  CURLOPT_URL => "https://YOUR_AUTH0_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 => array(
    "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_AUTH0_DOMAIN/api/v2/users/USER_ID", payload, headers)

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

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

url = URI("https://YOUR_AUTH0_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"]]

let postData = NSJSONSerialization.dataWithJSONObject(parameters, options: nil, error: nil)

var request = NSMutableURLRequest(URL: NSURL(string: "https://YOUR_AUTH0_DOMAIN/api/v2/users/USER_ID")!,
                                        cachePolicy: .UseProtocolCachePolicy,
                                    timeoutInterval: 10.0)
request.HTTPMethod = "PATCH"
request.allHTTPHeaderFields = headers
request.HTTPBody = postData

let session = NSURLSession.sharedSession()
let dataTask = session.dataTaskWithRequest(request, completionHandler: { (data, response, error) -> Void in
  if (error != nil) {
    println(error)
  } else {
    let httpResponse = response as? NSHTTPURLResponse
    println(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 an 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_AUTH0_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_AUTH0_DOMAIN/api/v2/users/USER_ID");
var request = new RestRequest(Method.PATCH);
request.AddHeader("content-type", "application/json");
request.AddHeader("authorization", "Bearer YOUR_MGMT_API_ACCESS_TOKEN");
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_AUTH0_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_AUTH0_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 settings = {
  "async": true,
  "crossDomain": true,
  "url": "https://YOUR_AUTH0_DOMAIN/api/v2/users/USER_ID",
  "method": "PATCH",
  "headers": {
    "authorization": "Bearer YOUR_MGMT_API_ACCESS_TOKEN",
    "content-type": "application/json"
  },
  "processData": false,
  "data": "{\"user_metadata\":{\"consent\": {\"given\":true, \"date\":\"01/23/2018\", \"text_details\":\"some-url\"}}}"
}

$.ajax(settings).done(function (response) {
  console.log(response);
});
var request = require("request");

var options = { method: 'PATCH',
  url: 'https://YOUR_AUTH0_DOMAIN/api/v2/users/USER_ID',
  headers: 
   { 'content-type': 'application/json',
     authorization: 'Bearer YOUR_MGMT_API_ACCESS_TOKEN' },
  body: { user_metadata: { consent: { given: true, date: '01/23/2018', text_details: 'some-url' } } },
  json: true };

request(options, function (error, response, body) {
  if (error) throw new Error(error);

  console.log(body);
});
#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_AUTH0_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, array(
  CURLOPT_URL => "https://YOUR_AUTH0_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 => array(
    "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_AUTH0_DOMAIN/api/v2/users/USER_ID", payload, headers)

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

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

url = URI("https://YOUR_AUTH0_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"
    ]]]

let postData = NSJSONSerialization.dataWithJSONObject(parameters, options: nil, error: nil)

var request = NSMutableURLRequest(URL: NSURL(string: "https://YOUR_AUTH0_DOMAIN/api/v2/users/USER_ID")!,
                                        cachePolicy: .UseProtocolCachePolicy,
                                    timeoutInterval: 10.0)
request.HTTPMethod = "PATCH"
request.allHTTPHeaderFields = headers
request.HTTPBody = postData

let session = NSURLSession.sharedSession()
let dataTask = session.dataTaskWithRequest(request, completionHandler: { (data, response, error) -> Void in
  if (error != nil) {
    println(error)
  } else {
    let httpResponse = response as? NSHTTPURLResponse
    println(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 endpoint.

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 a Management API token, you are ready to start exporting users. For a sample request and response see User Export.


What else do I have to do?

  • 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 users decides to withdraw their consent, you should take action.

First, you have to decide how you will handle 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_AUTH0_DOMAIN/api/v2/users/USER_ID' \
  --header 'authorization: Bearer YOUR_MGMT_API_ACCESS_TOKEN'
var client = new RestClient("https://YOUR_AUTH0_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_AUTH0_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_AUTH0_DOMAIN/api/v2/users/USER_ID")
  .header("authorization", "Bearer YOUR_MGMT_API_ACCESS_TOKEN")
  .asString();
var settings = {
  "async": true,
  "crossDomain": true,
  "url": "https://YOUR_AUTH0_DOMAIN/api/v2/users/USER_ID",
  "method": "DELETE",
  "headers": {
    "authorization": "Bearer YOUR_MGMT_API_ACCESS_TOKEN"
  }
}

$.ajax(settings).done(function (response) {
  console.log(response);
});
var request = require("request");

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

request(options, function (error, response, body) {
  if (error) throw new Error(error);

  console.log(body);
});
#import <Foundation/Foundation.h>

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

NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:@"https://YOUR_AUTH0_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, array(
  CURLOPT_URL => "https://YOUR_AUTH0_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 => array(
    "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_AUTH0_DOMAIN/api/v2/users/USER_ID", headers=headers)

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

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

url = URI("https://YOUR_AUTH0_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"]

var request = NSMutableURLRequest(URL: NSURL(string: "https://YOUR_AUTH0_DOMAIN/api/v2/users/USER_ID")!,
                                        cachePolicy: .UseProtocolCachePolicy,
                                    timeoutInterval: 10.0)
request.HTTPMethod = "DELETE"
request.allHTTPHeaderFields = headers

let session = NSURLSession.sharedSession()
let dataTask = session.dataTaskWithRequest(request, completionHandler: { (data, response, error) -> Void in
  if (error != nil) {
    println(error)
  } else {
    let httpResponse = response as? NSHTTPURLResponse
    println(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: Update a user). 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.

Step 1: 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_AUTH0_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_AUTH0_DOMAIN/api/v2/users/USER_ID");
var request = new RestRequest(Method.PATCH);
request.AddHeader("content-type", "application/json");
request.AddHeader("authorization", "Bearer YOUR_MGMT_API_ACCESS_TOKEN");
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_AUTH0_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_AUTH0_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 settings = {
  "async": true,
  "crossDomain": true,
  "url": "https://YOUR_AUTH0_DOMAIN/api/v2/users/USER_ID",
  "method": "PATCH",
  "headers": {
    "authorization": "Bearer YOUR_MGMT_API_ACCESS_TOKEN",
    "content-type": "application/json"
  },
  "processData": false,
  "data": "{\"app_metadata\":{\"deleted\":true}}"
}

$.ajax(settings).done(function (response) {
  console.log(response);
});
var request = require("request");

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

request(options, function (error, response, body) {
  if (error) throw new Error(error);

  console.log(body);
});
#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_AUTH0_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, array(
  CURLOPT_URL => "https://YOUR_AUTH0_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 => array(
    "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_AUTH0_DOMAIN/api/v2/users/USER_ID", payload, headers)

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

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

url = URI("https://YOUR_AUTH0_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]]

let postData = NSJSONSerialization.dataWithJSONObject(parameters, options: nil, error: nil)

var request = NSMutableURLRequest(URL: NSURL(string: "https://YOUR_AUTH0_DOMAIN/api/v2/users/USER_ID")!,
                                        cachePolicy: .UseProtocolCachePolicy,
                                    timeoutInterval: 10.0)
request.HTTPMethod = "PATCH"
request.allHTTPHeaderFields = headers
request.HTTPBody = postData

let session = NSURLSession.sharedSession()
let dataTask = session.dataTaskWithRequest(request, completionHandler: { (data, response, error) -> Void in
  if (error != nil) {
    println(error)
  } else {
    let httpResponse = response as? NSHTTPURLResponse
    println(httpResponse)
  }
})

dataTask.resume()

Step 2: Disable login for flagged users

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

Go to Dashboard > Rules and create a new rule. Copy the script you see 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:

  • Checks the value of the deleted metadata property (user.app_metadata.deleted)
  • Returns an Access denied (deleted user) error to your app if user.app_metadata.deleted = true

Give a name to your rule and save your changes.


What else do I have to do?

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

Auth0 Features and GDPR Compliance
Go back

Was this article helpful?