GDPR: Track Consent with Lock

In this tutorial we will see how you can use Lock to ask for consent information, and then save this input at the user's metadata.

The contents of this document 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.

Overview

We will configure a simple JavaScript Single Page Application and a database connection (we will use Auth0's infrastructure, instead of setting up our own database).

Instead of building an app from scratch, we will use Auth0's JavaScript Quickstart sample. We will also use Auth0's Universal Login Page so we can implement a Universal Login experience, instead of embedding the login in our app.

We will capture consent information, under various scenarios, and save this at the user's metadata.

All scenarios will save the following properties at the user's metadata:

  • a consentGiven property, with true/false values, shows if the user has provided consent (true) or not (false)
  • a consentTimestamp property, holding the Unix timestamp of when the user provided consent

For example:

{
  "consentGiven": "true"
  "consentTimestamp": "1525101183"
}

We will see three different implementations for this:

  • one that displays links to other pages where the Terms & Conditions and/or privacy policy information can be reviewed
  • one that adds custom fields at the signup widget and works for database connections
  • one that redirects to another page where the user can provide consent, and works for social connections

Configure the application

  1. Go to Dashboard > Applications and create a new application. Choose Single Web Page Applications as type.

  2. Go to Settings and set the Allowed Callback URLs to http://localhost:3000.

    This field holds the set of URLs to which Auth0 is allowed to redirect the users after they authenticate. Our sample app will run at http://localhost:3000 hence we set this value.

  3. Copy the Client Id and Domain values. You will need them in a while.

  4. Go to Dashboard > Connections > Database and create a new connection. Click Create DB Connection, set a name for the new connection, and click Save. You can also enable a social connection at Dashboard > Connections > Social (we will enable Google login for the purposes of this tutorial).

  5. Go to the connection's Applications tab and make sure your newly created application is enabled.

  6. Download the JavaScript SPA Sample.

  7. Set the Client ID and Domain values.

In this section, we will customize the login widget to add a flag which users must check in order to sign up. The flag's label will include links to pages that display the Terms & Conditions and privacy policy.

This works both for database connections and social logins.

  1. Go to Dashboard > Hosted Pages. At the Login tab enable the toggle.

  2. At the Default Templates dropdown make sure that Lock is picked. The code is prepopulated for you.

  3. To add a field for the consentGiven metadata, use the mustAcceptTerms option. To include links to your Terms & Conditions and/or privacy policy pages, use the languageDictionary option. The example below, displays next to the flag the text I agree to the terms of service and privacy policy (including links to both pages).

    //code reducted for simplicity
    var lock = new Auth0Lock(config.clientID, config.auth0Domain, {
      auth: {
        //code reducted for simplicity
      },
      languageDictionary: {
        signUpTerms: "I agree to the <a href='https://my-app-url.com/terms' target='_new'>terms of service</a> and <a href='https://my-app-url.com/privacy' target='_new'>privacy policy</a>."
      },
      mustAcceptTerms: true,
      //code reducted for simplicity
    });
    
  4. To see what this will look like, click the Preview tab, and when Lock loads select the Sign Up tab.

    Preview Lock with Terms & Conditions flag

  5. This flag does not allow users to sign up unless they accept the terms, however it does not set any metadata. To save this information at the consentGiven metadata property, add a rule. Go to Dashboard > Rules and click Create Rule. At the Rules Templates select empty rule. Change the default rule's name (empty rule) to something descriptive, for example Set consent flag upon signup.

  6. Add the following JavaScript code and save your changes. The code sets the consentGiven metadata to true, if it is not already set (which means it's the first login after a signup).

    function (user, context, callback) {
      user.user_metadata = user.user_metadata || {};
      // short-circuit if the user signed up already
      if (user.user_metadata.consentGiven) return callback(null, user, context);
      
      // first time login/signup
      user.user_metadata.consentGiven = true;
      user.user_metadata.consentTimestamp = Date.now();
      auth0.users.updateUserMetadata(user.user_id, user.user_metadata)
      .then(function(){
        callback(null, user, context);
      })
      .catch(function(err){
        callback(err);
      });
    }
    

Option 2: Add custom fields for database connections

In this section, we will customize the login widget to add a flag which users will check if they agree to the processing of their information.

This works only for database connections (if you use social logins, see the next paragraph).

  1. Go to Dashboard > Hosted Pages. At the Login tab enable the toggle.

  2. At the Default Templates dropdown make sure that Lock is picked. The code is prepopulated for you.

  3. To add a field for the consentGiven metadata, use the additionalSignUpFields option. The example below, sets the type to checkbox (so we have a flag), the label to I consent to data processing, and the default value to checked.

    //code reducted for simplicity
    var lock = new Auth0Lock(config.clientID, config.auth0Domain, {
      auth: {
        //code reducted for simplicity
      },
      additionalSignUpFields: [{
        type: "checkbox",
        name: "consentGiven",
        prefill: "true",
        placeholder: "I consent to data processing"
      }],
      //code reducted for simplicity
    });
    
  4. To see what this will look like, click the Preview tab, and when Lock loads select the Sign Up tab.

    Preview Lock with consent flag

Note that in this option we only set the flag and not the timestamp. Displaying the current time in the login widget is not optimal, that's why we didn't add an additional signup field. What you should do is set the timestamp in the background, with a rule that will check the value of consentGiven and set the additional consentTimestamp metadata to the current timestamp.

Option 3: Redirect to another page

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. We will use that same rule to save the consent information at the user's metadata so we can track this information and not ask for consent upon next login.

For simplicity, we will use this sample consent form. This is a form we have hosted for you using a webtask, but later on we will see how to host your own version of this form (with your own URL). You can find the webtask's code at Auth0 Redirect Rules repo.

If you are implementing this from a regular web app, hosting your own form, then you can also save the consent information at the user_metadata using the Management API's Update User endpoint.

  1. First, we will add the rule. Go to Dashboard > Rules and click Create Rule. At the Rules Templates select empty rule. Change the default rule's name (empty rule) to something descriptive, for example Redirect to consent form.

  2. Add the following JavaScript code and save your changes. The code redirects the user to the CONSENT_FORM_URL URL (we will configure this at the next step). Once the user hits Submit at the consent form, the rule runs again as part of the callback. At this point we persist the information at the user_metadata.

    function redirectToConsentForm (user, context, callback) {
      var consentGiven = user.user_metadata && user.user_metadata.consentGiven;
    
      // redirect to consent form if user has not yet consented
      if (!consentGiven && context.protocol !== 'redirect-callback') {
        var auth0Domain = auth0.baseUrl.match(/([^:]*:\/\/)?([^\/]+\.[^\/]+)/)[2];
    
        context.redirect = {
          url: configuration.CONSENT_FORM_URL +
            (configuration.CONSENT_FORM_URL.indexOf('?') === -1 ? '?' : '&') +
            'auth0_domain=' + encodeURIComponent(auth0Domain)
        };
      }
    
      // if user clicked 'I agree' on the consent form, persist it to their profile
      // so they don't get prompted again
      if (context.protocol === 'redirect-callback') {
        if (context.request.body.confirm === 'yes') {
          user.user_metadata = user.user_metadata || {};
          user.user_metadata.consentGiven = true;
          user.user_metadata.consentTimestamp = Date.now();
    
          auth0.users.updateUserMetadata(user.user_id, user.user_metadata)
            .then(function(){
              callback(null, user, context);
            })
            .catch(function(err){
              callback(err);
            });
        } else {
          callback(new UnauthorizedError('User did not consent!'));
        }
      }
    
      callback(null, user, context);
    }
    
  3. Go back to Dashboard > Rules, scroll down, and under Settings, create a Key/Value pair as follows:

  • Key: CONSENT_FORM_URL
  • Value: https://wt-peter-auth0_com-0.run.webtask.io/simple-redirect-rule-consent-form

If you want to work with your own implementation of the consent form webtask, you can host your own version of the webtask.js script. For instructions see Consent Form Setup.

To learn more about redirect rules, see Redirect Users from Rules.

If you plan on using this approach in a production environment, make sure to review Trusted Callback URL's and Data Integrity (both sections address some security concerns).

We are done with the configuration part, let's test!

Test the configuration

  1. Go to the folder where you downloaded the application and run it.

    npm install
    npm run
    
  2. Go to http://localhost:3000. Click Login. Once Lock is displayed, click Sign Up.

    The login page will be served by default at YOUR_AUTH0_DOMAIN/login. If you want to use your own domain, see Custom Domains.

  3. If you followed the first implementation option, you should see the flag to accept the terms of service and privacy policy. Note that the Sign up button remains disabled until you check the flag. Follow the links to check they are working. Set an email and password and accept the terms and click Sign Up. Alternatively, if you use a social connection, accept the terms and choose Sign Up with Google.

    Application Sign Up widget

  4. If you followed the second implementation option, you should see the new custom field we added. Set an email and password and leave the I consent to data processing flag checked. Click Sign Up.

    Application Sign Up widget

  5. If you followed the third implementation option, choose Sign Up with Google. You will be navigated to the consent form. Check the I agree flag and click Submit.

    Application Sign Up widget

    User did not consent

    If you do not check the I agree flag before clicking Submit, then you will see a popup error Unauthorized. Check the console for details.. At the console you will see this JSON:

    {
      error: "unauthorized", 
      errorDescription: "User did not consent!", 
      state: "q0GjMwzZN_q5r8XPHvfakkMYcYM2q1N3"
    }
    

    Note, that the user is created but they won't be able to log in. If they try to, they will be prompted again to provide consent.

  6. Go to Dashboard > Users and search for the new user.

  7. Go to User Details and scroll down to the Metadata section. At the user_metadata text area you should see the following:

    {
      "consentGiven": "true"
      "consentTimestamp": "1525101183"
    }
    

That's it, you are done!