Add User Attributes to Deny List

If there are user fields that should not be stored in Auth0 databases due to privacy reasons, you can add them to the Deny List. To add attributes to the Deny List, make a PATCH call to the Update Connection endpoint of the Management API.

  1. Get a valid access token to access the /patch_connections_by_id endpoint. The token must include the update:connections scope. See Management API Access Tokens for details.

  2. With the access token and the list of attributes to be denied, call the API. Here is an example HTTP request that denies two attributes: ethnicity and gender. Keep in mind you need to retrieve the options object and send the whole object in your PATCH request as there is no "merge" when you only update one or two values.

    
    
    curl --request PATCH \
      --url 'https://YOUR_DOMAIN/api/v2/connections/YOUR_CONNECTION_ID' \
      --header 'authorization: Bearer YOUR_TOKEN' \
      --header 'content-type: application/json' \
      --data '{"options": {"non_persistent_attrs": ["ethnicity", "gender"]}}'
    var client = new RestClient("https://YOUR_DOMAIN/api/v2/connections/YOUR_CONNECTION_ID");
    var request = new RestRequest(Method.PATCH);
    request.AddHeader("authorization", "Bearer YOUR_TOKEN");
    request.AddHeader("content-type", "application/json");
    request.AddParameter("application/json", "{\"options\": {\"non_persistent_attrs\": [\"ethnicity\", \"gender\"]}}", ParameterType.RequestBody);
    IRestResponse response = client.Execute(request);
    package main
    
    import (
    	"fmt"
    	"strings"
    	"net/http"
    	"io/ioutil"
    )
    
    func main() {
    
    	url := "https://YOUR_DOMAIN/api/v2/connections/YOUR_CONNECTION_ID"
    
    	payload := strings.NewReader("{\"options\": {\"non_persistent_attrs\": [\"ethnicity\", \"gender\"]}}")
    
    	req, _ := http.NewRequest("PATCH", url, payload)
    
    	req.Header.Add("authorization", "Bearer YOUR_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/connections/YOUR_CONNECTION_ID")
      .header("authorization", "Bearer YOUR_TOKEN")
      .header("content-type", "application/json")
      .body("{\"options\": {\"non_persistent_attrs\": [\"ethnicity\", \"gender\"]}}")
      .asString();
    var axios = require("axios").default;
    
    var options = {
      method: 'PATCH',
      url: 'https://YOUR_DOMAIN/api/v2/connections/YOUR_CONNECTION_ID',
      headers: {authorization: 'Bearer YOUR_TOKEN', 'content-type': 'application/json'},
      data: {options: {non_persistent_attrs: ['ethnicity', 'gender']}}
    };
    
    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_TOKEN",
                               @"content-type": @"application/json" };
    NSDictionary *parameters = @{ @"options": @{ @"non_persistent_attrs": @[ @"ethnicity", @"gender" ] } };
    
    NSData *postData = [NSJSONSerialization dataWithJSONObject:parameters options:0 error:nil];
    
    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:@"https://YOUR_DOMAIN/api/v2/connections/YOUR_CONNECTION_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/connections/YOUR_CONNECTION_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 => "{\"options\": {\"non_persistent_attrs\": [\"ethnicity\", \"gender\"]}}",
      CURLOPT_HTTPHEADER => [
        "authorization: Bearer YOUR_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 = "{\"options\": {\"non_persistent_attrs\": [\"ethnicity\", \"gender\"]}}"
    
    headers = {
        'authorization': "Bearer YOUR_TOKEN",
        'content-type': "application/json"
        }
    
    conn.request("PATCH", "/YOUR_DOMAIN/api/v2/connections/YOUR_CONNECTION_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/connections/YOUR_CONNECTION_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_TOKEN'
    request["content-type"] = 'application/json'
    request.body = "{\"options\": {\"non_persistent_attrs\": [\"ethnicity\", \"gender\"]}}"
    
    response = http.request(request)
    puts response.read_body
    import Foundation
    
    let headers = [
      "authorization": "Bearer YOUR_TOKEN",
      "content-type": "application/json"
    ]
    let parameters = ["options": ["non_persistent_attrs": ["ethnicity", "gender"]]] as [String : Any]
    
    let postData = JSONSerialization.data(withJSONObject: parameters, options: [])
    
    let request = NSMutableURLRequest(url: NSURL(string: "https://YOUR_DOMAIN/api/v2/connections/YOUR_CONNECTION_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()
    Where:

    1. YOUR_CONNECTION_ID is the connection ID for which these attributes will be denied.

    2. YOUR_TOKEN is the access token you received in the previous step.

    3. options.non_persistent_attrs object holds an array of the attributes that will be denied. If the claim that you want to deny is being sent by an upstream Identity Provider (IdP), you should set the claim exactly as sent by the upstream IdP. For example, for a claim received as https://acme.com/temporary_idtoken, the above sample non_persistent_attrs object would read:

      {"non_persistent_attrs": ["ethnicity", "gender", "https://acme.com/temporary_idtoken"]}
      

Limitations

  • Only root fields (such as user.name or user.phone_number) can be denied.

    • If user.name or user.nickname are denied, they will not be included in tokens.

    • If user.email is denied, the value cannot be mapped to a custom claim. For example, in a rule, context.idToken[namespace + 'work_email'] = user.email would not work.

  • When you deny attributes, they will still be available via rules and outgoing tokens. However, if any of the following apply, the Deny List attributes will not be included in tokens:

    • You have enabled multi-factor authentication (MFA)

    • You have performed a redirect via rules

    • Your app is using delegation (and you haven't set scope = passthrough)

    • Your app is using impersonation

    • You have enabled the Use Auth0 instead of the IdP to do Single Sign-On setting (legacy tenants only)

  • For SAMLP connections, if you enable Debug mode, your logs will contain information on the Deny List attributes

If any of these limitations are unacceptable, you can write a rule to encrypt the data and have the data persist to the user.app_metadata object.

Learn more