I'm new to JWT authorization and have followed the boiler plate to set up auth in Rails. When I try to test my routes by calling localhost:5000/api/v1/users
or any controller for that matter, I get the following everytime:
JWTSessions::Errors::Unauthorized in Api::V1
Nil JSON web token
Obviously a token isn't being generated but not sure how to create one from just checking controllers in Rails. Is there a way to do this with Postman?
Here are my relevant controllers:
Application Controller:
class ApplicationController < ActionController::API
include JWTSessions::RailsAuthorization
rescue_from JWTSessions::Errors::Unauthorized, with: :not_authorized
private
def current_user
@current_user ||= User.find(payload['user_id'])
end
def not_authorized
render json: { error: 'Not authorized' }, status: :unauthorized
end
end
Signup:
class SignupController < ApplicationController
def create
user = User.new(user_params)
if user.save
payload = { user_id: user.id }
session = JWTSessions::Session.new(payload: payload, refresh_by_access_allowed: true)
tokens = session.login
response.set_cookie(JWTSessions.access_cookie,
value: tokens[:access],
httponly: true,
secure: Rails.env.production?)
render json: { csrf: tokens[:csrf] }
else
render json: { error: user.errors.full_messages.join(' ') }, status: :unprocessable_entity
end
end
private
def user_params
params.permit(:first_name, :last_name, :username, :email, :password, :password_confirmation)
end
end
UsersController:
class Api::V1::UsersController < ApplicationController
before_action :find_user, only: [:update, :show, :destroy]
def index
@users = User.all
render json: @users, status: :accepted
end
def create
@user = User.new(user_params)
if @user.save
render json: {user: UserSerializer.new(@user), token: Rails.application.credentials.jwt}, status: :ok
else
render json: {errors: @user.errors.full_messages}
end
end
def show
if @user
if curr_user.id == @user.id
render json: @user
else
render json: {errors: @user.errors.full_messages }, status: :unprocessible_entity
end
else
render json: {errors: "User not found!"}
end
end
def update
if curr_user.id == @user.id
@user.update(user_params)
if @user.save
render json: @user, status: :accepted
else
render json: { errors: @user.errors.full_messages }, status: :unprocessible_entity
end
end
end
def destroy
if curr_user.id == @user.id
@user.delete
render json: "user deleted", status: :ok
else
render json: { errors: "You are not authorized to delete"}
end
end
private
def user_params
params.permit(:first_name, :last_name, :email, :username, :password_digest)
end
def find_user
@user = User.find(params[:id])
end
end
Routes
Rails.application.routes.draw do
# For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html
namespace :api do
namespace :v1 do
resources :users, only: [:index, :show, :create, :update, :destroy]
resources :categories, only: [:index, :show, :create, :update, :destroy]
resources :opportunities
resources :opportuniy_api, only: [:index, :show]
end
end
post 'refresh', controller: :refresh, action: :create
post 'signin', controller: :signin, action: :create
post 'signup', controller: :signup, action: :create
delete 'signin', controller: :signin, action: :destroy
end
It is showing up "Nil JSON Web Token" because rails is unable to find any authorization token in request.headers["Authorization"]. Going through the jwt_sessions readme file https://github.com/tuwukee/jwt_sessions you will find that "Headers must include Authorization: Bearer with access token." This is what you are missing in your case. Secondly, you don't need to use CSRF tokens in rails. As a Rails developer, you basically get CSRF protection for free. It starts with this single line in application_controller.rb, which enables CSRF protection:
protect_from_forgery with: :exception
I have tried the same and my code works fine:
Signup:
class SignupController < ApplicationController
skip_before_action :verify_authenticity_token
def create
user = User.new(user_params)
if user.save
payload = { user_id: user.id }
session = JWTSessions::Session.new(payload: payload, refresh_by_access_allowed: true)
tokens = session.login
response.set_cookie(JWTSessions.access_cookie,
value: tokens[:access],
httponly: true,
secure: Rails.env.production?)
render json: tokens
else
render json: { error: user.errors.full_messages.join(' ') }, status: :unprocessable_entity
end
end
private
def user_params
params.permit(:username, :email, :password, :password_confirmation)
end
end
Or you can make it even simpler: Signup
class SignupController < ApplicationController
def create
user = User.new(user_params)
if user.save
payload = { user_id: user.id }
session = JWTSessions::Session.new(payload: payload, refresh_by_access_allowed: true)
render json: session.login //or render json: session.login[:access]
else
render json: { error: user.errors.full_messages.join(' ') }, status: :unprocessable_entity
end
end
private
def user_params
params.permit(:email, :password, :password_confirmation, :signup)
end
end
application_controller:
class ApplicationController < ActionController::Base
protect_from_forgery prepend: true, with: :exception
include JWTSessions::RailsAuthorization
rescue_from JWTSessions::Errors::Unauthorized, with: :not_authorized
def index
render template: 'application'
end
private
def current_user
@current_user ||= User.find(payload['user_id'])
end
def not_authorized
render json: { error: 'Not authorized' }, status: :unauthorized
end
end
ajax call from frontend(vue) to rails: Inspect your token and you will get the access_token within it. Or render token[:access] from rails to frontend. Include this access token in the Header for every request you make to the rails api.
$.ajax({
beforeSend: function(xhr) {
xhr.setRequestHeader('Authorization', 'Bearer ' + <access_token>);
},
url: '/api/home'..
Hope this helps :)
I dit it, for me it worked
def current_user
return unless request.headers.include? "Authorization"
@current_user ||= User.find(payload['user_id'])
end
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.