Ruby on Rails API with JWT Auth Tutorial

Our Mission

Starting Up Our Project

rails new notesapi --database=postgresql --api

Configuring your dependencies

gem 'bcrypt', '~> 3.1.7'
gem 'rack-cors'
gem 'jwt'

CORS

Rails.application.config.middleware.insert_before 0, Rack::Cors do
allow do
origins '*'
resource '*',
headers: :any,
methods: [:get, :post, :put, :patch, :delete, :options, :head]
end
end

Setting up Auth Routes

Rails.application.routes.draw do
resource :users, only: [:create]
post "/login", to: "users#login"
get "/auto_login", to: "users#auto_login"
end

Creating the User Model

rails g model User username:string password_digest:string age:integer

Securing our User Model

class User < ApplicationRecord
has_secure_password
end

The User Controller

rails g controller Users

Migrating the model to your database

default: &default
adapter: postgresql
encoding: unicode
pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
development:
<<: *default
database: database_name
username: test
password: test
host: localhost
port: 5432
test:
<<: *default
database: database_name
production:
<<: *default
url: <%= ENV['DBURI'] %>
rails db:create && rails db:migrate

Seed some data

user = User.create(username: "alexmerced", password: "pineapple", age: 35)
rails db:seed

The Application Controller

class ApplicationController < ActionController::API
before_action :authorized
def encode_token(payload)
JWT.encode(payload, 'yourSecret')
end
def auth_header
# { Authorization: 'Bearer <token>' }
request.headers['Authorization']
end
def decoded_token
if auth_header
token = auth_header.split(' ')[1]
# header: { 'Authorization': 'Bearer <token>' }
begin
JWT.decode(token, 'yourSecret', true, algorithm: 'HS256')
rescue JWT::DecodeError
nil
end
end
end
def logged_in_user
if decoded_token
user_id = decoded_token[0]['user_id']
@user = User.find_by(id: user_id)
end
end
def logged_in?
!!logged_in_user
end
def authorized
render json: { message: 'Please log in' }, status: :unauthorized unless logged_in?
end
end

SO LET’S BREAK DOWN THESE METHODS:

fetch("/", {
method: "post",
headers: {
"Content-Type": "application/json",
Authorization: `bearer ${JWT_TOKEN}`,
},
body: JSON.stringify(requestBody),
})

The Users Controller

class UsersController < ApplicationController
before_action :authorized, only: [:auto_login]
# REGISTER
def create
@user = User.create(user_params)
if @user.valid?
token = encode_token({user_id: @user.id})
render json: {user: @user, token: token}
else
render json: {error: "Invalid username or password"}
end
end
# LOGGING IN
def login
@user = User.find_by(username: params[:username])
if @user && @user.authenticate(params[:password])
token = encode_token({user_id: @user.id})
render json: {user: @user, token: token}
else
render json: {error: "Invalid username or password"}
end
end
def auto_login
render json: @user
end
private def user_params
params.permit(:username, :password, :age)
end
end

Time to test our API Auth

rails s -p 4000

Making the Notes item

rails g scaffold note message:string user:references

The Result

class Note < ApplicationRecord
belongs_to :user
end
class NotesController < ApplicationController
before_action :set_note, only: [:show, :update, :destroy]
# GET /notes
def index
@notes = Note.all
render json: @notes
end
# GET /notes/1
def show
render json: @note
end
# POST /notes
def create
@note = Note.new(note_params)
if @note.save
render json: @note, status: :created, location: @note
else
render json: @note.errors, status: :unprocessable_entity
end
end
# PATCH/PUT /notes/1
def update
if @note.update(note_params)
render json: @note
else
render json: @note.errors, status: :unprocessable_entity
end
end
# DELETE /notes/1
def destroy
@note.destroy
end
private
# Use callbacks to share common setup or constraints between actions.
def set_note
@note = Note.find(params[:id])
end
# Only allow a trusted parameter "white list" through.
def note_params
params.require(:note).permit(:message, :user_id)
end
end
class CreateNotes < ActiveRecord::Migration[6.0]
def change
create_table :notes do |t|
t.string :message
t.references :user, null: false, foreign_key: true
t.timestamps
end
end
end
Rails.application.routes.draw do
resources :notes
resource :users, only: [:create]
post "/login", to: "users#login"
get "/auto_login", to: "users#auto_login"
end

Adding auth to notes

before_action :authorized
# GET /notes
def index
@notes = Note.where(user_id: @user.id)
render json: @notes
end
# GET /notes/1
def show
render json: @note
end
# POST /notes
def create
@note = Note.new(note_params)
@note.user_id = @user.id
if @note.save
render json: @note, status: :created, location: @note
else
render json: @note.errors, status: :unprocessable_entity
end
end

--

--

--

Alex Merced is a Full Stack Developer, learn more about his work at AlexMercedCoder.com

Love podcasts or audiobooks? Learn on the go with our new app.

Recommended from Medium

An organized way to manage projects using Kanban

Xcode Stack View Method That Works

Route 66 Buzz-Sixty six means 66 or LXVI

What it’s like to be in a coding bootcamp

Getting started with Unity

AnyDAO: Helping DAOs do what they do best

SANS Holiday Hack Challenge 2020: Sort-O-Matic (Regex Toy Sorting)

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Alex Merced Coder

Alex Merced Coder

Alex Merced is a Full Stack Developer, learn more about his work at AlexMercedCoder.com

More from Medium

Rails Authentication & Authorization

Ruby on Rails Console

Dockerizing a small Rails application

Ruby on Rails MVC Framework