Source Code References:
https://github.com/rajeshpillai/rails-membership-api
Let’s create a new rails project (in case you don’t have one)
$ rails new simple_membership_api # use your project name here
Then don’t forget to ‘cd into the project’.
Gemfile
First let’s setup the Gemfile with bcrypt, jwt and rack-cors.
gem 'bcrypt', '~> 3.1.7' # Used for password digest
gem 'jwt' # token auth
gem 'rack-cors', '~> 0.4.0' # cross origin request
After this install the gems by running
$ bundle install
Generator
Create a User model (Note: You can add more fields as needed)
$ rails g scaffold User email:uniq password:digest
# If you dont want the html form, if you are using this as an API project use
# rails g model User email:uniq password:digest
$ rails db:migrate
Adding the digest to the model enables the Rails generator to create a password_digest
field in the table and asks for an additional password_confirmation
in the form.
The model should contain has_secure_password
which takes care of encrypting the password and provides theauthenticate
method to authenticate with that password.
User.rb -> has_secure_password
class User < ApplicationRecord
has_secure_password
validates :email, presence: true, uniqueness: true
def generate_jwt
JWT.encode({
id: id, # user_id
exp: 60.days.from_now.to_i},
Rails.application.secrets.secret_key_base)
end
end
Sessions Controller
Let’s create a sessions controller for user to log in/ log out. We add an empty new, create and destroy action to our controller.
$ rails g controller sessions new create destroy
Modify the controller code as shown (sessions_controller.rb)
class SessionsController < ApplicationController
def new
end
def create
user = User.find_by_email(params[:email])
if user && user.authenticate(params[:password])
render json: user.as_json(only: [:email])
.merge("token": user.generate_jwt)
else
render json: { errors: {'email or password': ["is invalid"]}},
status: :unprocessable_entity
end
end
def destroy
# If token is saved in DB, you can destroy it here
head(:ok, status: :no_content)
end
end
User Controller
Let’s take a look at the show method of the users controller (which should be used for displaying profile information). In case you don’t have it, you can create it.
# GET /users/1#
# GET /users/1.json
def show
end
If the request type is for json then by default the jbuilder view will be rendered. You can also return json explicitly if you wish.
views/users/show.json.jbuilder
json.partial! "users/user", user: @user
views/users/_user.json.jbuilder (add remove fields here)
json.extract! user, :id, :full_name, :email, :phone, :country, :date_of_birth, :created_at, :updated_at
json.url user_url(user, format: :json)
Routes
Let’s also cross check our routes file.
Rails.application.routes.draw do
root 'home#index'
resources :users
resources :sessions, only: [:new, :create, :destroy]
delete 'logout', to: 'sessions#destroy', as: 'logout'
# Add the below routes if the login/register page is created in rails
# If rails is used only as an API backend, these forms can be created
# on the respective frontend, javascript/react/angular etc.
# get 'signup', to: 'users#new', as: 'signup'
# get 'login', to: 'sessions#new', as: 'login'
end
current_user
Let’s create a current_user helper method, which allows access to the currently logged in user.
app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
helper_method :current_user
protect_from_forgery unless: -> { request.format.json?}
before_action :validate_user!, except: [:login]
private
def validate_user!
if request.headers['Authorization'].present?
# {'Authorization' : 'Bearer <TOKEN>'}
token = request.headers["Authorization"]
token = token.split(" ")[1] # Remove "Bearer"
begin
jwt_payload = JWT.decode(token, Rails.application.secrets.secret_key_base).first
@current_user_id = jwt_payload['id']
rescue => exception
head :unauthorized
end
else
head :unauthorized
end
end
def current_user
if session[:user_id]
@current_user ||= User.find(session[:user_id])
else
@current_user = nil
end
end
end
To test this start up your rails server
$ rails s
Testing API
Use postman or any other rest-client tool to test the api.
POST /sessions (login)
On success you should get a token
DELETE /logout
You have to pass the token for signout
POST /users.json
This is a quick draft with no error handling. Watch out for updates!