I am using devise token authentication(devise_token_auth) for user login and signup based on token because it is only for developing an api. I need to make the user logged in soon after the user registers his/her account. How can i do it so? I have tried but could not succeed so i am here with the hope of help.
class Users::RegistrationsController < DeviseTokenAuth::RegistrationsController
prepend_before_action :configure_permitted_parameters
protected
def configure_permitted_parameters
devise_parameter_sanitizer.permit(:account_update, keys: [:id, :first_name, :last_name, :phone, :status])
devise_parameter_sanitizer.permit(:sign_up, keys: [:confirm_success_url])
end
private
def user_params
params.permit.all #(:id, :email, :firstname, :lastname, :phone, :status)
end
end
Rails.application.routes.draw do
# devise_for :users, controllers: { confirmations: 'confirmations' }
mount_devise_token_auth_for 'User', at: 'auth', controllers: {
# confirmations: 'confirmations',
registrations: 'users/registrations',
passwords: 'users/passwords'
}
class ApplicationController < ActionController::API
before_action :authenticate_user!
include DeviseTokenAuth::Concerns::SetUserByToken
def authenticate_current_user
head :unauthorized if get_current_user.nil?
end
def get_current_user
return nil unless cookies[:auth_headers]
auth_headers = JSON.parse cookies[:auth_headers]
puts('################################')
puts('auth_headers', auth_headers)
expiration_datetime = DateTime.strptime(auth_headers["expiry"], "%s")
current_user = User.find_by(uid: auth_headers["uid"])
if current_user &&
current_user.tokens.has_key?(auth_headers["client"]) &&
expiration_datetime > DateTime.now
#current_user = current_user
end
#current_user
end
end
I tried this below code
def after_sign_up_path_for(resource)
puts('it should be shown')
puts('################################')
puts('resource', resource)
puts('header', request.headers['client'])
client_id = request.headers['client']
new_auth_header = #resource.create_new_auth_token(client_id)
response.headers.merge!(new_auth_header)
end
in the Users::RegistrationController but it is not executed at all after successfully signing up.
Not too complicated. After creating the user, just call sign_in and pass the resource (user).
https://github.com/plataformatec/devise/wiki/How-To:-Sign-in-from-a-controller
Example:
sign_in #current_user
The Devise::RegistrationController in fact, already does this.
Furthermore, since you're using the DeviseTokenAuth gem and the DeviseTokenAuth::RegistrationsController controller inherits from Devise's own base controller, you (should) have access to all the helpers that Devise controllers do.
One implementation might look like this.
class Users::RegistrationsController < DeviseTokenAuth::RegistrationsController
def create
super do |resource|
sign_in(resource)
end
end
end
Related
I recently created an errors_controller.rb file which basically will route all non-existing page even those unathorized page by non-admin users to a 404 page:
class ErrorsController < ApplicationController
def page_not_found
show_404
end
end
And then on my routes.rb file:
match '*path', via: :all, to: 'errors#page_not_found'
I am using Pundit gem for my authorization and here's my users policy:
class UserPolicy < ApplicationPolicy
class Scope < Scope
def resolve
scope.all
end
end
def index?
user.admin?
end
end
Now the problem is whenever I visits a page that is non existing it shows me this:
But if a non-admin user visit a page that is unathorized to access (for instance localhost:3000/users-list), it's showing this:
How do I make sure to show the 404 page or a specific page on my public folder whenever an unathorized user visits a page that is can only access by an admin?
UPDATE: Here's the content of my application_controller.rb file:
class ApplicationController < ActionController::Base
include Pundit
protect_from_forgery with: :exception
before_action :configure_permitted_parameters, if: :devise_controller?
rescue_from Pundit::NotAuthorizedError, with: :user_not_authorized
rescue_from ActiveRecord::RecordNotFound, with: :show_404
def after_sign_in_path_for(resource)
stored_location_for(resource) || contacts_path
end
def after_sign_up_path_for(resource)
after_sign_in_path_for(resource)
end
protected
def configure_permitted_parameters
devise_parameter_sanitizer.permit(:sign_up) { |u| u.permit(:user_avatar, :name, :email, :password, :password_confirmation)}
devise_parameter_sanitizer.permit(:account_update) { |u| u.permit(:user_avatar, :name, :email, :password, :current_password)}
end
private
def user_not_authorized
flash[:danger] = "You are not authorized to perform this action."
redirect_to action: :index
end
def show_404
render template: "errors/404", status: 404
end
end
You should rewrite user_not_authorized on controller, that you call when a non-admin user visit a page that is unathorized to access
def user_not_authorized
show_404
end
I am using devise for my clients authentication, however, when I hit the sign up button in my view it throws me that I am already signed in, how so? I don't even have users in my database.
routes.rb
Rails.application.routes.draw do
devise_for :clients, controllers: { registrations: 'clients/registrations', sessions: 'clients/sessions' }
root to: "mainpages#index"
get '/mainpages', controller: 'mainpages', action: 'index'
get '/planes', controller: 'planes', action: 'planes'
resources :planes
My controllers are inside of controllers/clients, At the moment I want to activate all the related with registration and sessions, so here's my registration controller
registrations_controller
class Clients::RegistrationsController < Devise::RegistrationsController
before_action :configure_sign_up_params, only: [:create]
before_action :configure_account_update_params, only: [:update]
# GET /resource/sign_up
def new
super
#client = Client.new
end
# POST /resource
def create
super
#client = Client.new(client_params)
#Set user perms to provider or to client
if #client.perms == false
#client.perms = "Client"
else
#client.perms = "Provider"
end
#client.status = "Not activated"
if #client.save
return redirect_to :root
end
render 'new'
end
protected
def configure_sign_up_params
devise_parameter_sanitizer.permit(:sign_up, keys: [:attribute])
end
# The path used after sign up.
def after_sign_up_path_for(resource)
super(resource)
end
private
def client_params
params.require(:client).permit(:name, :last_name, :email, :country, :username, :city, :address, :date, :perms, :status)
end
end
sessions_controller
# frozen_string_literal: true
class Clients::SessionsController < Devise::SessionsController
# before_action :configure_sign_in_params, only: [:create]
# GET /resource/sign_in
def new
super
end
# POST /resource/sign_in
def create
super
end
# DELETE /resource/sign_out
def destroy
super
end
protected
# If you have extra params to permit, append them to the sanitizer.
def configure_sign_in_params
devise_parameter_sanitizer.permit(:sign_in, keys: [:attribute])
end
end
index.html.erb views/mainpages/
<div class="col-sm-6"><h4>Not a member?</h4>
<%= link_to '<p>You can create an account:</p>'.html_safe, controller: "clients/registrations", action:"new" %>
<p class="text-center">
<%= link_to '<i class="fas fa-sign-in-alt big-icon"></i>'.html_safe, controller: "clients/registrations", action:"new" %>
</p>
</div>
I tried to manually sign out by typing localhost:3000/clients/sign_out but I get a routing error, my link to the sign up page doesn't work if I don't get rid of that session, tried to restart the server but I still get the same error.
I've been trying to customize the devise register method to register with more parameters and also update more(no luck so far), but I always get Unpermitted parameters: error. I tried using this Adding extra registration fields with Devise and https://github.com/plataformatec/devise#strong-parameters, but I cant get over that.
I've also thought about creating a new table to hold a foreign key the user id and put in there stuff like user_id, display_name, profile_picture, but I would have the same problem when trying to submit everything from the same page(mess with the devise controller).
Do you have any suggestions on how I can solve this? What else do I have to post?
routes.rb
devise_for :users, controllers: { registrations: 'users/registrations' }
users/regC
def create
build_resource(registration_params)
if resource.save
if resource.active_for_authentication?
set_flash_message :notice, :signed_up if is_navigational_format?
sign_up(resource_name, resource)
respond_with resource, :location => after_sign_up_path_for(resource)
else
set_flash_message :notice, :"signed_up_but_#{resource.inactive_message}" if is_navigational_format?
respond_with resource, :location => after_sign_up_path_for(resource)
end
else
clean_up_passwords
respond_with resource
end
end
private
def registration_paramss
params.require(:user).permit(:email, :display_name, :terms_of_services, :profile, :password, :password_confirmation)
end
Looks like you just need to tell devise which parameters should be permitted. By default, devise permits the email (or username depending on configuration), password and password_confirmation params. You just need to add more.
The devise documentation suggests a "lazy way" of setting this up.
class ApplicationController < ActionController::Base
before_action :configure_permitted_parameters, if: :devise_controller?
protected
def configure_permitted_parameters
devise_parameter_sanitizer.permit(:sign_up, keys: [:display_name])
end
end
The documentation then says that
If you have nested attributes (say you're using accepts_nested_attributes_for), then you will need to tell devise about those nestings and types.
Only if you need to override the registrations#create action you should provide your custom route for devise. In that case, make sure you override the sign_up_params method too.
class Users::RegistrationsController < Devise::RegistrationsController
def create
# Your custom code here. Make sure you copy devise's functionality
end
private
# Notice the name of the method
def sign_up_params
params.require(:user).permit(:display_name, :email, :password, :password_confirmation)
end
end
In essence, you'd have to look into how your sign up form is posting the parameters to figure out how to configure strong parameters in the controller. Make sure you read on strong parameters syntax as well.
Hope it helps!
For Devise 4.2.0 you can whitelist additional parameters for your users table by adding those values to keys. By default devise gives you the comment to go off of now. Below I added :avatar
# If you have extra params to permit, append them to the sanitizer.
def configure_sign_up_params
devise_parameter_sanitizer.permit(:sign_up, keys: [:attribute, :avatar])
end
The accepted answer says the config should go in your applicationController but it can simply go in your user registration controller and you can specify that you only want to run it for create method and nothing else:
class Users::RegistrationsController < Devise::RegistrationsController
before_action :configure_sign_up_params, only: [:create]
protected
def configure_sign_up_params
devise_parameter_sanitizer.permit(:sign_up, keys: [:enter_param_name_here])
end
end
In my case this worked:
class ApplicationController < ActionController::Base
before_action :configure_permitted_parameters, if: :devise_controller?
protected
def configure_permitted_parameters
devise_parameter_sanitizer.permit(:account_update) { |u| u.permit(:name, :last_name, :image,:email, :password, :password_confirmation, :current_password) }
end
end
I have a user model which uses Devise for authentication and also have an administrator model, which also uses Devise.
I want administrators to be able to edit users profile via administrators/users/{user.id}/edit, however I want this process to be done through Devise Controllers, therefore I tried to inherit from the Users::RegistrationsController as shown below:
class Administrators::UsersController < Users::RegistrationsController
before_action :set_user, only: [:show,:edit,:update,:destroy]
def index
#users=User.all
end
def show
end
def new
super
end
def update
#user.update(user_params)
redirect_to [:administrators,:users]
end
but I get the following error:
Could not find devise mapping for path "/administrators/users". This may happen for two reasons: 1) You forgot to wrap your route inside the scope block. For example: devise_scope :user do get "/some/route" => "some_devise_controller" end 2) You are testing a Devise controller bypassing the router. If so, you can explicitly tell Devise which mapping to use: #request.env["devise.mapping"] = Devise.mappings[:user]
I tried to change the routes but I still get the same error.
Could you please help me?
Inheriting from Devise::RegistrationsController may initially seem like a good idea from a code reuse standpoint but it really not a very good idea.
The intent of the controllers is very different - Devise::RegistrationsController partially deals with an un-authenicated user - and the Devise controllers are scary beasts due to the amount of flexibility built in Devise.
Instead you should just setup a plain old CRUD controller as the task at hand is not very complex compared to clobbering over half of Devise::RegistrationsController.
# config/routes.rb
namespace :administrators do
resources :users
end
# app/controllers/administrators/base_controller.rb
module Administrators
class AuthorizationError < StandardError; end
class BaseController
respond_to :html
before_action :authenticate_user!
# Replace with the lib of your choice such as Pundit or CanCanCan
before_action :authorize_user!
rescue_from AuthorizationError, with: :unauthorized
private
def authorize_user!
raise AuthorizationError and return unless current_user.admin?
end
def unauthorized
redirect_to new_session_path, alert: 'You are not authorized.'
end
end
end
class Administrators::UsersController < Administrators::BaseController
before_action :set_user, only: [:show, :edit, :update, :destroy]
def show
end
def index
#users = User.all
end
def new
#user = User.new
end
def create
#user = User.create(user_params)
respond_with(:administrators, #user)
end
def edit
end
def update
#user.update(user_params)
respond_with(:administrators, #user)
end
def destroy
#user.destroy
respond_with(:administrators, #user)
end
private
def user_params
params.require(:user).permit(:email, :password, :password_confirmation)
end
end
Instead you may want to focus on reusing the views through partials for example.
See:
ActionController::Responder
Pundit
CanCanCan
I have Devise Admin & Devise User;
I want to use namespaces;
What I want to achieve:
only devise admin can create devise user
registerable for user is not deleted so that he can edit only page
user can see only current_user/show page
What I have
routes:
Rails.application.routes.draw do root :to => 'dashboard#index'
devise_for :users, controllers: { registrations: 'user_registrations' }
devise_for :admins, controllers: { registrations: 'admin_registrations' }
get 'dashboard/index'
namespace :admin do
root 'dashboard#index'
resources :users
end
user_registration_controller:
class UserRegistrationsController < Devise::RegistrationsController
end
users_controller:
class UsersController < ApplicationController
def index
#users = User.all
end
def show
#user = User.find(params[:id])
end
def new
#user = User.new
end
def edit
end
def create
#user = User.new(user_params)
respond_to do |format|
if #guest.save
format.html { redirect_to users_path }
else
format.html { render :new }
end
end
end
def update
respond_to do |format|
if #user.update(user_params)
format.html { redirect_to #user }
else
format.html { render :edit }
end
end
end
def destroy
user = User.find(params[:id])
user.destroy
redirect_to users_path
end
private
def set_user
#user = User.find(params[:id])
end
def user_params
params.require(:user).permit(:email, :password, :password_confirmation)
end
end
+ I have user views as they would be in a normal scaffold.
=> With this setup, anyone can create a user
Any ideas how to solve the questions on top?..
Don't use separate user classes with Devise, use roles instead. Devise is only really made to authenticate a single class, while you can hack it into using two classes its a mess. You have to override all the logic of serializing/desearializing users from the session among other things so that devise knows if it should load the Admin or User class.
Its also a bad solution since you are push down a authorization problem into the authentication layer. Devise's job is to verify that the user is who she/he claims to be, which is no small feat. Authorization, on the other hand is rules about what a user can do. "Only admins can create users" is a clear cut authorization rule.
The simplest possible role based authorization would be something like this:
class AddRoleToUser < ActiveRecord::Migration
def change
add_column :users, :role, :integer, default: 0
add_index :users, :role
end
end
class User
# ...
enum role: [:visitor, :admin]
end
We use an enum which is a single bitmask column to store the users role. Declaring it as an enum column also gives us a few methods for free:
user.visitor?
user.admin?
user.admin!
So lets create a basic authorization check:
def create
unless current_user.admin?
redirect_to root_path, status: 401, error: 'You are not authorized to perform this action' and return
end
# ...
end
But we don't want to repeat that every time we want to authorize, so lets clean it up:
class AuthorizationError < StandardError; end
class ApplicationController
rescue_from AuthorizationError, with: :deny_access!
private
def authorize_admin!
raise AuthorizationError, unless current_user.admin?
end
def deny_access!
redirect_to root_path,
status: 401,
error: 'You are not authorized to perform this action'
end
end
So then we can setup the controller with a filter to check the authorization before the action is performed:
class UsersController < ApplicationController
before_action :authorize_admin!, except: [:show]
# ...
end
However instead of reinventing the wheel you might want to have a look at Pundit or CanCanCan which are solid authorization libraries with great communities. You also might want to look at Rolify.