How !(not) to store passwords
Tags: webdev, beginners, security, ruby
Published: 2022-09-21 14:35:52

This article is the second part to [how not to store passwords](https://dev.to/aneshodza/how-not-to-store-passwords-20ib), which I advise you to read before reading this one. There we talk about the basics of password storage.
The source code of the first approach is [here](https://github.com/aneshodza/rails-auth-by-hand)
The source code of the second approach is [here](https://github.com/aneshodza/rails-auth-with-devise)
## Why this now?
The last article about passwords and their storage was merely to explain how it can be done. This article is about making the knowledge from before useful.
I will show you two different approaches on how you can handle users. The first approach will be doing it by hand and the second one makes use of the devise gem, which is the proper way to do it.
## Approach one - doing it by hand
This approach is the first one that someone might think of. Even though it works, it is by far the hardest and most insecure way. We will still do it this way, to better understand what happens when we actually do it with the devise gem.
### Creating the project
Step one is to create our project using
```bash
rails new rails-auth-by-hand
```
and jump in it with `cd rails-auth-by-hand`. There we generate our `home_controller` without the routes using
```bash
rails generate controller home index --skip-routes
```
Then we go into our `routes.rb` file, where we define that `root` is `home#index`. Our routes file should look like this:
```ruby
Rails.application.routes.draw do
  root 'home#index'
end
```
Now we can start our server using `rails server`. Our root page should just display Home#index and a bit of text below it.
### Importing bootstrap
To style this project we will use [bootstrap](https://getbootstrap.com/docs/5.2/getting-started/introduction/). I like keeping bootstrap open while I design. This tutorial will not explain bootstrap or responsiveness as it is about security and not design.
Now to import bootstrap we take the `<link>` tag and put it in the header of our `layouts/application.html.erb` view file. The `<script>` tag we put into the last line before the `<body>` tag closes.
In the end the file should look as follows:
```erb
<!DOCTYPE html>
<html>
  <head>
    <title>RailsAuthByHand</title>
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <%= csrf_meta_tags %>
    <%= csp_meta_tag %>

    <%= stylesheet_link_tag "application", "data-turbo-track": "reload" %>
    <%= javascript_importmap_tags %>
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.1/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-iYQeCzEYFbKjA/T2uDLTpkwGzCiq6soy8tYaI1GyVh/UjpbCx/TYkiZhlZB6+fzT" crossorigin="anonymous">
  </head>

  <body>
    <%= yield %>
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.2.1/dist/js/bootstrap.bundle.min.js" integrity="sha384-u1OknCvxWvY5kfmNBILK2hRnQC3Pr17a+RTT6rIHI7NnikvbZlHgTPOOmMi466C8" crossorigin="anonymous"></script>
  </body>
</html>
```
### Creating the models
Next we need to generate our user and a token model, which we will use as a session token. Now the user needs a username, a password and a password confirmation. For now we aren't going further than that. The token only needs a key and a user_id.
The user we generate by typing
```bash
rails generate model User username:string password:string confirmation:string
```
And then the token:
```bash
rails generate model ManualSessionToken key:string user:references
```
Now to get all of that into our database, we `rails db:migrate`. It is possible that your database doesn't exist yet. In that case just type `rails db:create` beforehand.
### Creating controllers
Next we need to generate our controllers. The user controller we create by entering
```bash
rails g controller user show new create --skip-routes
```
And the same for the session token controller
```bash
rails g controller sessionToken new create destroy --skip-routes
```
What both of these controllers will be doing is quite intuitive: The user controller handles viewing an account and also creating one, the session token controller handles login and logout because login is the creation of a session token and logout is a deletion of the session token.  
Then we add those routes in our `routes.rb`:
```ruby
Rails.application.routes.draw do
  root 'home#index'

  get '/account', to: 'user#show'
  get '/register', to: 'user#new'
  post '/register', to: 'user#create'

  get '/login', to: 'session_token#new'
  delete '/logout', to: 'session_token#destroy'
  post '/login', to: 'session_token#create'
end
```
### Working on the frontend
First, we will work on our homepage. There we will have a card in the center of our screen with a sign up and either a login or logout button. For now we will show both a login and a logout button, but later we will give it a conditional render to appropriately display what it needs. The styling is actually quite easy with the use of bootstrap. My code for it looks as follows:
```html
<div class="d-flex align-items-center justify-content-center vw-100 vh-100">
    <div class="card text-center home">
        <div class="card-header">
            <h3 class="card-title">Welcome!</h3>
        </div>
        <div class="card-body d-flex flex-column gap-2">
            <p>What would you like to do?</p>
            <a>
                <button type="button" class="btn btn-primary w-100">
                    Register
                </button>
            </a>
            <a>
                <button type="button" class="btn btn-primary w-100">
                    Log in
                </button>
            </a>
            <a>
                <button type="button" class="btn btn-danger w-100">
                    Log out
                </button>
            </a>
        </div>
    </div>
</div>

<style>
.home {
    width: fit-content;
    height: fit-content;
}
</style>
```
### Registration
Next, we will design our register form. Here I also used Bootstrap to my advantage, so that the design was done quicker. The form looks something like this:
```erb
<div class="d-flex align-items-center justify-content-center vw-100 vh-100">
    <%= form_with model: @user, url: "/register", method: :post, class: "rounded-2 border border-primary p-3" do |f| %>
        <div class="mb-3">
            <%= f.label :username, "Username", class: "form-label" %>
            <%= f.text_field :username, class: 'form-control' %>
            <div class="form-text">has to be unique</div>
        </div>
        <div class="mb-3">
            <%= f.label :password, "Password", class: "form-label" %>
            <%= f.password_field :password, class: 'form-control' %>
        </div>
        <div class="mb-3">
            <%= f.label :confirmation, "Password confirmation", class: "form-label" %>
            <%= f.password_field :confirmation, class: 'form-control' %>
        </div>
        <%= f.submit "Register", class: "btn btn-primary" %>
    <% end %>
</div>
```
With the magic of bootstrap it looks like this out of the box:
![Screenshot of register form](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7m4qa7d1wi1rk6itr14z.png)
And the most impressing part is, that the div is centered!!
Then we will also want to connect the register button on our homepage with this page, so we add the URI, which is `/register`:
```html
<a href='/register'>
    <button type="button" class="btn btn-primary w-100">
        Register
    </button>
</a>
```
And finally we will add divs to conditionally display our flash messages in our `application.html.erb`. This code comes right below the opening `<body>` tag:
```erb
<% if flash[:error] %>
  <div class="alert alert-danger position-absolute top-0 start-50 translate-middle mt-5" role="alert" >
    <%= flash[:error] %>
  </div>
<% end %>
<% if flash[:success] %>
  <div class="alert alert-success position-absolute top-0 start-50 translate-middle mt-5" role="alert" >
    <%= flash[:success] %>
  </div>
<% end %>
```
Then we write our controller. For that we go into our `user_controller.rb`. The new function will just make a new, empty, user instance, which is passed to the frontend:
```ruby
def new
  @user = User.new
end
```
Now our create function is the place where we actually save the user instance. If you try to print the params with `puts params` inside the `create` function it gives you something similar to this back:
```ruby
{
"authenticity_token"=>"WdWaPB8Idxow55WaK43QfwZFc4irsRm1Fme6RQug96ezIulgDUokoHMx6-HpQ3O1_q5goLiuPU8dmnJwynrelQ", 
"username"=>"anes",
"password"=>"12345678",
"confirmation"=>"12345678",
"commit"=>"Register",
"controller"=>"user",
"action"=>"create"
}
```
Now let us quickly go through what we can see. First we have the authenticity token. That is an auto-generated token by Rails which is responsible for our security. With that we can catch fake forms being sent in. Next we have the username we put in. Then comes our password and it's confirmation. The last three attributes specify where the request has been called from.
For us the username, password and its confirmation are important. In this tutorial we wont go over validation on the frontend, but a tutorial on how to do that is already in the making.
Next, we will try to validate that in our backend. Step one is creating strong parameters, so that the user can only submit the stuff we really want:
```ruby
private

def user_params
  params.require(:user).permit(:username, :password, 
                               :confirmation)
end
```
This blocks everything else that comes in when we get a form submission. Now let us start working on our validations and saving of the user. We will split up all of our validations into private methods as not to complicate our create function too much.
Step one is that the password has at least 6 characters. That is very simple to implement:
```ruby
private
# ...
def invalid_length?
  return false if user_params[:password].length >= 6

  flash[:error] = 'Password must be at least 6 characters'
  redirect_to '/register'
end
```
Now we want to check if the password matches with our validation:
```ruby
private
#...
def password_not_matches?
  return false if user_params[:password] == user_params[:confirmation]

  flash[:error] = 'Password and confirmation do not match'
  redirect_to '/register'
end
```
Then we want to check if the username is already in the database. For that we just query our database and if it exists we give an error:
```ruby
def non_unique_username?
  return false unless User.find_by(username: user_params[:username])

  flash[:error] = 'Username already exists'
  redirect_to '/register'
end
```
Now we reference all of those functions in our create method and return if they are false as not to trigger the rest of the function:
```ruby
def create
  return if password_not_matches?
  return if invalid_length?
  return if non_unique_username?
end
```
And then we obviously have to also try to store the user:
```ruby
def create
  #...
  @user = User.new(user_params)
  if @user.save
    redirect_to root_path
  else
    flash[:error] = 'There was an unexpected error :/'
```
Now we can see that our validation renders an flash message:
![Image of our flash message](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ub6g7k4wlt3dy8o5g0de.png)
If we successfully register a user we get redirected to the `root_path` with a success message. To check if the user was created we go into the rails console by typing `rails console`. There we enter `User.last` where we should see the user. If you were attentive in my last tutorial you can see a major flaw: The password is being stored in plain text! We need to fix that ASAP.
#### Encrypting the password
Step one is creating a new column in our user table to save the salt in. For that we type in the command `rails generate migration addSaltToUser salt:string` which automatically creates following migration:
```ruby
class AddSaltToUser < ActiveRecord::Migration[7.0]
  def change
    add_column :users, :salt, :string
  end
end
```
And then we obviously need to migrate our database with `rails db:migrate`. If we now take a look at our user model we should see that `salt` has been created.
Now we actually need to use the salt. Step one is going into our user model. There we need to create an `before_create` callback, which only triggers before the user is created for the first time. Then we need to call a function to generate our salt and also make a constant that is our pepper:
```ruby
class User < ApplicationRecord
  before_create :encrypt_password
  PEPPER = 'FollowAnesHodzaOnDev.toAndHodzaAnesOnTwitterIfYouEnjoy'.freeze

  private

  def encrypt_password
    self.salt = gen_salt
    self.password = Digest::SHA256.hexdigest(password + salt + PEPPER)
  end

  def gen_salt
    SecureRandom.hex(64)
  end
end
```
Now if we check our users in the rails console we can see that one important thing is missing:
![Screenshot of active model object](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1kd3b1f4ruklr42q540w.png)
The confirmation shows the password. That is a major security flaw. We will just fix it by changing the confirmation hardcoded:
```ruby
self.confirmation = 'follow @hodzaanes on twitter'
```
Now checking the db should not give you any information about the password except for the salt, which is alright.
### Login
Let's start off by going to the login page:
![Screenshot of our login page](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/po0kciqwlw57ldoxr2x0.png)
The function that is (as seen on the screenshot) `SessionToken#new` handles those. We first want to create the login form. We can just copy the register form and tweak it a bit: The destination has to be changed, the confirmation field is not needed anymore and the button text needs to be changed to 'Log in':
```erb
<div class="d-flex align-items-center justify-content-center vw-100 vh-100">
    <%= form_with model: @user, url: "/register", method: :post, class: "rounded-2 border border-primary p-3" do |f| %>
        <div class="mb-3">
            <%= f.label :username, "Username", class: "form-label" %>
            <%= f.text_field :username, class: 'form-control' %>
        </div>
        <div class="mb-3">
            <%= f.label :password, "Password", class: "form-label" %>
            <%= f.password_field :password, class: 'form-control' %>
        </div>
        <%= f.submit "Log in", class: "btn btn-primary" %>
    <% end %>
</div>
```
Now in our `home/index.html.erb` we can link this site too:
```html
<a href='/login'>
    <button type="button" class="btn btn-primary w-100">
        Log in
    </button>
</a>
```
Then we need to code the login. That will consist of 2 parts: Checking if the login is correct and creating the session token.
We check if the login is correct by first getting a user that matches our username and then we check if the password matches the hash stored in the db:
```ruby
class SessionTokenController < ApplicationController
  def new
    @user = User.new
  end

  def create
    @user = User.find_by(username: user_params[:username])
    return if invalid_login?

    flash[:success] = 'Login successful'
    redirect_to '/account'
  end

  def destroy; end

  private

  def user_params
    params.require(:user).permit(:username, :password)
  end

  def invalid_login?
    if @user && @user.password != Digest::SHA256.hexdigest(user_params[:password] + @user.salt + User::PEPPER)
      return false
    end

    flash[:error] = 'Invalid login :/'
    redirect_to '/login'
  end
end
```
Don't forget the strong parameters!
The second part is creating the session token. That will be stored in the cookies. Now to validate if that session token is correct we now need to take our `ManualSessionToken` class and create a new one when a user logs in. First we start off by generating a random session token and storing it in the users cookies:
```ruby
class SessionTokenController < ApplicationController
  #...
  def create
    #...
    cookies[:session_token] = generate_session_token
    ManualSessionToken.create(user_id: user.id, key: cookies[:session_token])
  #...
  private
  #...
  def generate_session_token
    SecureRandom.hex(64)
  end
  #...
end
```
Now if a hacker gets access to our db and is able to see all our `ManualSessionToken`s it's the same as if he had every users password, because he can just query requests through the token. That's why we also need to salt and pepper this.
This is the same process as when we did it to the user:
```bash
rails generate migration addSaltToManualSessionToken salt:string
rails db:migrate
```
```ruby
class ManualSessionToken < ApplicationRecord
  belongs_to :user
  before_create :encrypt_session_key
  PEPPER = 'ThisPepperIsSoHotItWillMakeYouSweatAndStopTheHackerFromAccessingOurPasswords'.freeze

  private

  def encrypt_session_key
    self.salt = gen_salt
    self.key = Digest::SHA256.hexdigest(key + salt + PEPPER)
  end

  def gen_salt
    SecureRandom.hex(64)
  end
end
```
Now we also need to think of other situations: what if the user logs in a second time?
#### Expanding the session key logic
Now we want to check if a SessionKey already exists when the user logs in. If that is the case, the user already has a SessionKey and instead of creating a new one every time we want to update that one:
```ruby
def create
  #...
  cookies[:session_token] = generate_session_token
  manage_session_token
  #...
end
#...
private
#...
manage_session_token
  session_token = ManualSessionToken.where(user_id: @user.id).first
  if session_token
    session_token.update(key: cookies[:session_token])
  else
    ManualSessionToken.create(user_id: @user.id, key: cookies[:session_token])
  end
end
```
#### Protecting a route
Now we want to implement the user account details view. We want to configure it in such a way, that the user can only see his account details if he is logged in.
First we need to create a view in html. That could look as following:
```erb
<div class="d-flex align-items-center justify-content-center vw-100 vh-100">
    <div class="d-flex align-items-center justify-content-center flex-column">
        <h1>Welcome <b><%= @user.username %></b></h1>
        <p>
            Here we would have some random data, if you even had any data
        </p>
    </div>
</div>
```
The next step is to make any way for our application to know what user the token belongs to. We already have the `user_id` in our db next to the session token, so we can just use that. For that we just need to expand our stored cookie:
```ruby
def create
  #...
  cookies[:session_token] = "#{@user.id};#{generate_session_token}"
  #...
end
```
As you shouldn't store objects in the cookies we just concat both to a string which we can then later parse back to an object.
Now in our protected function `user#show` we have to first check if the cookie exists, if the server has a cookie and if the both match. If that is the case, the user gets access to the user with that id:
```ruby
def show
  return unless logged?

  @user = User.find(parse_session_token[:id])
end
#...
private
#...
def parse_session_token
  token = cookies[:session_token]
  return { id: token.split(';')[0], key: token.split(';')[1] } if token

  false
  end


def logged?
  return false unless parse_session_token

  session_token = ManualSessionToken.find_by(user_id: parse_session_token[:id])
  return false unless session_token

  encrypt = Digest::SHA256.hexdigest(cookies[:session_token] + session_token.salt + ManualSessionToken::PEPPER)
  if encrypt == session_token.key
    true
  else
    send_to_login
    false
  end
end
```
### Logout
Now we want to create our logout. That logout exists of two functions: Destroying the server side `ManualSessionToken` and destroying the client-side cookie.
Let us first create the button. For that we can just use the rails `button_to`:
```erb
<%= button_to 'Log out', '/logout', method: :delete, class: "btn btn-danger w-100" %>
```
Now if we want to make the buttons render conditionally we will have to reuse our logged? function from before. To reuse it here we need to extract it into a helper method. For that we delete our functions `parse session token`, `logged?` and `send_to_login`. We paste them into our `application_helper.rb` which makes it possible for us to access them from anywhere.
**Caution: You need to reference the methods as `helpers.my_function` if you are in a controller**, which means our `user_controller#show` looks like this:
```ruby
def show
  return unless helpers.logged?

  @user = User.find(helpers.parse_session_token[:id])
end
```
And our `application_helper.rb`:
```ruby
module ApplicationHelper
  def parse_session_token
    #...
  end
  
  def logged?
    #...
  end

  def send_to_login
    #...
  end
end
```
Now we can use the logged? in our `home/index.html.erb` to conditionally show our buttons:
```erb
<% if logged? %>
    <a href='/login'>
        <button type="button" class="btn btn-primary w-100">
            Log in
        </button>
    </a>
<% else %>
    <%= button_to 'Log out', '/logout', method: :delete, class: "btn btn-danger w-100" %>
<% end %>
```
Which looks like this if you are logged in:
![Screenshot of the homepage displaying a log out button](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/be0lknpj8pgmm5tnmwy6.png)
## Approach two - devise gem
It may have been cool and important for our understanding to  that by hand, but in the real world no one does it like that. When we need to set up authentication we just use the [devise gem](https://github.com/heartcombo/devise). You will soon see how much quicker it is.
### Creating the project
This step is almost the same as in approach one:
```bash
rails new rails-auth-with-devise
cd rails-auth-with-devise
bundle add devise
bundle install
rails generate controller home index --skip-routes
```
`routes.rb`:
```ruby
Rails.application.routes.draw do
  root 'home#index'
end
```
Add bootstrap to your `application.html.erb` again:
```erb
<!DOCTYPE html>
<html>
  <head>
    <title>RailsAuthWithDevise</title>
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <%= csrf_meta_tags %>
    <%= csp_meta_tag %>
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">
    <%= stylesheet_link_tag "application", "data-turbo-track": "reload" %>
    <%= javascript_importmap_tags %>
  </head>

  <body>
    <%= yield %>
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.bundle.min.js" integrity="sha384-MrcW6ZMFYlzcLA8Nl+NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP+JcXn/tWtIaxVXM" crossorigin="anonymous"></script>
  </body>
</html>
```
Now we also make our `home/index.html.erb` look the same as it did before:
```html
<div class="d-flex align-items-center justify-content-center vw-100 vh-100">
    <div class="card text-center home">
        <div class="card-header">
            <h3 class="card-title">Welcome!</h3>
        </div>
        <div class="card-body d-flex flex-column gap-2">
            <p>What would you like to do?</p>
            <a>
                <button type="button" class="btn btn-primary w-100">
                    Register
                </button>
            </a>
            <a>
                <button type="button" class="btn btn-primary w-100">
                    Log in
                </button>
            </a>
            <a>
                <button type="button" class="btn btn-danger w-100">
                    Log out
                </button>
            </a>
        </div>
    </div>
</div>

<style>
.home {
    width: fit-content;
    height: fit-content;
}
</style>
```
### Adding devise
Now that we set up the basics we can start with devise. It is actually quite easy to set it up. We start off by typing `rails generate devise:install`. Now we get four instructions, which we will follow. First we go into our `development.rb` and add following code:
```ruby
Rails.application.configure do
  #...
  config.action_mailer.default_url_options = { host: 'localhost', port: 3000 }
end
```
Step two we already did before. For step three we will do the same as we did in the first tutorial for our flash messages but with a small tweak:
```erb
<body>
  <% if alert %>
    <div class="alert alert-danger position-absolute top-0 start-50 translate-middle mt-5" role="alert" >
      <%= alert %>
    </div>
  <% end %>
  <% if notice %>
    <div class="alert alert-success position-absolute top-0 start-50 translate-middle mt-5" role="alert" >
      <%= notice %>
    </div>
  <% end %>
  <!-- ... -->
</body>
```
Now before we do step four, we need to generate our model:
```bash
rails generate devise user
```
That generated a few things. Important are: the migration, the model and the new route. We run the migration with `rails db:migrate` to update our schema and add the user model in our database. The `devise_for` created all the routes used by devise. Those routes still need views, which we can create by typing `rails generate devise:views`.
By typing `rails routes -g user` we can see, that devise created everything for us. We can just link it to the `home/index.html.erb` like we did before:
```erb
<a href="/users/sign_up">
  <button type="button" class="btn btn-primary w-100">
    Register
  </button>
</a>
<a href="/users/sign">
  <button type="button" class="btn btn-primary w-100">
    Log in
  </button>
</a>
<%= button_to "Log out", '/users/sign_out', method: :delete, class: "btn btn-danger w-100" %>
```
By clicking on those links we can see that the forms already all exist:
![Screenshot of sign up form](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/o2c616b3zc9yt01oz5c6.png)
If you want to, you can also style these forms, but I won't go over that again.
For our conditional rendering of the login or logout button we used a helper we manually created. Devise also offers us that:
```erb
<% unless user_signed? %>
  <a href="/users/sign">
    <button type="button" class="btn btn-primary w-100">
      Log in
    </button>
  </a>
<% else %>
  <%= button_to "Log out", '/users/sign_out', method: :delete, class: "btn btn-danger w-100" %>
<% end %>
```
When you try to register there may be the issue `undefined method user_url`. If that is the case add this line to `initializers/devise.rb`:
```ruby
Devise.setup do |config|
  #...
  config.navigational_formats = ['*/*', :html, :turbo_stream]
  #...
end
```
And that's already it! Now you can officially claim that you know how to handle users, logins, sessions and much more in Ruby on Rails.