Authorization and Authentication in Ruby on Rails
Published: 2022-04-05 21:59:49

##Overview
Authorization and authentication are two valuable areas of coding to understand. In this summary, I will be going over what authentication and authorization are and how to make a simple login form, including a username input that will be used to determine who the user is and whether they are authorized to view a post.

By the way, when I use the word "auth" I am referring to both authorization and authentication as a whole, not one or the other and not something else entirely.

##Authentication versus authorization

Authorization refers to what a user has access to; authentication refers to the verification of who a user is. Both of these are important for user security because validation of a user can prevent unwanted actions, such as admin access, from affecting the program/user in a negative way. 

## Cookies
Cookies are data that are stored in a browser while a website is being used by a client. Cookies are generally client-side, not server-side. Their purpose is to remember information about the user, and they can be created as plain-text name-value pairs, such as user_name=eric. 

```
cookies[:user_name] = "eric"
```

Cookies can also be set as hashes and have attributes, such as expiration dates/times.
```
cookies[:login] = { :value => "Eric", :expires => Time.now + 1.hour}
```
This cookie, stored as the symbol :login, has a value set as "Eric" and it expires and is deleted 1 hour from the current time.

One issue with cookies is that since they are generally plain-text, their information can be accessed by anyone, causing security concerns. Below is a special type of cookie, called a session, that helps resolve this security issue.

## Sessions
Sessions are data that are stored on the server side. They are a specialized cookie that adds a signature to the cookie, encrypting it and preventing clients from tampering with its data. The following is a way to set up a session:

```
session[:user_id] = user.id
// stores the user's ID into an encrypted session 
ID that is several characters long
```

In the code above, the session method will create a signature specifically for the  value of the [:user_id] key and assign the value of the user's ID to that signed value. This way, it is difficult for a malicious user to access the user_id.

## Code for allowing your program to use sessions and cookies

Here is some starter code for allowing cookies and sessions to be used in your program:

```
class Application < Rails::Application
   config.middleware.use ActionDispatch::Cookies
   // allows one to use cookies in their program
   config.middleware.use ActionDispatch::Session:CookieStore
   // allows one to use sessions in their program
   config.action_dispatch.cookies_same_site_protection = 
   :strict
   // makes sure that cookies are only accepted 
   within applications on the same domain as the 
   program, rather than different domains, for 
   security of our program
end
```

```
class ApplicationController < ActionController::API
   include ActionController::Cookies
end
// This allows our application controller, 
which other controllers would generally 
inherit from, to use cookies
```

##Creating a login form with a username

Authentication could be done in Ruby with a create action using POST:

```
post "/login" to sessions#create
```

In the above code, post refers to the POST restful action that allows one to persist a new instance of a login to the database; sessions refers to the controller that will be performing the create action via a create method.

```
class SessionsController < ApplicationsController
   def create
      user = User.find_by(username:params[:username])
      session[:user_id] = user.id
      render json: user
   end
end
```
 
Below is how React could save the username in state. After making a POST request via a submit button on a React form, the default empty string of username (from useState("") is replaced with the username retrieved from the Ruby backend. The username is converted into a JavaScript object in the body of the request and then passed into the login callback function as a parameter called user.

```
function Login({ handleLogin }) {
  const [username, setUsername] = useState("");

  function handleSubmit(e) {
    e.preventDefault();
    fetch("/login", {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
      },
      body: JSON.stringify({ username }),
    })
      .then((r) => r.json())
      .then((user) => handleLogin(user));
  }

  return (
    <form onSubmit={handleSubmit}>
      <input
        type="text"
        value={username}
        onChange={(e) => setUsername(e.target.value)}
      />
      <button type="submit">Login</button>
    </form>
  );
}
```

## Adding authorization to the login
The code above shows how one could take a user, save the user.id into the session[:user_id] and save the username in state, which shows on both the front end and back end that the user is authenticated. At the moment, all that is needed is the username, not the password.

To take this a step further, an authorize method could be added that allows certain actions by the user to be restricted unless they have logged in with a username. Below is code that could be used to do this for a program that renders posts:

```
class PostsController < ApplicationController
   before_action :authorize

   def show
      post = Post.find_by(id:params[:id])
      render json: post
   end

   private
   
   def authorize
      return render json: { error: "Unauthorized" }, status: 
      :unauthorized unless session.include? :user_id
   end
end
```

This code prevents a user without a user_id stored in the session to access the post. Basically, before the show method is run, the authorize method runs (before_action ensures that :authorize will run before any method within the PostsController), returning an error unless a user_id is stored in the session as a truthy value.

##Summary

Here are some important takeaways from this blog:
1) Authentication and authorization are not the same.
2) POST is a useful RESTful action that can be applied to logging a user in.
3) Cookies are usually client-side, plain text, and not secure; sessions are server-side, signed cookies that are more secure than other unsigned cookies.
4) before_action allows one to use a method before any other methods within a class; the method, authorize, was the example used in this blog that determined whether or not a user could make a post.
5) Certain middleware needs to be set up in order to use cookies in a Rails application; :strict is an example of how to prevent different domains from making requests for cookies.

##References

https://api.rubyonrails.org/classes/ActiveModel/SecurePassword/ClassMethods.html#method-i-has_secure_password

https://www.w3schools.com/js/js_cookies.asp

https://www.tutorialspoint.com/ruby-on-rails/rails-session-cookies.htm

https://www.youtube.com/watch?v=IzbQAj_tcfI

https://learning.flatironschool.com/