Due to the inability to generate a well-defined way to authenticate rails API, I have sourced out this information in order to help me and you have a way of authenticating our rails API token. This tutorial would be based on the latest version (6.0) of Ruby on Rails.
In getting this set up as well as up and doing, the few steps through which the processes would be implemented are listed below:
Step 1:
We would need to, first of all, generate a new rails app as we embark on the journey. Follow the example given below:
$ rails new sample-app --api -d=postgresql -T
$ cd sample-app
$ rails db:create
$ rails db:migrate
Step 2:
In order to gain access to the API, we would need to uncomment or remove Cors gems as that serves as permission into gaining access to the API
gem "rack-cors"
After uncommented the gem ‘rack-cors’ then we run this command below
$ bundle install
Step 3:
After we might have gained access to the API, then we run Bcrypt and Knock Gem since Bcrypt is a hash algorithm for password hashing and knock, which is known to be mainly for JWT authentication.
Write the following in cors.rb :
# config/initializers/cors.rb
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]
end
end
Step 4:
Let us then create a user as we make use of the scaffold generator. The essence of the user controller here is to give us a field known as password_digest which the Bcrypt will further turn from this field into an additional two fields named password and password_confirmation. We must engage the Bcrypt since there’s the ultimate need of us changing the former field into these two latter ones. Follow the sample below:
gem "bcrypt"
gem "knock"
Let’s run:
$ bundle install
User model and controllers
Now, we are going to create a user using the scaffold generator.
$ rails generate scaffold User username:string email:string password_digest:string
Include this in your user model.
# app/models/user.rb
class User < ApplicationRecord
has_secure_password
validates :username, presence: true
VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-]+(\.[a-z\d\-]+)*\.[a- z]+\z/i.freeze
validates :email, presence: true,uniqueness: true, format: { with: VALID_EMAIL_REGEX }
end
# app/controllers/users_controller.rb
def user_params
params.permit(:username, :email, :password, :password_confirmation)
end
Let’s remove the ‘location: @user’ in the create method.
# app/controllers/users_controller.rb
def create
@user = User.new(user_params)
if @user.save
render json: @user, status: :created
else
render json: @user.errors, status: :unprocessable_entity
end
end
Migrate data by running.
$ rails db:migrate
Let’s create a user using the rails console.
$ rails c
$ User.create(username: 'test', email: '[email protected]' password: 'test@123', password_confirmation: 'test@123')
Step 5:
After that all these have been done, we can then move into configuring the Knock Gem. "Knock Configuration"
$ rails g knock:install
After imputing the above command line, an initializer file will be created at config/initializers/knock.rb that contains the default configuration. In doing this, it is possible that we encounter an error like this: Could not load generator “generators/knock/install_generator”.
Ignore it and skip to the next step (default knock token). The error is mainly caused by a Rails 6 autoloader known as Zeitwerk.
Step 6:
Here, we are going to degenerate and generate the Knock token as we input the line below into application.rb.
$ rails d knock:install
Let’s include this in application.rb file:
# config/application.rb
config.load_defaults 6.0 and config.autoloader = :classic
$ rails g knock:install
Since the token is set by default to expire in just 24 hours, we can uncomment the line below and adjust it to suit our interest.
# config/initializers/knock.rb
config.token_lifetime = 1.day
Step 7:
Let us then generate a controller through which users will sign in
$ rails generate knock:token_controller user
The above line generates a user_token_controller sourced out from Knock::AuthTokenController which comes with a default create the action that will create a JWT when signed in. The generator also inserts a route in the routes.rb file as an API endpoint for sign in.
Be aware of the fact that user_token_controller is just meant for sign in, it is different from users_controller.
# app/controllers/user_token_controller.rb
class UserTokenController < Knock::AuthTokenController
end
Step 8:
This is where we’ll insert the include Knock::Authenticable module in our application_controller file.
# app/controllers/application_controller.rb
class ApplicationController < ActionController::API
include Knock::Authenticable
end
It is necessary that we add an authenticate user before filtering our controllers for protection. I’m adding it to users_controller and exempt the create method in order to enable users to sign up.
# app/controllers/users_controller.rb
before_action :authenticate_user,except: [:create]
Note: For people who are using Rails 5.2 or higher ones, there are two additional steps to follow:
1. You should skip the protect_from_forgery in the knock controller since it has already been included in ActionController::Base by default. It’s nice that we disable the standard rails authenticity token since we’re treating the back-end as an API.
# app/controllers/user_token_controller.rb
class UserTokenController < Knock::AuthTokenController
skip_before_action :verify_authenticity_token, raise: false
end
2.
# config/initializers/knock.rb
config.token_secret_signature_key = -> {
Rails.application.credentials.secret_key_base
}
You will include the above line to the Knock configuration file. This is because Rails no longer uses config/secrets.yml to hold the secret_key_base that is used for various security features including generating JWTs with the Knock gem.
The latest encoded file that rails uses nw is config/credentials.yml.enc.
Current_user
You also have access directly to current_user.
def index
if current_user
# do something
else
# do something else
end
end
Step 9:
Let’s create routes by adding an API namespace as we use scopes.
let’s add an “auth/” scope to all of our API routes. That will add “auth” to the path, but not to the controller or model. The user route here is being edited to `user_token controller`.
# config/routes.rb
scope '/auth' do
post '/signin', to: 'user_token#create'
post '/signup', to: 'users#create'
end
Step 10:
Wow! We’re done but let’s run a test on our work so far as we use Insomnia. Postman or Curl command-line tool could also be used to run the test. Let’s run the server as we hope that the odds would ever be in our favor.
$ rails s
Don’t let us forget to wrap our details in auth since it contains the sign-in form field names and values:
{
"auth": {
"email": "[email protected]",
"password": "test@123"
}
}