How can I set auth header with JWT in Rails? - ruby-on-rails

I'm quite new to RoR programming and stuck while trying to migrate my authentication from Clearance to JWT. I created all of the required methods in ApplicationController and UsersController and even managed to sign up a user, save the user's password_digest to the database and then log in a user (in terms of POST params, I mean that no errors were thrown). However, I fail to keep the user logged in. I understand that there should be an auth_header attached to each request by my user, but how do I create one? I googled it multiple times, but failed to find how to handle it in terms of front-end. Everybody seems to use these fancy apps with buttons to create all the required headers and send raw json data ((
In other words, I have my JWT token encoded in the entrance method (as posted below) but I cannot understand how to pass it as a header in each and every request to the app further on?
users_controller.rb
class UsersController < ApplicationController
def create
#user = User.create(user_params)
if #user.valid?
token = encode_token({ user_id: #user.id })
redirect_to root_path
else
render json: { error: "Invalid email or password"}, status: :unprocessable_entity
end
end
def entrance
#user = User.find_by(email: user_params[:email])
if #user && #user.authenticate(user_params[:password])
token = encode_token({ user_id: #user.id })
redirect_to root_path
else
render json: { error: "Invalid email or password"}, status: :unprocessable_entity
end
end
def login
render 'login'
end
def signup
#user = User.new
render 'signup'
end
application_controller.rb
class ApplicationController < ActionController::Base
helper_method :current_user
def encode_token(payload)
JWT.encode(payload, 'secret')
end
def decode_token
auth_header = request.headers["Authorization"]
if auth_header
token = auth_header.split(' ')[1]
begin
JWT.decode(token, 'secret', true, algorithm: 'HS256')
rescue JWT::DecodeError
nil
end
end
end
def current_user
decoded_token = decode_token()
if decoded_token
user_id = decoded_token[0]['user_id']
#user = User.find(user_id)
end
end
def signed_in?
current_user.present?
end
def authorize
if signed_in?
redirect_to root_path
else
redirect_to "/log_in"
end
end
I truncated the code a bit, but all the relevant methods are included.

Related

Rails API not able to destroy session

I am using Rails API and devise for authentication but when sending a request to destroy the session, it doesn't return any response other then status 200.It returns the same response no matter if the user exists, is logged in or the password is incorrect.
url http://localhost:3000/users/sign_in
I'm sending a DELETE request through Postman and it just returns a 200 response. It should be returning the data which is stored in the #message
{
"user":{
"email":"abc#abc.com",
"password":"abc#abc.com"
}
}
I even tried to destory the session using the session id:
curl -X DELETE -d "3" http://localhost:3000/users/sign_out
sessions_controller.rb
class API::V1::SessionsController < Devise::SessionsController
def create
#user = User.find_by_email(user_params[:email])
if #user && #user.valid_password?(user_params[:password])
session[:user_id]=#user.id
sign_in :user, #user
render json: #user
elsif #user && not(#user.valid_password?(user_params[:password]))
invalid_attempt
else
no_user
end
end
def destroy
#message = "signed out"
session.delete(:user_id)
sign_out(#user)
render json: #message
end
private
def no_user
render json: {error: "An account with this email doesn't exist. Please create a new one"}, status: :unprocessable_entity
end
def invalid_attempt
render json: { error: "Your password isn't correct" }, status: :unprocessable_entity
end
def user_params
params.require(:user).permit(:email, :password)
end
end
routes.rb
devise_for :users, controllers: { registrations: 'api/v1/registrations', sessions:'api/v1/sessions'}

Efficiently save user session in Rails using devise

I am trying to learn Rails and have created User model using devise but just using Rails an API. I have added custom validations on the controller and it works fine. The user is able to login when posting data from the front-end. But, the problem is that the browser doesn't store the session anywhere. Hence, the session will be considered new every-time a page is reloaded. I am wondering if devise has an in-built functionality which would create session and store in cookie or local storage.
class API::V1::SessionsController < Devise::SessionsController
def create
#user = User.find_by_email(user_params[:email])
if #user && #user.valid_password?(user_params[:password])
sign_in :user, #user
render json: #user
elsif #user && not(#user.valid_password?(user_params[:password]))
invalid_attempt
else
no_user
end
end
def destroy
#message = "signed out"
sign_out(#user)
render json: #message
end
private
def no_user
render json: {error: "An account with this email doesn't exist. Please create a new one"}, status: :unprocessable_entity
end
def invalid_attempt
render json: { error: "Your password isn't correct" }, status: :unprocessable_entity
end
def user_params
params.require(:user).permit(:email, :password)
end
end

Unable to authenticate JWT in Ruby on Rails with Devise

I'm currently having trouble authenticating my Jason Web Token in Ruby on Rails 5.0.1. I'm using devise and jwt gems to validate the jwt generated for the user during log-in.
SessionController
def create
self.resource = warden.authenticate!(auth_options)
set_flash_message!(:notice, :signed_in)
sign_in(resource_name, resource)
yield resource if block_given?
respond_with resource, location: after_sign_in_path_for(resource)
jwt_create
end
def jwt_create
user = User.find_by(email: params[:user][:email])
if user.valid_password? params[:user][:password]
token = JsonWebToken.encode(sub: user.id)
flash[:notice] = "JWT Key: #{token}"
end
end
I'm currently using this to encode and decode my payload.
class JsonWebToken
def self.encode(payload)
payload['exp'] = 2.hours.from_now.to_i
JWT.encode(payload, Rails.application.secrets.secret_key_base)
end
def self.decode(token)
JWT.decode(token, Rails.application.secrets.secret_key_base)
end
end
And authenticating as a before_action with the following:
protected
def authenticate_request!
unless user_id_in_token?
render json: { errors: ['Not Authenticated'] }, status: :unauthorized
return
end
#current_user = User.find(auth_token[:user_id])
rescue JWT::VerificationError, JWT::DecodeError
render json: { errors: ['Not Authenticated'] }, status: :unauthorized
end
private
def http_token
#http_token ||= if request.headers['Authorization'].present?
request.headers['Authorization'].split(' ').last
end
end
def auth_token
#auth_token ||= JsonWebToken.decode(http_token)
end
def user_id_in_token?
http_token && auth_token && auth_token[:user_id].to_i
end
end
I'm not sure if my logic is flawed or I'm missing a key part to validate JWT. Any help would be greatly appreciated.

RoR - NoMethodError, undefined method

I have created an app with simple login authentication, it is actually a twitter clone. The user logs in and access the pages, etc.
But when the user posts something from there profile. It gives an error
NoMethodError in RibbitsController#create
undefined method `id=' for nil:NilClass
The error is around line 5:
class RibbitsController < ApplicationController
def create
#ribbit = Ribbit.create(user_ribbits)
#ribbit.userid = current_user.id
if #ribbit.save
redirect_to current_user
else
flash[:error] = "Problem!"
redirect_to current_user
end
end
private
def user_ribbits
params.require(:ribbit).permit(:content, :userid)
end
end
The request given to app:
Request
Parameters:
{"utf8"=>"✓",
"authenticity_token"=>"dwVmjDNO4GOowphGFgChMDBxBfvka+M/xSUHvJMECzwxtv4NF6OuWtiaX74NLz91OwQJ9T9+wm7yMiPQ0BLpGA==",
"ribbit"=>{"content"=>"hi. test.\r\n"},
"commit"=>"Ribbit!"}
The sessions controller:
class SessionsController < ApplicationController
def new
end
def create
user = User.find_by_username(params[:username])
if user && user.authenticate(params[:password])
session[:userid] = user.id
redirect_to rooturl, notice: "Logged in!"
else
flash[:error] = "Wrong Username or Password."
redirect_to root_url
end
end
def destroy
session[:userid] = nil
redirect_to root_url, notice: "Logged out."
end
end
The users controller:
class UsersController < ApplicationController
def new
#user = User.new
end
def create
#user = User.create(user_params)
if #user.save
session[:user_id] = #user.id
redirect_to #user, notice: "Thank you for signing up!"
else
render 'new'
end
end
def show
#user = User.find(params[:id])
#ribbit = Ribbit.new
end
private
def user_params
params.require(:user).permit(:name, :username, :email, :password, :password_confirmation, :avatar_url)
end
end
And the application controller
class ApplicationController < ActionController::Base
protect_from_forgery with: :exception
private
def current_user
#current_user ||= User.find(session[:user_id]) if session[:user_id]
end
helper_method :current_user
end
I would really appreciate it if you guys would help!
Thanks.
You're trying to assign current_user.idto #ribbit.userid without ensuring that current_user is set. 'current_user' would be set only if a user has been previously saved before.
Therefore, you need either to make sure that an authenticated user is trying to create a Ribbit, or if you consider the userid as a non mandatory field, you can simply change your line 5 by:
#ribbit.userid = current_user.id unless current_user.blank?
If you only want authenticated user to create Ribbits, then consider using a gem to handle authentication such as Devise. You could then use before_filter :authenticate_user! in your controller to make sure users are properly authenticated.

basic ruby on rails authentication trouble

I am working on a basic authentication system for a rails app. The authentication is verifying account information from Active Directory using a net-ldap class (this part is working fine).
Something seems to be wrong with my session_helper however. Even though ActiveDirectoryUser.authenticate is successful, the signed_in helper always returns false. After signing in, the script redirects to root_path (default_controller's home) and then immediately redirects back to signin_path again-- as a result of the signed_in helper returning false.
See the code below. What am I missing?
Thanks
application_controller.rb
class ApplicationController < ActionController::Base
protect_from_forgery
include SessionsHelper
end
default_controller.rb
class DefaultController < ApplicationController
before_filter :signed_in_user
def home
end
private
def signed_in_user
redirect_to signin_path, notice: "Please sign in." unless signed_in?
end
end
sessions_helper.rb
module SessionsHelper
def sign_in(user)
#current_user = user
end
def current_user
#current_user ||= nil
end
def signed_in?
!#current_user.nil?
end
def sign_out
#current_user = nil
end
end
sessions_controller.rb
class SessionsController < ApplicationController
def new
end
def create
user = ActiveDirectoryUser.authenticate(params[:session][:username],params[:session][:password])
if user.nil?
# authentication failed
flash.now[:error] = 'Invalid email/password combination'
render 'new'
else
# authentication succeeded
sign_in #user
flash[:error] = 'Great success'
redirect_to root_path
end
end
def destroy
sign_out
redirect_to root_path
end
end
You should use session for to persist that kind of data (will be assessable for every request), it's user data. But I highly recommend you to use something like the devise gem that do all that authentication things and more for you. Why reinvent the weel right?
I believe this would work for you.
module SessionsHelper
def sign_in(user)
session[:user_id] = user.id
end
def current_user
ActiveDirectoryUser.find(session[:user_id]) ||= nil
end
def signed_in?
!session[:user_id].nil?
end
def sign_out
session[:user_id] = nil
end
end

Resources