Tuesday, January 19, 2016

Forget The Password

How to built an account system where users don't need a password to login.



Is there a better way than asking the to-be-signed in user for their email and password? In theory, sure, it seams quite safe. But does it really doesn't work all that well? How about the increasing number of passwords the people have to remember, since it's not safe to use the same password for each application. Think about all the websites you have an account with, if you are like most, that’s quite a few. For each of these you have to enter a password and for the ones you are always logged into, well... Things get especially bad if your computer itself is not protected by any sort of security; people can freely get into your account without anything stopping them, well, besides morals I guess. Furthermore, many people use the same password for each website and as you probably have guessed, thats just one better then morality.


So what is safe? 


There are a few safer options, but as with all security mechanisms, there are vulnerabilities that need accounting for.



Foremost, here is how it works step-by-step. 

A person enters their email address > A token is created with the temporary password  > The to-be-signed in user gets send an email with a link > After the link in the email is clicked, they are sent to the website with the temporary token ID attached in the link URL > website recognizes the ID and accesses the database to retrieve the key and then combines that with the email > User gets logged in > Finally, that token becomes expired and a new key becomes attached to the user so the used one cannot be used again.

Alternatively you can delete the token after it is used, however I have decided to keep the expired ones so that we can track if a previously used token has been tried again. Also while a user is trying to log in, two keys are generated, not just one. While they try to log in, and after they successfully login. The second one is a safety precaution so if the generated key is intercepted, it cannot be used again as it doesn't match the user's current login key.


I just recently discovered a code-less application builder and as with everything new, I like to test the potential of the platform to understand it's limitations. The subject of this experiment was Bubble. So far, limitations are present, but over the course of experimenting with this system, many of the walls I hit have disappeared. Many still exist, but the rate of improvements are astounding.


Building it with Bubble

Disclaimer: The following content assumes some knowledge of Bubble, however if you follow the steps exactly, you should end up with the same result. Try their interactive tutorials to get a basic understanding of building with Bubble. I am also assuming that we are starting with a blank application, so adapt the processes I put forth to your own situation if you are trying to implement them into an existing application. Also note that the following is going to be about creating the sign in system, not the creating account system.



Setting up the database

We are going to need to set up a database that will store the temporary key and email to be accessed later. So create a custom datatype called ‘AuthKey’ in Bubble, you can obviously call it what ever you want to. Within the AuthKey datatype, create three fields. One called ‘email’ and one called ‘tempKey’. Set both to text type, as only strings will be stored in it. We are going to need to track if this key was used or not, so lastly create a field named ‘expired?’ with the type of ‘yes / no’. A way for me to know if it is a ‘yes / no’ value without looking up its type, is to add a question mark at the end of the name. I’ll talk later on an alternative method where we don’t store the email with the key.

The tempKey will hold the randomly generated key that was attached to the user while logging in. I’ll show you how to retrieve the key after it is generated for the user, so we can capture it and store it in the database temporarily for later use.



Getting the user input

To log the user in, we need their email. So add an input field into the web page area. Set the parameter to ‘Email’. You can add something like ‘Please enter your email’ instead, but that just sounds too needy. Then set the content format to ‘email’ and to have ‘This input should not be empty’ checked. This function will not allow the button to trigger a workflow unless there is content in the input field. The reason I like to use the ‘email’ format is because it will verify if it an email address or not, so  just any statement will not be allowed in the field. Now add a button that says “Login”, as that will be the trigger to our workflow.



Creating the temporary key generator

Now that we have their email, we are going to have to do something with it. To give the button a higher and better purpose, we are going to call a workflow when the button is clicked, but only after an email has been entered in the input field.

Now start a workflow for the button. The first step in the workflow is going to be assigning the temporary password to the user that matches up with the email entered. The action we are going to call is “Assign a temp password to a user” which is under the Account tab in the actions popover. This action needs to know the user that it is generating a temporary password for, so we are going to ask it to do a search for a user. Then add a constraint to only look for users with the same email as the one entered in the input. Now it will look through the database for users that have the same email. However, their should only be one user with that email, so add :’first item’ to the ‘Search for Users’ argument. This will satisfy the action’s need to pinpoint only one user. Being that only one user will have that email, it’s a safe bet that the first user the action is going to see will also be the user we want.

If you want the user to login using a username instead, make a field under the User type called ‘username. Now have the action search for users with the same username from the input field, then add the ‘:first item’ to pinpoint the user. This will allow people to login into their account without using their email. Being that we now know the user from their username, we can access their email by doing a search for users that have that username. We then ask for the email address from the result of that username search. Make sure that multiple users cannot have the same username though. This will not allow us to pinpoint the correct user.






Creating a test user 

We can have the system working all fine, but how would we know without testing it? To create a test user account, go under the ‘App Data’ tab and make sure you are viewing the list of users. Create a new entry, then select User as the ‘Type of thing’. An email field will appear, now we enter an email address that we are going to use to test it. Make sure it is an email address you can retrieve emails from, as we are going to need to send ourselves emails to make sure the tokens are working correctly. Then confirm that; this should successfully create a test user. After creating a user, you may not see it displayed in the database just yet. Reload the page, and you should see it then, if not, you may have to create it again.



Storing the authentication key in the database

To store ‘things’, that's what Bubble refer to as stored or structured data, we are going to want to use the action ‘Create a new thing…’ under the 'Data' tab and set the type to AuthKey. As this will create a new AuthKey we will be retrieving later on. Add a field for email that will get the value from the email input. Add another field for the 'tempKey', which you want to get the result of step 1 or from the ‘Assign a temp password to a user’ action. This will take the key that was generated, and put that into the ‘tempKey’ field. Finally, add the ‘expired?’ field that we will use to track if the token has been used before. We can also ask if the token is expired, because we might want to change visual elements in the future to let the attempting user know that the token is invalid. You will want to set the value of ‘expired’ to “No”. As the token is currently innocent and pure.



Sending the verification email to the user

The third step in our workflow is going to be the action ‘Send Email’. We are going to be sending the unique ID in the email itself. In an early iteration of this system, I had the actual password itself sent in the email. Then when the user clicks through to our site, the site would just take the key that was stored in the URL parameter and enter that in with the email to login. It was a crude and I can’t image that secure of a system, but I digress. The ‘Send Email’ action requires a few components. One being the ‘To’ argument, which is the email address that this email will be sent to. We are going to want the same email address as before, so to satisfy the ‘To’ argument we need to pass it the input you made to get their email address. Alternatively, we can retrieve the email address from when a new AuthKey was created, but it seems unnecessary for this instance. For now we do not need to specify a different address to reply to, since there is no need to reply to any of these emails we are sending. The ‘Sender name’ can be whatever you want and the ‘Subject’ should be something that explains the action the user will be taking to login, so like “Sign into your account” or “Your login key” will work fine. 

If you don’t already know, ‘Cc’ will create a carbon copy of the email and send it to the signified email address. ‘Bcc’ does the same as the ‘Cc’, but recipients of the carbon copied email will not know of the recipients of ‘Bcc’ emails. So you can send a copy of the email to the support team, and the person you are sending the message to originally will not be able to know that. However, if would be “safer” to just create a another originating email, because if the ‘Bcc’ed’ recipient replies to all parties, they will become known. For this instance, we will not be needing this feature, so you can leave those two fields blank. 

The body of the message is where a lot of the fun is going to be. It is where we will store the unique ID of the AuthKey in a button URL link. This link will then send the user to our web page that we will make shortly. 

To do this, we are going to write a URL tag in the body of the email that will link the user to our web page, which will be created in the next section of this article. 

Write whatever gubbins you like, explaining why they should click the following button to log them in. Now we write [url=http://bubble.is/site/(the name you called the app)/version-test/logintoken?token=(AuthKey unique ID)]Continue[/url] under the gubbins you wrote. This link will work on the published version of the site, but it will always direct them to the version-test version of your site while trying to login. So you want to remove the /version-test from the link when you want to publish it. I added an image of my body, I mean the text body for the email.

Learn more about the newer domain structure Bubble uses here.

Different email apps will treat links with redirects differently, or in some apps, the links will not work at all for various reasons. So we should add the URL below in addition to the button, just in case. You do not have to do this, it is completely up to you.

A good habit to have is to reset the input after the user submits data, so add ‘Reset relevant inputs’ from the ‘Element Actions’ in the workflow. Again, you do not have to do this, it is completely up to you.



Setting up the login token page

This is where the website is going to interpret the incoming unique ID and email; to log the user in. Create a new web page in Bubble and name it “logintoken”, just name it that, I won’t allow creative freedom here.  

We are now going to make another workflow trigger when the ‘logintoken’ page is loaded. After the user clicks the button in the email and lands on this page, we will want to cause an event that will log the user in. Add a new workflow for when the ‘logintoken’ page is loaded. In this new workflow, add the “Log the user in” action under the ‘account’ tab. This action requires a password and email to complete the action, which is where we are going to pass it the AuthKey data. To satisfy the ‘Log the user in’ action we are first going to ask the ‘Email’ field to do a search for AuthKeys, then filter it so it only looks for AuthKeys in our database with the unique ID that have the same unique ID that was given in the URL ‘token’ parameter. The action still will not be satisfy yet, as it is needy. So we will ask it to return us the first item that it finds. Of course, it is still not satisfied, so we also need to tell it that we want the email from the item it finds. 



The ‘Password’ field is pretty much the same story as the email field, except it wants to be passed the ‘tempKey’, well I don't know if it wants to be, but it will have to like it. Make sure ‘Stay logged in’ is set to ‘true’, because it would defeat the whole purpose of this login page otherwise. The ‘Remember the email’ component if set to ‘yes’, it will store a cookie in the user’s browser to fill in the email input field next time they go to login. It could be a convenience depending on what you are making, so consider the option.

Before we continue, we want to make sure that a miscreant won’t use an expired AuthKey. So on the ‘When Page is loaded’ event, make the ‘and when’ argument search for AuthKeys. Then filter is to only return AuthKeys that match the URL parameter ‘token’. The URL should contain the unique ID of the AuthKey being attempted, so this will look for if it has been expired first before continuing the login process. Have it pinpoint the first item, then ask if the first item’s expired? is “no”. This will then only accept AuthKey’s that are not expired.

For the second workflow step, we are going to expire the just used AuthKey, since we just used it the step before to log the user in. We are going to use the ‘Make changes to thing…’ action under the ‘Data (Things)’ tab. For this action it wants to know what it is going to be changing, so to satisfy that, we are going to ask it to search for AuthKeys, then filter it like before and then pinpoint that first item. We won’t need to add anything after asking for the first item, as it is only looking for the AuthKey and not a specific piece of data from the AuthKey. You can leave ‘Create if the thing doesn’t exist’ unchecked, because we wouldn't want to just create a new AuthKey after they are logged in and then expire it now would we? Add a value below for ‘expired?’ and have it equal “yes”.

As a safety measure, it would be best if we generated a new temporary password for the just logged in user. So even if the key’s raw info was visible to that hoodlum, the password for the user would of just changed again, making their information irrelevant. For the third workflow step, we add “Assign a temp password to a user” action which we will pass the current user. Since the user logged in on the first step, the ‘Assign a temp password to a user’ action is called after it, so by the time the browser gets to doing this step, the user is already logged in. That being said, while you are working on workflows, be it in Bubble or normal programming, you will want to watch your step.

We have one more workflow step left, but before we do that we are going to need to make another page for the user’s account. 



Making the user’s account page

Create a new page in Bubble and name it “account”, this will be the page the ‘logintoken’ redirects to if the AuthKey is valid. On the account page, create two text fields, not inputs though. These will be just for testing; to make sure that we have successfully logged in. Make one of the text fields say ‘email: ‘ then after that text, insert dynamic content. The dynamic content would be asking for the current user’s email. We want the second text to say ‘logged in? ’ and after that we want to insert dynamic content as well. The dynamic content would be asking for the current user is logged in, that sounded poorly written, but I can promise it is technically correct.

We also need a button, this button will be for the user to sign out. So create a button on this page that says “Sign out”. Then create a new workflow for when a user clicks on it. The first step should be ‘Log the user out’, which is straight-forward; doesn't require anything to satisfy it. Then a ‘Go to page …’ action which we want it to direct the recently logged out user to the index of the website. 



Directing the user to their account page

This is quite easy to do, just go back to your ‘logintoken’ page and set the destination for the ‘Go to page …’ action to the ‘account’ page.



Testing the system

The system should now be working. So lets try logging in with the test user we made earlier. We must go to the the index page first in the Bubble editor, then press the Preview button. This should open up a new window that now is displaying the index page. Enter your email into the email field and then follow the instructions that you will be emailed.

If the system isn't working, double check all the parameters and workflow steps. You can also try the debugging tool that is at the bottom of the page if things aren’t going smoothly. If this is not working still, please make a comment if a comment section is available (depending on where you are reading this) for others and myself to help you. 

You can email me here



Closing notes

If some random stranger goes to the account page they may see a weird page that has missing content, due to not being logged in. A way to prevent this from happening is to add a new workflow event to the ‘account’ page that states when the user is logged out to go to the index page. This will make anyone who isn't currently logged in, to immediately be taken to the index, they should never see any content on the ‘account’ page.

If some malefactor gets hold of the login details of your users, the password they obtained, because it is not static, would be irrelevant the next time a user logs in. Making the information they have, much less valuable.



Storing the email in the email

Instead of storing the email address alongside of the ‘tempKey’ we can store it in the URL parameter that we give the button in the email. So it would look something like &email=<email address> that goes right after the AuthKey unique ID in the button link. If you are including multiple parameters in a URL, you are going to include ‘&’ instead of ‘?’ for all parameters after the first one. The first one still has a ‘?’ that goes along with it. Then for the login action, you would ask for the ‘email’ parameter from the URL. As this article is running a bit long, I will save some alternative solutions for another post.



Confirmation emails

Well, you wouldn’t exactly need to send a confirmation email to a user, since the first time they log in, they in a way, have confirmed it is their email address.



Creating a signup system

Well, this will take up another blog post itself, so that is where I will leave it. However, I will help you get started if you want to do it yourself, you go-getter you. 

You have the option to do it in a few ways, especially depending on the kind of app and information you need from your user. But you will want to change the workflow that is ran depending if the email in the input email address is already a user or not. If it detects that the email in the input is not already a user, you will want to run the workflow that will sign them up. You will also want a ‘signuptoken’ page that signs the user up there, instead of logging them in.




One reason I'm writing this article is to get feedback on the system. Are there vulnerabilities and can they be fixed? Is there a better way than what I built? What are improvements to the system? What do you use to log users in?

2 comments:

  1. Great article. Bubble is very flexible, and your no password system is clever.
    If you use it with a internal email instead of gmail, for example, for another layer of security.

    ReplyDelete