Ruby on Rails: Authentication and Authorization
Tags: ruby, rails
Published: 2022-08-02 22:08:00

Before learning about authentication and authorization, I always thought that users and their profiles were a black box that I would never understand. But when I discovered how to create and utilize profiles during my time at Flatiron School, I realized how simple of a process it actually is. In this blog, I'll be teaching you how to do it!

## Creating a User to be Authenticated

Let's start with authentication. Authentication is the process through which your application verifies that a user has been created. Prior to any model creation, it's important install the gem BCrypt, which allows you to encrypt and thereby protect clients' passwords. 

`gem 'bcrypt', '~> 3.1.18'`

Now, let's go ahead and create some models, along with their controllers, serializers, and routes. We can do so with the following code:

```
rails g resource Recipe title servings ingredients recipe
rails g resource User name password_digest bio
rails g resource Review content user_id:integer recipe_id:integer
```
To be compatible with BCrypt, it's important to create your password column as `password_digest` so that BCrypt can encrypt that password. An important note: the only time you will refer to the password as `password_digest` is here. Everywhere else (react, rails, etc.), you will refer to it as password. This is only so BCrypt knows to specifically encrypt that part in the database. Once you have migrated these files, go into your `User.rb` and put `has_secure_password`. This allows BCrypt to function. 

Before we create a user, how can we ensure that we are signed in as a user, the application remembers who we are? We can do this through cookies. Inside of our `application_controller.rb`, we must have written `include ActionController::Cookies`. This allows us to create sessions, which in essence allocates each user a session when they are logged in. This allows the application to essentially "remember" a user by associating all of the actions done in that session with the user to which it belongs.

Next, we must create our routes. To create an appropriate route to create a user, go into `/config/routes.rb` and put the following code: `post "/signup", to: "users#create". This opens up the route to create a user. Now, let's go to our `users_controller.rb` file. Inside, we have to create an appropriate landing pad for our route. We have to create a user with the given parameters. 


`````
  def create
    user = User.create!(user_params)
    session[:user_id] = user.id
    render json: user, status: :created
  end

private

  def user_params
    params.permit(:username, :password, :bio)
  end
`````


This code allows us to create a user and assign a session to it. Once this code runs and works, we have created a user!

##Authenticating and Authorizing a User

Okay, so we've created the user, but how do we actually login and start a session with it? Let's look back at our routes. In order to create a session with our user, we must post the following inside `routes.rb`: `post "/login", to: "sessions#create`. Similar to before, this sets up our route to be able to login. 

Now, let's head to our `sessions_controller.rb` file. Now, let's think about what we need to do here. We need to find our user in the database that we've created, and we need to make sure that they are created. But don't we also need to make sure that user is who they say they are? Users prove who they are by giving us a correct password. We must not only validate that the user exists (authentication) but also ensure that the user is who they say they are (authorization). So, we can do the following: 

`````
def create
     user = User.find_by(username: params[:username])
     user&.authenticate(params[:password])
     session[:user_id] = user.id
     render json: user
end
`````
The `user&.authenticate` bit does two things: it ensure that the user exists, and that password provided matches the encrypted one that we have in our database. In the case that the user provides the wrong password, we can provide them with errors:

`````
  def create
    user = User.find_by(username: params[:username])
    if user&.authenticate(params[:password])
      session[:user_id] = user.id
      render json: user
    else
      render json: { errors: ["Invalid username or password"] }, status: :unauthorized
    end
  end
`````
Because of the need to authorize and authenticate the user, we cannot hoist this error into the application controller. It must be written this way. Here, we are authenticating and authorizing the user. If they pass both tests, we start a session for them, and they are logged into the application. 

When it is time to logout of the application, the procedure is much simpler. In our `routes.rb` file, we create the following line: `delete "/logout", to: "sessions#destroy"`. In our `sessions_controller.rb`, we can create our landing pad with the following code:

`````
  def destroy
    session.delete :user_id
    head :no_content
  end
`````
This deletes the session associated with that user, successfully logging said user out. As you can see, the procedure for authenticating and authorizing is fairly simple. If there is interest, I can go ahead and write a future blog post about how to set this up on the frontend in react, creating a fullstack authentication and authorization app feature. Either way, I plan on writing about it soon. I hope this can help someone!