Rails 7 API only app requiring session store with Devise - ruby-on-rails

I have a new app that I am trying to setup with devise and devise-jwt. For some reason my authenticate_user! call is causing an error because sessions have been disabled:
{"status":500,"error":"Internal Server Error","exception":"ActionDispatch::Request::Session::DisabledSessionError: Your application has sessions disabled. To write to the session you must first configure a session store"
Routes:
Rails.application.routes.draw do
namespace :api do
namespace :v1 do
resources :clients, only: [:show]
devise_for :users,
controllers: {
sessions: 'users/sessions',
registrations: 'users/registrations'
}
end
end
end
User
class User < ApplicationRecord
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable, :trackable and :omniauthable
devise :database_authenticatable,
:jwt_authenticatable,
jwt_revocation_strategy: JwtDenylist
end
Controllers:
class API::V1::Users::SessionsController < Devise::SessionsController
respond_to :json
private
def respond_with(resource, _opts = {})
render json: { message: 'Logged.' }, status: :ok
end
def respond_to_on_destroy
current_user ? log_out_success : log_out_failure
end
def log_out_success
render json: { message: "Logged out." }, status: :ok
end
def log_out_failure
render json: { message: "Logged out failure."}, status: :unauthorized
end
end
class Api::V1::ClientsController < ApplicationController
before_action :authenticate_api_v1_user!
def show
end
end
What else do I need to do? When I make a request to http://localhost:3000/api/v1/clients/ID I'd expect the response to be:
=> <html><body>You are being redirected.</body></html>%
rather than this 500 error.

I was able to find a solution on GitHub:
config.session_store :cookie_store, key: '_interslice_session'
config.middleware.use ActionDispatch::Cookies
config.middleware.use config.session_store, config.session_options
from this post

Related

How do you render jwt token using active model serializer for a user when they sign in / sign up?

I am using Rails API and Active model serializers to render custom JSON responses on a user when they sign up / sign in and don't know how to add generated JWT tokens to this so I can retrieve it for further requests using Postman. When I send user sign up information I need to see this token in the response with everything else.
Configuration
Rails API only
Devise for user auth
jwt gem for tokens
Postman for API requests
Here is my code:
User serializer
class UserSerializer < ActiveModel::Serializer
attributes :id, :first_name, :last_name, :email, :username, :bio, :avatar, :created_at, :updated_at
end
user model
class User < ActiveRecord::Base
include ActiveModel::Validations
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :validatable,
:trackable
has_one_attached :avatar
def full_name
"#{first_name} #{last_name}"
end
def generate_jwt
JWT.encode({ id: id,
exp: 60.days.from_now.to_i },
Rails.application.credentials.secret_key_base)
end
end
application_controller.rb
class ApplicationController < ActionController::API
respond_to :json
before_action :configure_permitted_parameters, if: :devise_controller?
before_action :authenticate_user
private
def configure_permitted_parameters
devise_parameter_sanitizer.permit(:account_update, keys: [:first_name, :last_name, :username, :bio, :avatar])
devise_parameter_sanitizer.permit(:sign_up, keys: [:first_name, :last_name])
end
def authenticate_user
if request.headers['Authorization'].present?
authenticate_or_request_with_http_token do |token|
begin
jwt_payload = JWT.decode(token, Rails.application.secrets.secret_key_base).first
#current_user_id = jwt_payload['id']
rescue JWT::ExpiredSignature, JWT::VerificationError, JWT::DecodeError
head :unauthorized
end
end
end
end
def authenticate_user!(options = {})
head :unauthorized unless signed_in?
end
def current_user
#current_user ||= super || User.find(#current_user_id)
end
def signed_in?
#current_user_id.present?
end
end
routes.rb
Rails.application.routes.draw do
scope :api, defaults: { format: :json } do
devise_for :users, controllers: { sessions: :sessions },
path_names: { sign_in: :login }
resource :user, only: [:show, :update]
end
end
Assuming that you have user information stored in #user instance and the token stored in #token instance variable.
Since you have not mentioned the routes, I'll assume that you want to create a user
def create
#user = User.create(user_params)
if #user.save
render json: { user: UserSerializer.new(#user), token: #token }, status: :created
else
render json: #user.errors.full_messages, status: :bad_request
end
end
private
def user_params
# stuff
end

Rails Devise methods & helpers don't work

I have inherited an application built with Rails (v. 4.2.0) + AngularJS. Most of it works reasonably well, but I have hit a wall with Devise (v .3.5.1) that I am unable to overcome.
The application is structured this way: Angular.js handles the client side and is served from the back by the Rails app through an API. (The structure is very similar to the one from this tutorial: https://thinkster.io/angular-rails).
The problem if that for some reason, the app does not recognize helper methods of devise such as: current_user or user_signin? among others. Currently, anyone could make a request to /api/users and get a JSON with the information.
For instance, if we add this code to the /controllers/api/employees_controller.rb and you make a request in your browser to /api/employees/:id you get the JSON despite nos being logged
def show
#employee = Employee.find(params[:id])
respond_to do |format|
format.json{
if user_signed_in? then
render text: #employee.to_json, status: 200
else
render :json => nil, status: 422
end
}
end
end
Note: in this application the entity "Employee" is a type of "User"
I have tried solutions named in this post Rails devise: user_signed_in? not working but it didn't work for me
Here are all the parts of the code that I think could offer some light to this issue
/controllers/api/users/sessions_controller.rb
module Api
class Users::SessionsController < Devise::SessionsController
before_filter :configure_sign_in_params, only: [:create]
#Devise couldn't find "users_url" so just defining it here (500 error)
def users_url
:root_path
end
# GET /resource/sign_in
def new
super
end
# POST /resource/sign_in
def create
user = User.new
if ( params[:user][:email] == "" || params[:user][:password] == "") then
render json: {errors: "Insufficient data provided (user and/or password)"}.to_json, status: :bad_request
elsif User.find_by_email(params[:user][:email]).present?
user = User.find_by_email(params[:user][:email])
if user.valid_password?(params[:user][:password]) then
sign_in :user, user
render json: user.to_json(only: [:id, :email, :name, :last_name, :type, :company_id,:configuration,:phone]), status: :created
else
render json: {errors: "Wrong password."}.to_json, status: :unauthorized
end
else
render json: {errors: "Could not find user with that email."}.to_json, status: :unauthorized
end
end
# DELETE /resource/sign_out
def destroy
puts "HEEEEY"
end
# protected
# If you have extra params to permit, append them to the sanitizer.
def configure_sign_in_params
devise_parameter_sanitizer.for(:sign_in) << :attribute
end
end
end
models/user.rb
class User < ActiveRecord::Base
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable#, :confirmable
scope :admins, -> { where(admin: true) }
validates :email, presence: true
validates :name, presence: true, length: { in: 1..50 }
end
config/routes.rb
Rails.application.routes.draw do
namespace :api, defaults: {format: :json} do
resources :contacts
resources :job_application
# devise_for :admins, controllers: {sessions: 'admins/sessions'}
# devise_for :employees, controllers: {sessions: 'employees/sessions'}
devise_for :users, controllers: {
sessions: 'api/users/sessions',
registrations: 'api/users/registrations',
}
devise_for :employees, controllers: {
sessions: 'api/employees/sessions',
registrations: 'api/employees/registrations',
}
If you need any other part of the code, please just let me know.
Thank you very much!

Rails Beginner NoMethodError in PinsController#new

having issues keep getting error undefined method `pins' for nil:NilClass, can someone explain how to fix this error. Been following a tutorial and recently got stuck.
pins_controller.rb
class PinsController < ApplicationController
before_action :find_pin, only: [:show, :edit, :update, :destroy]
def index
#pins = Pin.all.order("created_at DESC")
end
def new
#pin = current_user.pins.build
end
def create
#pin = current_user.pins.build(pin_params)
if #pin.save
redirect_to #pin, notice: "Successfully created new Pin"
else
render 'new'
end
end
def edit
end
def update
if #pin.update(pin_params)
redirect_to #pin, notice: "Pin was Successfully updated!"
else
render 'edit'
end
end
def destroy
#pin.destroy
redirect_to root_path
end
private
def pin_params
params.require(:pin).permit(:title, :description)
end
def find_pin
#pin = Pin.find(params[:id])
end
end
user.rb
class User < ActiveRecord::Base
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
has_many :pins
end
pin.rb
class Pin < ActiveRecord::Base
belongs_to :user
end
routes.rb
Rails.application.routes.draw do
devise_for :users
resources :pins
root "pins#index"
end
If you are using the gem Devise then the helper method current_user returns you the current logged in user in your session.
Pleases make sure a user is logged in/
You can make sure that the user is logged in by adding before_action :authenitcate_user! in your application controller(for all actions in your application).
If you are using devise, you might want to add
before_action :authenitcate_user!
method inside pins controller which ensures that the user is logged in.

Devise: NameError (undefined local variable or method `user_id=') in controller when using `current_user`

I'm using Devise in my Rails app. A Project belongs to a (devise) User. (Relevant models are below.)
When the create action is called in ProjectsController, however, the following error is shown in the server logs:
NameError (undefined local variable or method `user_id=' for #<Project:0x007f4c1a0aa3a8>):
app/controllers/projects_controller.rb:20:in `create'
Any ideas?
Project.rb
class Project < ActiveRecord::Base
belongs_to :user
has_many :timestamps
end
User.rb
class User < ActiveRecord::Base
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
has_many :projects
end
projects_controller.rb (relevant parts)
class ProjectsController < ApplicationController
before_action :authenticate_user!
def create
#project = current_user.projects.new(project_params)
respond_to do |format|
if #project.save
format.html { redirect_to #project, notice: 'Project was successfully created.' }
format.json { render :show, status: :created, location: #project }
else
format.html { render :new }
format.json { render json: #project.errors, status: :unprocessable_entity }
end
end
end
routes.rb
Rails.application.routes.draw do
root 'static_pages#home'
devise_for :users
resources :projects do
resources :timestamps
end
end
It's the #project = current_user line that's causing the problem. Looks like it isn't recognising the method, which is weird.
Thanks for the help :)
Just in case anyone else makes the same mistake and doesn't think to check their db schema, here's what was causing the problem.
I'd forgotten to add a foreign key (user_id) to the projects table, even though I'd added the belongs_to association in the model.
To fix the problem, I ran the following migration:
class AddUserToProjects < ActiveRecord::Migration
def change
add_reference(:projects, :user, foreign_key: true, index: true)
end
end
and then of course ran rake db:migrate

NoMethodError: undefined method 'user' for nil:NilClass

I'm working through the book APIs on Rails and am super stuck in chapter 5 trying to test the sessions controller. I'm getting the following error and can't seem to track it down. Is there a good method for hunting down these kinds of error? And what am I missing?
1) Api::V1::SessionsController POST #create when the credentials are correct returns the user record corresponding to the given credentials
Failure/Error: post :create, { session: credentials }
NoMethodError:
undefined method `user' for nil:NilClass
App is in Rails 4.0.2, Ruby 2.2.1
Here is my test:
require 'spec_helper'
describe Api::V1::SessionsController do
describe "POST #create" do
before(:each) do
#user = FactoryGirl.create :user
end
context "when the credentials are correct" do
puts #user
before(:each) do
credentials = { email: #user.email, password: "12345678" }
post :create, { session: credentials }
end
it "returns the user record corresponding to the given credentials" do
#user.reload
expect(json_response[:auth_token]).to eql #user.auth_token
end
it { should respond_with 200 }
end
end
end
Here is the Sessions Controller:
class Api::V1::SessionsController < ApplicationController
respond_to :json
def create
user_password = params[:session][:password]
user_email = params[:session][:email]
user = user_email.present? && User.find_by(email: user_email)
if user.valid_password? user_password
sign_in user, store: false
user.generate_authentication_token!
user.save
render json: user, status: 200, location: [:api, user]
else
render json: { errors: "Invalid email or password" }, status: 422
end
end
end
The User Controller:
class Api::V1::UsersController < ApplicationController
respond_to :json
def show
respond_with User.find(params[:id])
end
def create
user = User.new(user_params)
if user.save
render json: user, status: 201, location: [:api, user]
else
render json: { errors: user.errors }, status: 422
end
end
def update
user = User.find(params[:id])
if user.update(user_params)
render json: user, status: 200, location: [:api, user]
else
render json: { errors: user.errors }, status: 422
end
end
def destroy
user = User.find(params[:id])
user.destroy
head 204
end
private
def user_params
params.require(:user).permit(:email, :password, :password_confirmation)
end
end
The routes.rb:
require 'api_constraints'
MarketPlaceApi::Application.routes.draw do
mount SabisuRails::Engine => "/sabisu_rails"
devise_for :users
# Api definition
namespace :api, defaults: { format: :json }, constraints: { subdomain: 'api' }, path: '/' do
scope module: :v1, constraints: ApiConstraints.new(version: 1, default: true) do
resources :users, :only => [:show, :create, :update, :destroy]
resources :sessions, :only => [:create, :destroy]
end
end
end
And the user model:
class User < ActiveRecord::Base
validates :auth_token, uniqueness: true
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
before_create :generate_authentication_token!
def generate_authentication_token!
begin
self.auth_token = Devise.friendly_token
end while self.class.exists?(auth_token: auth_token)
end
end
Have you added devise_helper to spec/rails_helper.rb ?
RSpec.configure do |config|
...
config.include Devise::TestHelpers, type: :controller
...
end
Change
if user.valid_password? user_password
to:
if user and valid_password? user_password
or:
if user && valid_password?(user_password)
in your sessions_controller.rb file.
Do you have a User factory?
FactoryGirl.define do
factory :user do
username user
email user#user.com
password '12345678'
password_confirmation { '12345678' }
end
end
You need to have it defined so that FactoryGirl can create a user

Resources