React/Rails Authentication: API Configuration

This is the first entry in setting up an application using a React client and a Rails API to use Rails sessions for authentication.

Let’s get right into it by initializing our API.

rails new auth-app-api --database=postgresql

Rails has an option to initialize your app with the API flag, but doing so will require us to add back a lot of dependencies, so we will stick to the normal initialization.

I always like to initialize with the PostgreSQL as the default database, as SQLite is not supported by Heroku. It saves us a step later on in case we want Heroku to host our app on a live server. Additionally, I usually go into config/puma.rb and change the port number from 3000 to 5000, as the default Heroku dyno runs on 5000. It would need to be changed regardless since React’s default port is the same as Rails’ default, port 3000. Your puma.rb should have this line of code now:

port ENV.fetch(“PORT”) { 5000 }

Next, we want to create the database:

rails db:create

Then, we need to make sure we have the appropriate gems:

gem 'rack-cors'   #for communicating fetch requests
gem 'bcrypt' #for storing encrypted passwords
gem 'pry' #I prefer pry over byebug, but either one works

Go ahead and run bundle install to install these new gems. Afterwards, we will need to create two new files in our config/initializers folder, cors.rb and session_store.rb.

We’ll start with cors.rb. It should look like this:

Rails.application.config.middleware.insert_before 0, Rack::Cors do
allow do
origins "http://localhost:3000"
resource '*'
headers: :any,
methods: [:get, :post, :put, :patch, :delete, :options, :head],
credentials: true
allow do
origins ""
resource '*'
headers: :any,
methods: [:get, :post, :put, :patch, :delete, :options, :head],
credentials: true

There’s a lot going on here. Let’s go over it.

The origins you define is the domain being whitelisted for CORS. Rails automatically protects your app from Cross Origin Resource Sharing, so you need to specify the domain that should have permission to this API’s resources.

The resource '*' simply tells Rails that localhost:3000 has full access to the API’s resources. You can use this line to only allow specific resources if you choose to do so, but majority of the time in this situation the asterisk will work just fine.

The methods line defines what HTTP verbs you’ll allow in the fetch requests sent from your client application. Here, we are allowing our frontend app to use all of the verbs as well as send options and custom headers in requests.

The credentials: true line is what allows the headers and the cookie to be passed from the frontend to the API.

I like to take the initial extra steps for deployment in the beginning, which is why I included an additional allow block with a different origin. Now, we won’t have to include the domain upon deployment as long as we stick to the naming convention. Any additional places you choose to host your React frontend will also have to be listed here.

Next, we move onto our session_store.rb:

if Rails.env == "production" Rails.application.config.session_store :cookie_store, key: "_auth-app", domain: ""else Rails.application.config.session_store :cookie_store, key:   "_auth-app"end

This file is the file that creates our cookie to be passed from React to Rails.

First off, we have an if statement that checks the Rails Environment. If the app is in production, we list the domain name for the cookie. Otherwise, leave out the domain.

For the key:, conventional naming practices are the name of your app prepended with an underscore( _ ). You can name them what you want, but following this convention will be helpful in identifying the cookie in your browser.

Now that this is up and running, let’s generate a User model:

rails g model User username password_digest

This line of code should automatically generate a migration file in db/migrate that looks like this:

class CreateUsers < ActiveRecord::Migration[6.0]
def change
create_table :users do |t|
t.string :username
t.string :password_digest

It’s very important that :password_digest is included as it will automatically encrypt the password when saving the user. As a developer, you should NEVER have direct access to someone’s password. BCrypt can handle verifying it for you.

Additionally, the command will have generated a file in app/models called user.rb. We want to add the macro has_secure_password to our User class for BCrypt to store passwords. Our user.rb should look like this:

class User < ApplicationRecord

Run rails db:migrate to add these tables to the database. Double check your schema.rb to make sure everything looks good.

Next, let’s make a user. Open the rails console by running rails c and type in the command User.create(username: 'auth', password: 'asdf'). If everything is set up correctly, pressing Enter will create a new User in your database. You can verify this by seeing ActiveRecord log it in your console, and by using general query methods.

Next, we need a route and controller to process a request. In config/routes.rb, let’s define a route:

Rails.applications.routes.draw do
post '/login', to: 'sessions#create'

We’re going to need a controller to go with this. In app/controllers, let’s create a sessions_controller.rb with a create action:

class SessionsController < ApplicationController  def create
user = User.find_by(username: params[:username])
if user && user.authenticate(params[:password])
render json: { "logged_in": true }
render json: { "logged_in": false }

So, in this action, we are setting a user variable to a User in our database that matches the username sent in the post request. We then have a check to see if that user variable returns truthy, which would mean ActiveRecord found our user, and then we use BCrypt’s authenticate method to compare the password in the params to the user’s password. If both of these return truthy, for now we just want to know if we would’ve logged in or not.

Now, we are going to test this with a curl command. If you aren’t familiar with curl, that’s okay. I made a short guide that you can check out here. But for now, you’ll need two running terminals to test this. One terminal runs your server, the other one sends the request. In your server terminal, make sure you are in the root directory of your project folder and run rails s. It should be listening now.

In the other terminal, copy and paste this command:

curl -H "Content-Type: application/json" -H "Accept: application/json" -d '{"username": "auth", "password": "asdf"}' http://localhost:5000/login

If done correctly, you should see the JSON output {"logged_in: true"}. Great! Now you have the basics for authentication.

In the next guide, we will build out a portion of the React client that will communicate with this API.