Google Apps Script: Store a Unique User Key from a User Accessing your WebApp

Google Apps Script Store Unique Key from users on your WebApp

Google Apps Script: WebApp

In this tutorial, we will cover how you can get a unique temporary access key from a user accessing your WebApp that lasts for 30 days.

Temporary access keys allow you to track users as they use your WebApp over time while still providing anonymity to the user by providing only an access key to that user. Rather than, say, use their name or email address.

Why is this important? Well, you might want to limit the number of times a user submits a form on your WebApp. If you can get a user’s access key unique to them then you can store the number of attempts by the user and check it before the data is submitted.

For example, in a previous post, we created a chain story that we might want to limit the number of times our users contribute to our story to once a day.

NOTE! This tutorial is pretty much standalone. However, it will require some basic knowledge of Google Apps Script WebApp and HTML. Don’t worry if some basic setup parts are not covered in this tutorial, I’ll link to how to do these bits if you need some more instruction.

Let’s take a look at what we are going to make:

The Example

Check out the Google Iframe WebApp below. NOTE! You will be required to sign into your Google Account for this WebApp to run.

In the above iframe of our Google Apps Script WebApp, you can see your unique temporary active user key that will be assigned to you for 30 days.

We’ve stored your key server-side along with the date and time that you first accessed the WebApp. We also make a record of the times you have accessed the WebApp. Either through accessing this tutorial page or directly through the URL. You can see this value changes if you refresh this page, or better yet click this link to the URL below:

https://script.google.com/macros/s/AKfycbxRb8orTieMYTz8X0MVmbsw64dpDwJL-qjQf8uy8JmO/dev

If you bookmark the link and come back to it in a few hours, or tomorrow, you will see that the time valid will change.

Here, take a look at mine. I first accessed the WebApp a little over a day ago:

Google Apps Script WebApp unique token generator
Click to Expand!

Let’s take a look at the code.

The Code

Before, I share the code. Let’s quickly go over how it all works.

First of all, this Web App needs to be deployed as:

  • Execute the app as: User accessing the web app
  • Who has access to the app: anyone
Google Apps Script WebApp executing as User for unique key
Click to Expand!

This will allow us to access the current user’s user key rather than our own if we had executed as the owner.

When the users open the WebApp, either directly or via an embedded iframe, code is activated server-side (Google Apps Script-side). A unique user key is retrieved.

The user key is then searched for inside Google Apps Script’s Properties Service. Each key is assigned an object of the following keys:

If the key exists, then the access frequency is increased by one and the current date-time is modified to the present. If the user does not exist, then we add the user as a property of the Properties Service, adding the date of the first time that they accessed this WebApp, set the frequency to one and the current date-time to the same as the first time it was accessed.

Next, we use this data to generate HTML that displays the unique ID, the number of times the user accessed the web app and use the start date to work out the remaining days until this current user key expires.

On a daily time-trigger set to a separate function the user can’t access, we check through each user key property each day. If the user’s key has a start date plus 30 days that is less than the current date, then that user will be removed from the Properties Service.

Index.html

Code.gs

Admin.gs

Code Breakdown

Index.html

The HTML file is pretty basic. There is a little bit of styling you can see at the top of the file just to make the space a little different from the white background. We then have a DIV we’ve cleverly labelled theResult.

In the code portion of our index file, we have two functions that are run depending on the outcome of our attempt to make a call and run our script server-side.

When the webpage opens, we want to immediately make a call to our getID function in our Code.gs file server-side. This is done with the google.script.run class. First, we will test for errors with withFailureHander and if there is an error the onFailure() function is called and an error is reported. If the code works, we run onSuccessHandler, which will call the server-side getID function and return the results to the Index.html result function. This, in turn, adds the data from getID into the DIV of the Index.html.

For a more detailed explanation of this process, check out this post:

Create a basic interactive Web App: Connecting with the server-side…safely.

Code.gs

doget()

A doGet or postGet function is a mandatory requirement to run a Google Apps Script web app. Inside this function we call on the HtmlService class to generate and deliver our Index.html file.

More on this here:

Create a basic interactive WebApp: doGet()

getID()

The getID() is called from the Index.html client-side when the webpage is first loaded. This is essentially our main working function that will call all our other task functions and finally return back our results client-side.

Get the users key and store it server-side

Our first task is to get our unique user key and save it to our Properties Service. We’ve put this process in a separate function called createUserProperty() (Line 12). This will return an object with the unique user key as our um…key and then a nested object containing the date of the first time the user accessed the web app with this unique key, how many times they have accessed the app and the date of the last time they accessed the app. It should look a little like this:

Next, we want to display the users long alphanumeric key. We can do this by accessing the zeroeth key in the object with the JavaScript Object.keys() method. Line 14

We will also need to get alls the properties of the key so we can do some calculations before sending them back to Index.html to be displayed. We’ll store this in the prop variable. Line 15

Get the number of days and hours remaining

Our next task is to get the number of days and hours remianing before our user’s current unique key expires. To do this we have created another function getDaysAndHoursRemianing() which takes 3 arguments:

  • The initial date: prop.date
  • The current date: prop.currentDate
  • The number of days before the key expires: 30 days.

This will then return an object containing the number of days and hours (hrs) remaning. Line 17

Getting our text set up.

Once we have our days and hours we want to put that in a statement. We’ll use two ternary operators to determine if there is a single day or hour we will use the singluar, ‘day’ and ‘hour’, or if there is more than one day or hour, we will add the plural, ‘s’. Lines 18-19

Next, we will grab our times accessed property and check how many times the user has accessed the text. If they have this is their first time, we’ll let them know. Otherwise, we will give them their current count. Line 21

Now that we have our plural and singluar conditions out of the way, we can put all our html text together into our textOutput string. We’ll use the template literal back ticks( ) so that we can easitly add in our variables (${variable}). Lines 25-29

Finally, we return the textOutput client-side to be displayed in the browser.

createUserProperty()

This function is the main event of this tutorial.

Creating a unique temporary user key

The createUserProperty() function first grabs a unique temporary user key. It does this by Google Apps Script’s Session class which provides access to the current session information, like the user’s email, the timezone the user is in and their language settings. But the important one for us is that we can get a temporary key for the user that won’t change each time the user logs into their Gmail account and then accesses this webapp.

The getTemporaryActiveUserKey() method gets a key for the currently active user without reveailing their identity. This key then lasts for this web app for 30 days before it expires. Line 15

For us, this allows us to store user data that we can collect later, say through a time trigger.

If a user already has acquired a key from running the code earlier, then that key will be returned and stored in the userSession variable.

Storing a record of our user

We need to be able to store our users records somewhere. We can do this with the Properties Service class. This class allows us to store simple key-value object pairs. You can choose how you want your properties service to be scoped:

  • To the Script: all properties are specifically accessible by the users of the script.
  • To the User: These are specifically to both the script being used, but also exclusive to the current user of the script.
  • To the Document: Were the document contains an active add-on. All users of the document can access the properties of this script.

Because we want to keep a record of all of our users in the script we will apply the getScriptProperties() method. Line 20

You can add, remove and view all or specific properties in your properties service.

A quick headsup – Some limitations

Properties Service is not designed to store large amounts of object data, although it’s not too bad. The current limitations on Properties Service is:

  • The value of each property must be less than 9 kilobytes. For reference, our data is barely a handful of bytes.
  • The total storage for each property store is 500 kilobytes. This means for this project script properties, we can’t have more than 500kb of property data stored. For another project, thats another 500kb.
  • For your enire account, there is a limit of 50,000 reads and writes per day. If you are getting traffic like this, you probably shouldn’t be using Google Apps Script. Have a look a Firebase or something.
Checking if we already have a record of the user

Next, we want to check if we already have a record of the user among our properties.

We titled each of our keys as the unique temporary user key of each user to help make this process easier. First, we call the Google Apps Script getKeys() method to get a list of keys in our Properties Service.

Then, we use the JavaScript includes method on our list of keys. The includes method takes a text item as a parameter. It iterates through each item in the array and checks if the item exists. If it does, then it will return the boolean, True. Otherwise, it will return false.

For us, we want to see if the current userSession exists in our list of keys. Line 21.

if(props.getKeys().includes(userSession)){

If the user’s key already exists, then we want to update the current key’s child keys of timesAccessed and currentDate. All data stored in a Properties Service value is a text. Specifically, for us, this is a JSON stringified text.

We need to return the values back into an object containing our date, timesAccessed and currentDate. We do this with the JSON.parse() method. The argument for this method is the value of our selected user’s property. We get this value with the getProperty method which takes a key as an argument. For us, this is the userSession. Line 22
const currentVals = JSON.parse(props.getProperty(userSession));

Next, we set up our new inner object in the userPropery variable we created earlier (Line 23). We reenter the date with the stored date variable (Line 24). Then we add one to the preivous timeAccessed to keep the count (Line 25). Finally, we update the current date (Line 26).

Adding a new user

If the user doesn’t exists, then we need to add them. We’ll update our userProperty again. First, we use the new Date() constructor to both the date and currentDate keys. Then we set the timeAccessed to one.

Creating and updating a property

Creating and updating a property is the same thing. If a property with the same key already exists, it will simply overwrite it. So this means that we can use the same method to update and add a property.

Before we add our property we need to convert it to a string. We do this with JSON stringify. Then we apply the setProperty() method to add it to our script properties. Lines 36-37

Finally, we return our updated or new property back to the getID() function. Line 39

getDaysAndHoursRemaining()

This function determines the number of days and hours remaining until the users unique key expires. It returns an object containing the days and hours remining:

The getDaysAndHoursRemaining() function has three parameters:

  • startDate: the date when the users first accessed the web app. This is stored in the date key of the user’s Properties Service property.
  • currendDate: The date that the script is currently being accessed.
  • expiryDays: The number of days that a user’s key will expire. For us, this is set to 30 days to match Google Apps Script max number of days the key will be available for.

First, we need to ensure the start and current dates are readable dates. We do this with the new Date() constructor. Lines 13-14

One line 16, we calculate our remaining days. Using the setDate method to set the day of the startDate object we want to add our expiryDays to our current date. Then we subtract this value from our current date. This will return a larger number set or remaining milliseconds.

Next, we work out the number of milliseconds in an hour and in a day and store them both in separate variables. Lines 18-19

Then we determine the number of days rounded down (Math.floor) by dividing the dateDifference by millisecondsPerDay. Line 21

We then want to get the leftover milliseconds to calculate the hours remaining. Line 23

Using the remaining milliseconds we use our millisedondsPerDay valuable to calculate the remaining hours, rounded down. Line 24

Finally, we put our days and hours remaining results into our object to be returned back to the getID() function.

Admin.gs

Perhaps unusually, we have added an Admin.gs Google Apps Script file. The scripts in this folder will not be used by the user or when the Index.html file loads. These will be independent functions.

I’m using this to highlight that you can use your own admin file or section of code to run independent processes and collect the properties data to use in these processes.

We will be using these functions to maintain and get data from our Properties Service.

clearExpiredTokens()

One of our main administrative aims for this project is to clear out any user whose unique key tokens has expired from our Properties Service.

The clearExpiredTokens() function iterates through each stored properties and checks the assigned start date plus 30 days. If this date is less than the current date, the user’s property will be deleted.

First, we call our PropertiesService. Specifically our script properties. Next, we want to grab all the properties that have been stored by our users accessing our WebApp. We do this with the getProperties method. Lines 15-16

We need to loop through all of our object properties to check if they have expired. This is done with our object-specific for-in loop. Line 18

We need to get the start date from each of our properties. Remember that in Properties Service all properties even if they are nested objects, must be strings. We used JSON.stringify on these to turn them into strings when they were added. So to make them objects again, we can use JSON.parse. Once we have the inner objects we can get the value in the date key. Line 19

Next, we want to create our expiry date we use the new Date constructor again and set the date plus thirty days. Line 20

Then, we get the current date and assign it to our currentDate variable.

Finally, we check to see if the expiry date is less than the current date. If it is, we record the property in our console for us to check later and then use the deleteProperty method to remove the property completely. Lines 25-29

Setting a trigger to our clearExpiredTokens() function

We don’t want to have to go in each day to run a check of the expired key tokens. Fortunately, we can assign a time trigger to our function. Here is one quick approach to do this:

Watch this video on YouTube.

Our other two little functions

We have two more functions the first one simply gets a list of all of the users who have accessed the WebApp and have tokens. It then logs them in the console for us to review. checkProperties()

The other function clears out all the properties in the script properties with the deleteAllProperties method. This is useful for preparing or updating our code or if we want to reset our list. Just keep in mind that it won’t reset the users key so their expiry count may be a little off. clearPropService()

Conclusion

Storing unique user keys from users accessing a WebApp with Google Apps Script can be a helpful tool for you to collect data on your users to use at another time. This could be used to collect statistics on your users or temporarily store their input for them to call on and edit later in your WebApp.

You might also store form data this way and then run a time trigger to later submit it to your Google Sheets. This way you don’t have to worry about sharing your Google Sheet’s edit permissions publically.

What do you think you would use this approach for? I really like to hear how people implement these tutorials in their own projects. So feel free to share in the comments below. If you like this tutorial give it a like so I know that I should be creating more of these types of tutorials and if you want to get regular updates when my posts come out, why not subscribe (top right sidebar)?
Looking to learn more about Google Apps Scripts in a more structured format? Udemy has some great courses that can get you from the basics to a real Google Apps Script pro!
Got a more specific problem you need help with, but don’t have the time to develop the skills? Fiverr’s your best bet to find a skilled Google Apps Script professional to solve your problem quickly and cheaply.
The above affiliate links have been carefully researched to get you to what you specifically need. If you decide to click on one of these links it will cost you just the same as going to the site. If you decide to sign up, I just get a little pocket money to help pay for the costs of running this website.

Leave a Reply