How to build your own Adobe GDPR interface

If you have a steady stream of GDPR requests that need processing, using the Privacy UI can quickly become pretty time consuming. Fortunately Adobe’s Privacy Service is also available via APIs, which got me thinking about what could be done to speed up a fairly time consuming manual process.

The end result of that thinking time was a pretty simple UI but one that has reduced the processing time by more than 50%. This article will give you an overview of the different component parts and the process of joining them all together.

#1 Design Overview

The screenshot at the top of the article is a bit of a giveaway but it’s worth quickly listing out the requirements at the beginning — partly because I didn’t actually do this when I built the UI, so I ended up taking a circuitous route to the finish line.

The requirements for the UI are that the user can:
i) specify whether it is an “Access” or a “Delete” request
ii) add a Ticket ID for internal records
iii) add one or more declared IDs (used as the custom key for each request)
iv) remove rows if the form input is incorrect
v) receive confirmation when the requests are registered successfully
vi) receive a daily email update on the status of jobs

#2 Create an Adobe IO integration

The first step is to set up an integration in Adobe IO, so that requests to the Privacy API can be made. There is plenty of documentation to help guide you through the integration setup but the main steps are:
i) choose which Adobe service you want to integrate with (in this case the Privacy Service API)
ii) provide a name & description for your integration
iii) generate public/private keys and add the public key in the drag and drop section
iv) click “create integration” to complete the process

By the end of this step you should end up with something that looks like this

#3 Create a json web token

Within the integration you’ve created, there is a JWT tab that will generate a token for you when you add the Private Key:

However, we want to be able to handle the generation of the jwt as part of the process that is triggered when a user submits their IDs, without needing to rely on manually generating a token in the Adobe IO interface.

This means we’ll need to create our own token, which is a fairly straightforward process, but there are plenty of resources if you want to read up on jwt further:
i) look here for more general information on json web tokens
ii) there’s a very useful guide about jwt authentication for Adobe IO service accounts (i.e. like the GDPR service account integration created above) here
iii) finally, there is some sample code for several different languages here, which covers how to generate a jwt token

I’m using Node.js to take care of the heavy-lifting involved behind the scenes, so my code to generate the jwt looks like this:

#4 Exchange jwt for an access token

Now that the jwt has been created, the next step is to exchange that token for an access token that can be used in your API requests. To complete this exchange you need to do the following:
i) Make a POST request to https://ims-na1.adobelogin.com/ims/exchange/jwt/
ii) Include jwt_token, client_id & client_secret as parameters in the request Note: the jwt_token key takes the value of the jwt created in step 3, and the client_id & client_secret values should be taken from the Adobe IO integration.

I’m making use of the Axios library for my API requests, so I end up with a token exchange request that looks like this:

Whilst the code above might look a bit odd in isolation, I’m using async/await syntax so that code further down the line (i.e. when I make the calls to the Privacy API) won’t start running until a response has been received from the token exchange request.

#5 Construct the API request

Now that all the formalities are complete, we can move on to actually making the requests to the Privacy API. I need to be able to handle multiple IDs, so the data entered into the UI will be sent to my Node.js app in an array format, which I can then use to create a structure that the Privacy API will understand:

If you’re not familiar with the different parameters that are included in a Privacy API request then have a look at the help documentation here.

For POST requests, the Axios library accepts 3 arguments — url, config & body — so my next step is to define the url & config that I will be using:

No major stumbling blocks here, the url is available in the API reference I linked above and you can get your x-api-key (aka client id) & x-gw-ims-org-id (aka Adobe Org ID) from the Adobe IO integration created earlier. Finally, I need to define the body of my request, which includes the users array I constructed at the start of this section:

Again, there may be some parameters here that don’t look too familiar, in which case just head to the help section to get a better understanding of what is needed and why.

#6 AWS Lambda setup

At this point I tested what I’d built so far on my local machine to make sure that I was getting a successful response from the Privacy Service. With everything looking good, I then zipped up my main index.js file, the node modules and the secret.pem private certificate, so that it could be uploaded to an AWS Lambda function.

I’m not going to go through how to get everything set up in AWS Lambda because there is plenty of documentation, which also includes some really useful examples, but I will point out one thing in relation to the callback in line 1 of the screenshot above. Whilst this might seem obvious, it took me a little while to realise that the Lambda function can only complete successfully if this callback is made.

Consequently, you need to trigger that callback with valid response data in all scenarios, whether that involves success or error. The response from my Lambda function contains the job ids that I need to inform the user of, so it’s fairly obvious if my function doesn’t complete successfully; however, even if you’re doing nothing with the response data, it’s still good practice to ensure there are no dead ends that will result in your function “failing” even though the API request is a success. Below is an example of the response data I’m including when I have a successful response from Privacy API request:

#7 UI Build

Now that all of the server-side plumbing is complete, it’s just a question of creating a UI that meets the requirements that I listed out in section #1. The only gotcha here, if you’re not too familiar with making these requests, is to make sure that you “stringify” the body of your request before sending it, otherwise it will fail.

I set up an Amazon API Gateway that links to my Lambda function, so when a user clicks “submit” I make a request to that gateway endpoint, and include the user input in the request body. As mentioned in section #6, I want to inform the user of the job id associated with each request, so I also added some functionality that displays this information in a table once the processing is complete:

Anyone familiar with making the Privacy requests will know that it usually takes at least a couple of weeks for the processing to complete, so the final piece of the puzzle was to find an efficient way of checking the status of the submitted requests. Fortunately, the steps to achieve this ended up being much more simple than I had imagined:
i) I created a new Lambda function that retrieves the status of all jobs in the last 30 days (note: a 30 day window is the max)
ii) the function applies filters to the job information to ensure that only valid Ticket IDs are included
iii) it then sends an e-mail with all the relevant status information, using the Nodemailer library
iv) using AWS Cloudwatch I set up a schedule that triggers the function (and therefore the request and subsequent e-mail send) at 10:00 every day

Hopefully you found this post interesting and perhaps it’s given you some ideas about how you could apply similar technology to solve problems or make your own processes more efficient. It’s worth mentioning that I chose the AWS route primarily because I’d already been looking into what it was capable of; however, I’m keen to create a similar UI using Adobe’s IO Runtime environment to see how that compares.