Signing up and Signing in to a web site are so commonplace these days that most people probably don’t even think about it. In fact, in many places you can do both using your information from other locations like facebook or google (which I don’t tend to do in general, the linking of information like that bothers me) that it probably seems almost easy to set up, especially if you don’t write code for a living (which I hope to do someday) or as skill building (which is what I do now). However, the logistics of properly setting up a sign-up/sign-in functionality aren’t as easy as one would think before studying it, as a variety of issues are very important.
These days everyone is (rightly) worried about their privacy and information on the web being secure. (Quick hint, it’s probably not, not really, because the criminals are always ahead of the people fighting them. If hackers want information from a site you’re registered on bad enough, they’ll get it). Now, as an every day web user you might think “well I followed the rules of upper and lower case characters, not using common words, birthdays, etc…, so I must be safe”. I mean it sounds like it makes sense right? Well, in theory, sure it does, but this assumes that the web site your signing up on also uses password security. See, in the olden days, web sites would store your password as ‘clear text’, i.e. if you set up your password as JaKoB#1, the web site would store it in their records as JaKoB#1 to compare with when you sign-in in the future. And that makes sense, right? They need to know the password I enter matches to the correct password I used when setting up my account. Well, that would make it really easy for hackers to get your password if it’s right there no? Once they crack the sites server, they have you password, and that’s no good. (Before you scoff, I know web developers selling a web platform who as recently as 2015 were still using clear text passwords for back end user sign-up, I wasn’t really thrilled about it). So now, we need a better way to store passwords.
Long before I got fully into this, and well over my head to fully explain, very smart people came up with ways to create long random strings using cryptographic that would correspond to to an algorithmic transformation of your entered password (JaKoB#1) from above to a very long series of characters that would be very hard to any hacker to figure out the ‘true meaning’ of. Then, when you sign-in and enter your simple password, the site performs the same translation on the information you entered and compares the resultant long series of characters to your original long series of characters that was created when you signed up and looks for a match, which while taking an infinitesimal amount of time longer to sign in creates infinitely more secure password security on the servers.
In the old wild west days of the web, if you were around, you could often recover your password just by entering your email and then they’d send you the password via email. This was doable because of the clear text storage. These days if you forget your password, you have to do slightly more work, and create a new password, because of this new encryption method. Smart programmers come up with new and devious (in a good way) ways to encrypt stored passwords so that ‘decrypting’ them is nigh impossible (as should be). Thus the user must do a little more work when they forget their password, but we promise, it’s for your own good and protection.
There are a variety of other issues that can come up when setting up users on a web site, be it valid email addresses, confirming email addresses, lost passwords (as above), persistent login and a host of other things that could come up if you want to set up a user authentication system, and let’s face it, if you’re setting up an application that will be used anywhere beyond your own home computer, you probably are going to need a system to authenticate who is contributing to your site. (It’s also required if you want to set up an authorization system, which for those who don’t know is the concept of limiting access to certain parts of your application to certain users or user types, but you can’t do that without authentication first.
If you want to, you can ‘roll your own’ authentication system every time you build a web site. Write the tests, write the code, refactor the code, and hope you’ve thought of every possible way to worry about user breeches. You can add to it when you want to alter the number of failed password attempts, or the steps required to recover a password. If you get something really good you can package it and reuse it, but, let’s face it, you’re going to probably need it on every project you build. Like the routes controller or the database connector, user authentication is to the point where it’s de facto on every web application built, and as such, shouldn’t you just be able to use an existing piece that sets up the foundation you can build on. Of course, and while there are many good ones out there, the one I use is devise.
Authentication with Devise
I’m not going to go into too much detail about what devise does here because, well, their git repository is pretty well written and clear for all levels of comprehension. Even if you don’t write code you should be able to see how easy it is to get started with devise, and it is really easy to get started with devise. Add one line to you gemfile and update it (bundle), write a few commands in the command line, and you’re on your way. devise is set up. You can write tests if you really want to, but if you’re only working with the built-in functionality of devise, it’s not entirely necessary I don’t think. However, like I said, all you’ve got is the basic functionality. At this points you can not:
- Control who signs up
- Control how many people sign up
- Block people from accessing certain parts of your site (remember that’s not really part of authentication but if you have basic authentication you can modify devise to do so)
- Allow people to create user names or add personal information, devise provides you with an email/password set up from the jump.
It’s true, you can’t do these four things, yet, but you do have a foundation upon which to build them. For instance, on this blog, as of this date (June 25, 2016), I used information gleaned in the devise wiki to customize the basic devise functionality so that only user can sign up. If any other people try to sign up on this blog, they would not be able to. Instead of having to build the entire foundation from scratch and then tweak it, all I had to do is write the tweak, and that’s the power of working with devise, and many other gems, the foundation is provided so you don’t have to rebuild the wheel.
An aside just for me to say. I think that this type of user authentication is so common place in web apps, that the Rails people should really consider a ‘built-in’ option that allows you to uses authentication from the beginning, when you type
rails new. I’m not advocating as a default, but much like you can select what database you want to use, you should be able to specify, say, one of the top 3 authentication systems out there so the basics are executed from the beginning. As authentication is to the point of being ‘foundational’, give us the chance to just run it from the beginning
Devise and the Kegtracker
The current kegtracker project that I’m working on to be used at my job has a different set of issues that require customization with devise, but because of the strong foundation, I don’t have to do as much as if I was going to build it from scratch. The customization required, in the beginning (because it’s still an in the beginning project) includes:
- Taking the first and last name of users.
- No sign-up page. The number of people who are signed up for this site is not fixed, and I can’t guarantee any other fixed quantities, so the sign-up page wont be accessible to users.
- Signed-in users can access the site - those who are not will not have any access to any other page.
Other issues may come up in the future, but these are the three I wish to start with today. Using a variety of sources, these issues are easy to deal with, because the creators of devise provided a lot of built-in options and functions that build upon the already extant rails foundation.
Taking the first and last name of users.
As stated above, the basic devise set up creates a user table that only stores the email address to identify users. In principal this is fine, but in practice you will want more information from users. A good place to start with that is to collect the first and last name of the user. Usually you just add those ideas into your database table and you’re good to go. Rails however, for security reasons, requires that you actively identify what information can be entered/updated by users. This concept (called Strong Parameters) is not a required part of Rails and anyone who has worked for Rails even a little recognizes the code that starts out
params.require(:user). So, the built in devise information only allows the email address and password, and I want to include first and last names. Thanks to devise and Rails both having the same foundation (ruby), it shouldn’t be an issue, and a little research into Chapter 14 of The Rails Way proved to me it wasn’t. Using a built-in devise methodology and the ability to write your own methods that apply to the whole application, the process is as simple as
- Add your fields to your User table
- Write one line of code to allow those new fields to be accepted by devise.
The application already has devise installed and I tested the sign-in working using a user created using FactoryGirl. The way I’m going to refactor the test to make sure this works is to add the new fields within the factory for a user. At first the test should fail because the factory won’t be able to create the new fields, but then I’ll add them and it should pass.
(I’m only focused on getting the fields accepted by devise at this time, validating the fields themselves will wait for another time)
Well, turns out that my plan only partly works. Yes the factory would not create the user because the fields did not exist in the User table, but after using Rails to create them, the test then fully passed, and I shouldn’t have been surprised. Strong parameters are about user input, and factories don’t take user input. So to really make sure it works, I will test the edit user functionality as sign-up won’t be a part of the project.
This does require alteration of the built in views (layouts) provided by devise. Devise doesn’t create them ‘publicly’ right away because they do have default versions, but if you are going to need to edit them, devise of course presents an easy way to give you that access. I’ve never had a reason to look closely at the devise view creation and as I did it I did find something interesting. They have different built-in pages to edit user information or just edit password information. That is good to know, as I could use that in the future to make people change their password on the first login.
So the first step is to alter the default devise edit user view to reflect the ability to change the first and last name of the user, and then run that test. Testing that requires some methods not native in RSpec, but as always, the functionality can be added pretty easily. Rails in Action 4 guides you on how to add functionality, via Warden, in your feature testing. This gets past the first ‘failure’ in my test, as the login method called will now work.
Following the instructions in Chapter 6 of Rails 4 in Action, I was able to now progress to the meat of the test. Can I update the first and last name of a user?
This took a little longer to build a proper ‘failure’ test than I had expected. Having not done too much testing with devise I wasn’t 100% sure what would and wouldn’t work to reveal the specific test I wanted to execute would pass. The correct testing involves checking that specific user attributes (the name ones) are changed after a successful update. After creating the proper test failure I was ready to pass it.
Even following the instructions in The Rails Way it took me a few attempts to get it right, (note to self: remember if you are checking if attributes changed without relying on view content, reload the object you updated) but in the end it did work. I had successfully added fields on top of the devise foundation and set up additions to said foundation to allow users to alter those added fields properly.
Originally this article was going to cover all three tasks but as I reach the end of this, I realize that this probably is enough for now and it took a little longer than I had planned. It’s 80 degrees with no A/C or ocean breeze so I’m going to end this here, and parts 2 and 3 from above can be done later.
Thanks again for reading, if you read.