Showing posts with label devise. Show all posts
Showing posts with label devise. Show all posts

Wednesday, October 17, 2018

Rails Devise API - Login route responds with `You need to sign in or sign up before continuing.`

1 comment

I'm currently using Devise with my Rails API app to authenticate users using devise-jwt.

This is what my User model looks like:

class User < ApplicationRecord   devise :database_authenticatable,          :registerable,          :jwt_authenticatable,          jwt_revocation_strategy: JWTBlackList end 

And config/routes.rb is set up like this:

Rails.application.routes.draw do   devise_for :users,          path: '',          path_names: {            sign_in: 'login',            sign_out: 'logout',            registration: 'signup'          },          controllers: {            sessions: 'sessions',            registrations: 'registrations'          } end 

This is the sessions controller:

class SessionsController < Devise::SessionsController    private    def respond_with(resource, _opts = {})     render json: resource   end    def response_to_on_destroy     head :no_content   end end 

and the registrations controller:

class RegistrationsController < Devise::RegistrationsController   respond_to :json    def create     build_resource(sign_up_params)      resource.save     render_resource(resource)   end end 

I ran some Rspec tests shown below which were successful -

require 'rails_helper'  RSpec.describe SessionsController, type: :request do   let(:user) { create(:user) }   let(:url) { '/login' }   let(:params) do     {       user: {         email: user.email,         password: user.password       }     }   end    context 'when params are correct' do     before do       post url, params: params     end      it 'returns 200' do       expect(response).to have_http_status(200)     end      it 'returns JTW token in authorization header' do       expect(response.headers['Authorization']).to be_present     end      it 'returns valid JWT token' do       decoded_token = decoded_jwt_token_from_response(response)       expect(decoded_token.first['sub']).to be_present     end   end end 

But when I run the following POST request to /login on Postman I get the following message:

postman request

On the right hand side you are able to see the rails console and server, showing the credentials are correct, but still we get 401

Any clues on what might be wrong? It's been difficult finding good resources on Devise using Rails API.

Thank you in advance

1 Answers

Answers 1

After digging through the SessionsController I found the reason it returned 401: warden was trying to authenticate an empty body. Fixed this by added Content-Type to the request header

Read More

Monday, July 9, 2018

Return JWT token when user signs in rails

Leave a Comment

Having an issue with getting JWT token using devise gem and devise-jwt gem. This is how my confirmation looks like.

devise.rb

  Devise.setup do |config|      config.jwt do |jwt|       jwt.secret =  SECRETS.devise_jwt_secret_key       jwt.dispatch_requests = [ ['POST', %r{^/authentication_tokens/create$}] ]     end end  

user.rb

class User < ApplicationRecord      devise :database_authenticatable, :registerable,            :recoverable, :rememberable, :trackable, :validatable,            :jwt_authenticatable, jwt_revocation_strategy: Devise::JWT::RevocationStrategies::Null    end 

authentication_tokens_controller.rb

class Api::V1::AuthenticationTokensController < Devise::SessionsController   include Devise::Controllers::Helpers   skip_before_action :verify_authenticity_token     prepend_before_action :require_no_authentication, only: [:create]    before_action :rewrite_param_names, only: [:create]    def new     render json: { response: "Authentication required" }, status: 401   end    def create     self.resource = warden.authenticate!(auth_options)     sign_in(resource_name, resource)     yield resource if block_given?      render json: {success: true, jwt: current_token, response: "Authentication successful" }   end    private    def rewrite_param_names     request.params[:user] = {email: request.params[:email], password: request.params[:password]}   end    def current_token     request.env['warden-jwt_auth.token']   end  end 

routes.rb

   get 'home#secret'    devise_for :users    resources :tasks    #other routes for the website removed for brevity    namespace :api, defaults: { format: :json } do     namespace :v1 do       resources :users       devise_scope :user do         post '/authentication_tokens/create', to: "authentication_tokens#create"       end     end   end 

For some reason request.env['warden-jwt_auth.token'] returns null all the time, however, the user is authenticated. Is there anything that I need to add to get the JWT token when a user signs in?

Update - routes and namespacing

After days of debugging, I believe I have found the source of my problem. My app has a frontend which uses normal routes. The code above doesn't work however if I do something like the code below. All is good.

  scope :api, defaults: {format: :json} do     devise_for :users, controllers: {sessions: 'v1/authentication_tokens'}   end 

Is there a way of namespacing the devise_for for me API even though it has been used above for the website?

2 Answers

Answers 1

I've briefly looked on your issue and, it's probably wrong, but something for you to give a try:

looking on the following lines

def create   self.resource = warden.authenticate!(auth_options) end  def current_token   request.env['warden-jwt_auth.token'] end 

If you say that user is being authenticated even with nil returned from current_token method, so that means that jwt is passing correctly, but your way of fetching it is wrong.

Try to debug self.resource = warden.authenticate!(auth_options) line and see what contains inside auth_options, probably you can take JWT from there, or you just trying to get warden-jwt_auth.token in a wrong way. Try to debug this line as well and see if you should probably take "warden-jwt_auth.token" from request.headers["warden-jwt_auth.token"], or something like this. Just print out the whole response of your request and search by needed header.

I hope this helps!

Answers 2

You just need to make your route RESTful.

routes.rb

post '/authentication_tokens', to: "authentication_tokens#create" 

devise.rb

config.jwt do |jwt|   jwt.secret =  SECRETS.devise_jwt_secret_key   jwt.dispatch_requests = [ ['POST', %r{^/authentication_tokens$}] ] end 
Read More

Friday, June 1, 2018

How to use devise-jwt with devise for signin, signup and signout in rails api

Leave a Comment

I am using rails for the backend using devise-jwt and react for the frontend part.

I am following this https://github.com/waiting-for-dev/devise-jwt/blob/master/README.md

my routes.rb file contains:

 Rails.application.routes.draw do   # remove this in production   require 'sidekiq/web'   mount Sidekiq::Web => '/sidekiq'    namespace :api, defaults: { format: 'json' } do     namespace :v1 do       devise_for :users, :controllers => {sessions: 'api/v1/sessions', registrations: 'api/v1/registrations'}     end   end end 

my registrations_controller.rb (app/controllers/api/registrations_controller.rb)

class Api::V1::RegistrationsController < Devise::RegistrationsController   respond_to :json, :controllers => {sessions: 'sessions', registrations: 'registrations'}    before_action :sign_up_params, if: :devise_controller?, on: [:create]    def create     build_resource(sign_up_params)      if resource.save       render :json => resource, serializer: Api::V1::UserSerializer, meta: { message: 'Sign up success', token: request.headers["Authorization"] }, :status => :created     else       render :json => resource, adapter: :json_api, serializer: ActiveModel::Serializer::ErrorSerializer, meta: { message: 'Sign up success' }, :status => :created     end   end     protected    def sign_up_params     params.require(:sign_up).permit(:first_name, :last_name, :mobile, :email, :password, :password_confirmation)   end end 

my sessions_controller.rb (app/controllers/api/sessions_controller.rb)

class Api::SessionsController < Devise::SessionsController     respond_to :json end 

my application_controller.rb (app/controllers/application_controller.rb)

class ApplicationController < ActionController::Base end 

Basically what will be the next step to acees the token. I am confused. How will i get the acess token and use it to authenticate in the frontend react part.

1 Answers

Answers 1

Assuming you have your server-side setup the response will include an Authorization Header.

On the front-end you'll make request to sign in and have a callback to catch the response:

 window.fetch(LOGIN_URL, dataWithLoginInfo).then(response => {     const jwt = response.headers.get('Authorization').split('Bearer ')[1];     window.sessionStorage.setItem('jwt', jwt);   }).catch(handleError) 

Next make the requests with the Authorization header included:

const token =  window.sessionStorage.getItem('jwt') const headers = { Authorization: `Bearer ${token}` } 

or use it in your app after you decode it:

import jwt from 'jsonwebtoken'; const decodedToken = jwt.decode(window.sessionStorage.getItem('jwt'));  if (decodedToken.isAdmin) {   return <AdminPage />; } else {   return <NotAdminPage />; } 

You'll use something like https://www.npmjs.com/package/jwt-decode or https://www.npmjs.com/package/jsonwebtoken to decode the token and read the information from it like id, roles, permissions, etc.

You really need to follow a tutorial like: https://auth0.com/blog/secure-your-react-and-redux-app-with-jwt-authentication/ or http://jasonwatmore.com/post/2017/12/07/react-redux-jwt-authentication-tutorial-example. Then have some local expert take a look at all your code.

Read More

Thursday, April 19, 2018

URI::InvalidComponentError (bad component(expected scheme component): : https):

Leave a Comment

I'm working on a Ruby on Rails web app and I'm using Devise for user/password and OmniAuth for authentication using social media accounts. And I'm also using Nginx.

Authentication with username/password worked perfectly. But when I added ssl certificate to Nginx. I'm now able to login. But when I logout I get the error message in the title URI::InvalidComponentError (bad component(expected scheme component): : https): and telling that I have an exception generated from:

def check_scheme(v)   if v && parser.regexp[:SCHEME] !~ v     raise InvalidComponentError,       "bad component(expected scheme component): #{v}"   end 

How can I fix this problem in order for all types of authentications to work?

UPDATE

It throws the same exception after almost every redirect withing the web app. But redirects anyway, event user login. But it never logs out

*UPDATE 2 *

This question has more detailed description of my issue

This other question is another try to fix the issue

UPDATE 3

When I tried adding config.force_ssl = true to my environment .rb file, I get "page not found" and the following in ssl.error.log

2018/04/13 05:05:14 [error] 7317#0: *553 connect() failed (111: Connection refused) while connecting to upstream, client: <my laptop ip>, server: vps37181, request: "GET / HTTP/1.1", upstream: "http://127.0.0.1:3000/", host: "<domain name>" 2018/04/13 05:05:14 [error] 7317#0: *553 open() "/var/www/<app-name>/public/50x.html" failed (2: No such file or directory), client: <my laptop ip>, server: vps37181, request: "GET / HTTP/1.1", upstream: "http://127.0.0.1:3000/", host: "<domain name>" 

UPDATE 4

My current nginx configuration at /etc/nginx/conf.d/ssl.conf is:

server {     listen 443 ssl;     server_name <server-name>;      client_max_body_size 15M;     ssl                  on;     ssl_certificate      <.pem file path>;     ssl_certificate_key  <.key file path>;     ...     root <"public" directory inside my app directory>;     access_log <ssl.access.log path>;     error_log  <ssl.error.log path>;      include /etc/nginx/default.d/*.conf;      location / {         proxy_pass http://127.0.0.1:3000;          proxy_set_header X-Real-IP  $remote_addr;         proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;         proxy_set_header Host $http_host;         proxy_set_header X-NginX-Proxy true;         proxy_set_header X-Forwarded-Proto: $scheme;  } 

UPDATE 5

After every request redirected from Nginx to the app, I get the following in the app console:

Cannot render console from <my laptop ip>! Allowed networks: 127.0.0.1, ::1, 127.0.0.0/127.255.255.255  URI::InvalidComponentError (bad component(expected scheme component): : https):  /home/tamer/.rvm/rubies/ruby-2.5.0/lib/ruby/2.5.0/uri/generic.rb:322:in `check_scheme' /home/tamer/.rvm/rubies/ruby-2.5.0/lib/ruby/2.5.0/uri/generic.rb:363:in `scheme=' actionpack (5.1.5) lib/action_dispatch/routing/redirection.rb:35:in `serve' actionpack (5.1.5) lib/action_dispatch/routing/redirection.rb:21:in `call' actionpack (5.1.5) lib/action_dispatch/routing/mapper.rb:17:in `block in <class:Constraints>' actionpack (5.1.5) lib/action_dispatch/routing/mapper.rb:46:in `serve' actionpack (5.1.5) lib/action_dispatch/journey/router.rb:50:in `block in serve' actionpack (5.1.5) lib/action_dispatch/journey/router.rb:33:in `each' actionpack (5.1.5) lib/action_dispatch/journey/router.rb:33:in `serve' actionpack (5.1.5) lib/action_dispatch/routing/route_set.rb:844:in `call' omniauth (1.8.1) lib/omniauth/strategy.rb:190:in `call!' omniauth (1.8.1) lib/omniauth/strategy.rb:168:in `call' omniauth (1.8.1) lib/omniauth/strategy.rb:190:in `call!' omniauth (1.8.1) lib/omniauth/strategy.rb:168:in `call' omniauth (1.8.1) lib/omniauth/strategy.rb:190:in `call!' omniauth (1.8.1) lib/omniauth/strategy.rb:168:in `call' omniauth (1.8.1) lib/omniauth/strategy.rb:190:in `call!' omniauth (1.8.1) lib/omniauth/strategy.rb:168:in `call' omniauth (1.8.1) lib/omniauth/strategy.rb:190:in `call!' omniauth (1.8.1) lib/omniauth/strategy.rb:168:in `call' omniauth (1.8.1) lib/omniauth/builder.rb:63:in `call' warden (1.2.7) lib/warden/manager.rb:36:in `block in call' warden (1.2.7) lib/warden/manager.rb:35:in `catch' warden (1.2.7) lib/warden/manager.rb:35:in `call' rack (2.0.4) lib/rack/etag.rb:25:in `call' rack (2.0.4) lib/rack/conditional_get.rb:25:in `call' rack (2.0.4) lib/rack/head.rb:12:in `call' rack (2.0.4) lib/rack/session/abstract/id.rb:232:in `context' rack (2.0.4) lib/rack/session/abstract/id.rb:226:in `call' actionpack (5.1.5) lib/action_dispatch/middleware/cookies.rb:613:in `call' activerecord (5.1.5) lib/active_record/migration.rb:556:in `call' actionpack (5.1.5) lib/action_dispatch/middleware/callbacks.rb:26:in `block in call' activesupport (5.1.5) lib/active_support/callbacks.rb:97:in `run_callbacks' actionpack (5.1.5) lib/action_dispatch/middleware/callbacks.rb:24:in `call' actionpack (5.1.5) lib/action_dispatch/middleware/executor.rb:12:in `call' actionpack (5.1.5) lib/action_dispatch/middleware/debug_exceptions.rb:59:in `call' web-console (3.5.1) lib/web_console/middleware.rb:135:in `call_app' web-console (3.5.1) lib/web_console/middleware.rb:20:in `block in call' web-console (3.5.1) lib/web_console/middleware.rb:18:in `catch' web-console (3.5.1) lib/web_console/middleware.rb:18:in `call' actionpack (5.1.5) lib/action_dispatch/middleware/show_exceptions.rb:31:in `call' railties (5.1.5) lib/rails/rack/logger.rb:36:in `call_app' railties (5.1.5) lib/rails/rack/logger.rb:24:in `block in call' activesupport (5.1.5) lib/active_support/tagged_logging.rb:69:in `block in tagged' activesupport (5.1.5) lib/active_support/tagged_logging.rb:26:in `tagged' activesupport (5.1.5) lib/active_support/tagged_logging.rb:69:in `tagged' railties (5.1.5) lib/rails/rack/logger.rb:24:in `call' sprockets-rails (3.2.1) lib/sprockets/rails/quiet_assets.rb:13:in `call' actionpack (5.1.5) lib/action_dispatch/middleware/remote_ip.rb:79:in `call' request_store (1.4.0) lib/request_store/middleware.rb:19:in `call' actionpack (5.1.5) lib/action_dispatch/middleware/request_id.rb:25:in `call' rack (2.0.4) lib/rack/method_override.rb:22:in `call' rack (2.0.4) lib/rack/runtime.rb:22:in `call' activesupport (5.1.5) lib/active_support/cache/strategy/local_cache_middleware.rb:27:in `call' actionpack (5.1.5) lib/action_dispatch/middleware/executor.rb:12:in `call' actionpack (5.1.5) lib/action_dispatch/middleware/static.rb:125:in `call' rack (2.0.4) lib/rack/sendfile.rb:111:in `call' railties (5.1.5) lib/rails/engine.rb:522:in `call' puma (3.11.2) lib/puma/configuration.rb:225:in `call' puma (3.11.2) lib/puma/server.rb:624:in `handle_request' puma (3.11.2) lib/puma/server.rb:438:in `process_client' puma (3.11.2) lib/puma/server.rb:302:in `block in run' puma (3.11.2) lib/puma/thread_pool.rb:120:in `block in spawn_thread' 

This message doesn't prevent the website from loading. But when user logs out (I'm using Devise for username/password user authentication). the website throws in the browser the error I mentioned earlier:

URI::InvalidComponentError bad component(expected scheme component): : https Extracted source (around line #322):  def check_scheme(v)   if v && parser.regexp[:SCHEME] !~ v     raise InvalidComponentError, # line 322       "bad component(expected scheme component): #{v}"   end 

and I get the console error message twice.

UPDATE 7

I'm running my server in development.

I dug deeper and I found that :

URI::InvalidComponentError (bad component(expected scheme component): : https) 

means that what was expected was : https while received https.

I tried modifying parser.regexp[:SCHEME] in check_shceme in /home/tamer/.rvm/rubies/ruby-2.5.0/lib/ruby/2.5.0/uri/generic.rb in which it approves : https also. But now, on logout, which supposed to send a request to

https://<my domain name>/users/log_out 

now redirectes to :

https://<my domain name>/users/:%20https://<my domain name>/:%20https://<my domain name>/:%20https://<my domain name>/:%20https://<my domain name>/:%20https://<my domain name>/:%20https://<my domain name>/:%20https://<my domain name>/:%20https://<my domain name>/:%20https://<my domain name>/:%20https://<my domain name>/:%20https://<my domain name>/:%20https://<my domain name>/:%20https://<my domain name>/:%20https://<my domain name>/:%20https://<my domain name>/:%20https://<my domain name>/:%20https://<my domain name>/:%20https://<my domain name>/:%20https://<my domain name>/:%20https://<my domain name>/ 

in which :%20 means whitespace.

0 Answers

Read More

Sunday, March 18, 2018

Devise redirect back on sign up failure with validations

Leave a Comment

I have a simple Devise registration form with the validatable plugin. It mostly works as intended, if the user forgets to enter a first name it redirects them back with a red validation message.

The problem is it redirects to the same path a successful login would have the user go to (i.e. it redirects them to /user and not back to /user/sign_up). If the user then refreshes the page for whatever reason they get a No route matches [GET] "/user" error.

How can I force a redirect to go back to the original /user/sign_up route on a sign up failure? I know I can hack the create action in the registration controller but when I redirect to the proper route I lose the Devise validation messages.


Update

It appears the problem is the way Devise handles respond_with Rails 4 How Overwrite Devise Respond Path Upon Error. It looks like Devise tries to render a create template at /user but I still can't override it.

2 Answers

Answers 1

First you can create devise controllers using following command -

rails generate devise:controllers users 

Then you have to modify your routes for devise

# config/routes.rb Rails.application.routes.draw do    devise_for :users,          :skip => [:registrations]    devise_scope :user do     get "user/sign_up", to: "users/registrations#new", as: :new_user_registration     post "user/sign_up", to: "users/registrations#create", as: :user_registration   end  end 

Hope it's work.

Answers 2

A possible solution can be to move your sign_up code to a separate layout file. Make sure you include the <html> and title tags in your file.

Generate Devise controllers using

rails generate devise:controllers users 

In your routes:

devise :users, controllers: { registrations: "users/registrations" } 

Then, render your custom layout in the Devise controller you just generated.

class Users::RegistrationController < Devise::RegistrationsController   layout "new_registration", only: [:new, :create] end 
Read More

Monday, March 12, 2018

Devise after_sign_in_path_for not working; being ignored when model has validations on: :update

Leave a Comment

My method is executing, but Devise is not using the return value at all. On the sign in page, it just reloads the page with a 'Signed in successfully' notice. It doesn't redirect to the value returned from the method.

Log

Started POST "/users/sign_in" for 127.0.0.1 at 2018-03-05 22:19:50 -0500 Processing by Users::SessionsController#create as HTML   Parameters: {"utf8"=>"√", "authenticity_token"=>"tQd5a43StP85oyyCpEmFU8cAkFXdJL2OLpuAK1+sqQC6/rIqcd+fB2iE4RT0RoPKPCqreNBYlv2bxjl9gZFrWg==", "user"=>{"email"=>"test11@example.com", "password"=>"[FILTERED]", "remember_me"=>"0"}, "commit"=>"Log in"}   User Load (2.0ms)  SELECT  "users".* FROM "users" WHERE "users"."email" = $1 ORDER BY "users"."id" ASC LIMIT $2  [["email", "test11@example.com"], ["LIMIT", 1]]    (5.0ms)  BEGIN   User Exists (3.0ms)  SELECT  1 AS one FROM "users" WHERE "users"."email" = $1 AND ("users"."id" != $2) LIMIT $3  [["email", "test11@example.com"], ["id", 23], ["LIMIT", 1]]   Sector Load (0.0ms)  SELECT "sectors".* FROM "sectors" INNER JOIN "sectors_users" ON "sectors"."id" = "sectors_users"."sector_id" WHERE "sectors_users"."user_id" = $1  [["user_id", 23]]   Region Load (0.0ms)  SELECT "regions".* FROM "regions" INNER JOIN "regions_users" ON "regions"."id" = "regions_users"."region_id" WHERE "regions_users"."user_id" = $1  [["user_id", 23]]   Criterium Load (0.0ms)  SELECT "criteria".* FROM "criteria" INNER JOIN "criteria_users" ON "criteria"."id" = "criteria_users"."criterium_id" WHERE "criteria_users"."user_id" = $1  [["user_id", 23]]   AssetType Load (0.0ms)  SELECT "asset_types".* FROM "asset_types" INNER JOIN "asset_types_users" ON "asset_types"."id" = "asset_types_users"."asset_type_id" WHERE "asset_types_users"."user_id" = $1  [["user_id", 23]]   Company Load (1.0ms)  SELECT  "companies".* FROM "companies" WHERE "companies"."id" = $1 LIMIT $2  [["id", 42], ["LIMIT", 1]]    (5.0ms)  ROLLBACK ############### /users/23/edit   Rendering users/sessions/new.haml within layouts/application   Rendered users/shared/_links.html.erb (3.0ms)   Rendered users/sessions/new.haml within layouts/application (251.2ms)   Rendered layouts/_footer.haml (15.0ms) Completed 200 OK in 6554ms (Views: 3364.9ms | ActiveRecord: 86.1ms) 

Notice it is rendering users/sessions/new.haml instead of the edit page?

Code

class ApplicationController < ActionController::Base ...   def after_sign_in_path_for(resource)     logger.debug '############### ' + edit_user_path(resource) if resource.is_a?(User) && resource.signature.blank?     return edit_user_path resource if resource.is_a?(User) && resource.signature.blank?     stored_location_for(resource) ||       if resource.is_a?(User)         dashboard_path       elsif resource.is_a?(Facilitator) && resource.name.nil?         edit_facilitator_path resource       elsif resource.is_a?(Facilitator)         facilitator_path resource       else         super       end   end 

I completely commented out the method and it still reloaded the login page.

Started POST "/users/sign_in" for 127.0.0.1 at 2018-03-05 22:25:21 -0500 ...   Rendering users/sessions/new.haml within layouts/application 

Devise 4.4.0

Documentation:

https://github.com/plataformatec/devise/wiki/How-To%3A-Redirect-to-a-specific-page-on-successful-sign-in-and-sign-out

http://www.rubydoc.info/github/plataformatec/devise/master/Devise/Controllers/Helpers:after_sign_in_path_for


I added

  def after_sign_in_path_for(resource)     logger.debug '############# ' + resource.errors.full_messages.join(', ') 

And did discover validation errors like

 ############# Title can't be blank, Country can't be blank, Signature can't be blank, ... 

But it does show the notice

Signed in successfully. 

And I do have a session and can navigate elsewhere. My validations are on: :update.

  validates :email, :name, :title, :phone, :address1, :city, :state, :zip, :country, :type, :signature, presence: true, on: :update 

This should not cause log in behavior errors.


I commented all validations on the model and it does work, but this is highly unusual! Validations should not affect login behavior. There has to be a workaround.

Started POST "/users/sign_in" for 127.0.0.1 at 2018-03-05 23:11:43 -0500   SQL (15.0ms)  UPDATE "users" SET "current_sign_in_at" = $1, "last_sign_in_at" = $2, "current_sign_in_ip" = $3, "sign_in_count" = $4, "updated_at" = $5 WHERE "users"."id" = $6  [["current_sign_in_at", "2018-03-06 04:11:44.225501"], ["last_sign_in_at", "2017-11-09 01:22:28.245231"], ["current_sign_in_ip", "127.0.0.1/32"], ["sign_in_count", 6], ["updated_at", "2018-03-06 04:11:44.230506"], ["id", 23]] Redirected to http://localhost:3000/users/23/edit Completed 302 Found in 2183ms (ActiveRecord: 48.0ms) 

3 Answers

Answers 1

As you only want your validations on update, I guess that you only need them for a specific form, since your users are still valid even without this validations. In that case I would use a so called form object, that does the on update validations for you and remove the on update validations on your user model. In that case your validations don't affect other parts of your app.

Here is a good guide on how to do that with just using ActiveModel.

An alternative could be to add a virtual attribute to the model and run your validations conditionally in the user controller.

Answers 2

You might need conditional validation on your model. Something like this:

 validates :email, :name, :title, :phone, :address1, :city, :state, :zip, :country, :type, :signature, presence: true, on: :update, unless: Proc.new {|user| user.sign_in_at.present? } 

Devise will update sign_in_at whenever sign_in happens. Which will trigger update action and related validations.

Also Documentation said the allow_nil: true instruct the model to validate the fields ONLY if it exists on the submitted form.

Answers 3

Check this documentation https://github.com/plataformatec/devise/wiki/How-To:-redirect-to-a-specific-page-on-successful-sign-in. They have clearly mentioned when you will go in loop and solution for it. Check Preventing redirect loops section in above doc.

Read More

Monday, December 18, 2017

Login page is stuck in redirection loop after using omniauth . While it was working fine before

Leave a Comment

I am working on a site that has two sides one for user and second for admin. I have used devise gem for authentication. Every thing was working fine but suddenly when I sign in to my admin account. The page is not working. I have recently worked on omniauth gem but I have not touched any previous code. I have tried to check the current_user but it is "nil". I think it is not getting user data in the application controller. Here is my code.

Application controller

class ApplicationController < ActionController::Base   add_flash_types :success, :warning, :danger, :info   protect_from_forgery prepend: true   before_filter :configure_permitted_parameters, if: :devise_controller?   layout :layout_by_resource   def current_user     @current_user ||= User.find(session[:user_id]) if session[:user_id]   end   helper_method :current_user    def logged_in_using_omniauth     session[:logged_in_using_omniauth].present?   end   helper_method :logged_in_using_omniauth    private    def layout_by_resource     if devise_controller?       "admin"     else       "application"     end   end    protected    def after_sign_in_path_for(resource)     if(resource.admin==false)       '/donations/donor_history'     else       '/admins/create_account' #your path     end   end    def after_sign_out_path_for(resource)     '/users/sign_in' #your path   end    def configure_permitted_parameters     devise_parameter_sanitizer.permit(:sign_up) do |user_params|       user_params.permit(:admin, :email, :password, :password_confirmation,:first_name,:last_name)     end     devise_parameter_sanitizer.permit(:account_update) do |user_params|       user_params.permit(:admin, :email, :password, :password_confirmation,:current_password,:first_name,:last_name)     end   end  end 

The admin controller is

class AdminsController < ApplicationController     before_action :set_admin, only: [:show, :edit, :update, :destroy, :social_media_sharing]     before_action :check_admin_level, only: [:donation_analysis]     helper_method :resource_name, :resource, :devise_mapping     before_filter :authenticate_user!     before_filter do         redirect_to new_user_session_path unless current_user && current_user.admin?     end     before_filter :index      COMMON_YEAR_DAYS_IN_MONTH = [nil, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]      layout "admin"     # GET /admins     # GET /admins.json     def resource_name         :admin     end      def resource         @resource ||= User.new     end      def devise_mapping         @devise_mapping ||= Devise.mappings[:admin]     end     ----------- 

user model is

class User < ApplicationRecord   # Include default devise modules. Others available are:   devise :database_authenticatable, :registerable, :recoverable, :rememberable, :trackable, :validatable, :confirmable,     :omniauthable, :omniauth_providers => [:facebook,:twitter,:linkedin]   has_many :organizations_users   has_many :organizations, through: :organizations_users      def active_for_authentication?     # Uncomment the below debug statement to view the properties of the returned self model values.     super && self.active && self.exp_alert == false   end    def self.from_omniauth(auth)     user =  where(provider: auth.provider, uid: auth.uid).first_or_create do |user|       user.email = auth.info.email       user.password = Devise.friendly_token[0,20]       user.active = 'true'       user.admin=='false'       user.exp_alert == 'false'       user.skip_confirmation!     end     user   end    def self.new_with_session(params, session)     super.tap do |user|       if data = session["devise.facebook_data"] && session["devise.facebook_data"]["extra"]["raw_info"]         user.email = data["email"] if user.email.blank?       end     end   end    def self.find_or_create_from_auth_hash(auth_hash)     user = where(provider: auth_hash.provider, uid: auth_hash.uid).first_or_create do |user|       user.first_name =  auth_hash.info.nickname       user.active = 'true'       user.admin=='false'       user.exp_alert == 'false'       user.password = Devise.friendly_token[0,20]       user.token =  auth_hash.credentials.token       user.email =  "#{auth_hash.info.nickname}@test.com"       user.secret =  auth_hash.credentials.secret       user.skip_confirmation!     end     user   end    def self.linkedin_hash(auth_hash)     user = where(provider: auth_hash.provider, uid: auth_hash.uid).first_or_create do |user|       user.first_name =  auth_hash.info.first_name       user.last_name =  auth_hash.info.last_name       user.active = 'true'       user.admin=='false'       user.exp_alert == 'false'       user.password = Devise.friendly_token[0,20]       user.token =  auth_hash.credentials.token       user.email =  auth_hash.info.email       user.skip_confirmation!     end     user   end    def inactive_message     "Your Account has not been active yet."   end    def after_confirmation     super     self.update_attribute(:active, true)   end end 

routes are

Rails.application.routes.draw do    devise_for :users,  controllers: {confirmations: 'confirmations',registrations: 'users/registrations',omniauth_callbacks: 'users/omniauth_callbacks' } do     get "confirmation", to: "confirmations#after_confirmation_path_for"      delete 'sign_out', :to => 'devise/sessions#destroy', :as => :destroy_user_session   end    devise_for :models    get 'donations/donor_history/' => 'donations#donor_history'   get 'donations/donor_signup/' => 'donations#donor_signup'   post 'donations/donor_signup/' => 'donations#donor_signup'   post 'donations/sms_service/' => 'donations#sms_service'   post 'donations/create_user_account' => 'donations#create_user_account'   post 'donations/add_user_payroll' => 'donations#add_user_payroll'    resources :donations, except: [:new, :create]    resources :campaigns do     resources :donations, only: [:new, :create, :create_user_account]     get 'donations/create_user_account' => 'donations#create_user_account'   end   resources :organizations    post 'admins/social_sharing_switch/' => 'admins#social_sharing_switch'   get 'admins/error_detail/' => 'admins#error_detail'   get 'admins/generate_report/:id' => 'admins#generate_report'   get 'admins/create_company/' => 'admins#create_company'   post 'admins/create_company/' => 'admins#create_company'   get 'admins/revenue_detail/' => 'admins#revenue_detail'   get 'admins/create_account' => 'admins#create_account'   get 'admins/view_account' => 'admins#view_account'   get 'admins/view_company/:id' => 'admins#view_company'   constraints RouteConstraint.new do     get 'admins/donation_analysis' => 'admins#donation_analysis'   end    get 'admins/link_expiry' => 'admins#link_expiry'   get 'admins/edit_profile' => 'admins#edit_profile'   post 'admins/update_profile' => 'admins#update_profile'   match '/admins/create_account', to: 'admins#create_account', via: 'post'   match '/admins/:id', to: 'admins#destroy', via: 'get' , as: 'admin_destroy'    resources :admins    get 'crons/expirylink_alert' => 'crons#expirylink_alert'     devise_scope :user do     get '/users/sign_out' => 'devise/sessions#destroy'   end    def user_params   params.require(:user).permit(:name, :email, :password, :password_confirmation) end     root to: "campaigns#latest" end 

Logs are

Started GET "/admins/create_account" for 10.0.2.2 at 2017-12-02 15:13:38 +0000 Cannot render console from 10.0.2.2! Allowed networks: 127.0.0.1, ::1, 127.0.0.0/127.255.255.255 Processing by AdminsController#create_account as HTML   [1m[36mUser Load (1.1ms)[0m  [1m[34mSELECT  "users".* FROM "users" WHERE "users"."id" = $1 ORDER BY "users"."id" ASC LIMIT $2[0m  [["id", 64810987], ["LIMIT", 1]] Redirected to http://localhost:8090/users/sign_in Filter chain halted as #<Proc:0x00000002223c68@/vagrant/donation-simple/app/controllers/admins_controller.rb:6> rendered or redirected Completed 302 Found in 4ms (ActiveRecord: 1.1ms)   Started GET "/users/sign_in" for 10.0.2.2 at 2017-12-02 15:13:38 +0000 Cannot render console from 10.0.2.2! Allowed networks: 127.0.0.1, ::1, 127.0.0.0/127.255.255.255 Processing by Devise::SessionsController#new as HTML   [1m[36mUser Load (0.8ms)[0m  [1m[34mSELECT  "users".* FROM "users" WHERE "users"."id" = $1 ORDER BY "users"."id" ASC LIMIT $2[0m  [["id", 64810987], ["LIMIT", 1]] Redirected to http://localhost:8090/admins/create_account Filter chain halted as :require_no_authentication rendered or redirected Completed 302 Found in 4ms (ActiveRecord: 0.8ms)   Started GET "/admins/create_account" for 10.0.2.2 at 2017-12-02 15:13:38 +0000 Cannot render console from 10.0.2.2! Allowed networks: 127.0.0.1, ::1, 127.0.0.0/127.255.255.255 Processing by AdminsController#create_account as HTML   [1m[36mUser Load (0.8ms)[0m  [1m[34mSELECT  "users".* FROM "users" WHERE "users"."id" = $1 ORDER BY "users"."id" ASC LIMIT $2[0m  [["id", 64810987], ["LIMIT", 1]] Redirected to http://localhost:8090/users/sign_in Filter chain halted as #<Proc:0x00000002223c68@/vagrant/donation-simple/app/controllers/admins_controller.rb:6> rendered or redirected Completed 302 Found in 4ms (ActiveRecord: 0.8ms)   Started GET "/users/sign_in" for 10.0.2.2 at 2017-12-02 15:13:39 +0000 Cannot render console from 10.0.2.2! Allowed networks: 127.0.0.1, ::1, 127.0.0.0/127.255.255.255 Processing by Devise::SessionsController#new as HTML   [1m[36mUser Load (1.0ms)[0m  [1m[34mSELECT  "users".* FROM "users" WHERE "users"."id" = $1 ORDER BY "users"."id" ASC LIMIT $2[0m  [["id", 64810987], ["LIMIT", 1]] Redirected to http://localhost:8090/admins/create_account Filter chain halted as :require_no_authentication rendered or redirected Completed 302 Found in 4ms (ActiveRecord: 1.0ms)   Started GET "/admins/create_account" for 10.0.2.2 at 2017-12-02 15:13:39 +0000 Cannot render console from 10.0.2.2! Allowed networks: 127.0.0.1, ::1, 127.0.0.0/127.255.255.255 Processing by AdminsController#create_account as HTML   [1m[36mUser Load (1.1ms)[0m  [1m[34mSELECT  "users".* FROM "users" WHERE "users"."id" = $1 ORDER BY "users"."id" ASC LIMIT $2[0m  [["id", 64810987], ["LIMIT", 1]] Redirected to http://localhost:8090/users/sign_in Filter chain halted as #<Proc:0x00000002223c68@/vagrant/donation-simple/app/controllers/admins_controller.rb:6> rendered or redirected Completed 302 Found in 5ms (ActiveRecord: 1.1ms)   Started GET "/users/sign_in" for 10.0.2.2 at 2017-12-02 15:13:39 +0000 Cannot render console from 10.0.2.2! Allowed networks: 127.0.0.1, ::1, 127.0.0.0/127.255.255.255 Processing by Devise::SessionsController#new as HTML   [1m[36mUser Load (0.8ms)[0m  [1m[34mSELECT  "users".* FROM "users" WHERE "users"."id" = $1 ORDER BY "users"."id" ASC LIMIT $2[0m  [["id", 64810987], ["LIMIT", 1]] Redirected to http://localhost:8090/admins/create_account Filter chain halted as :require_no_authentication rendered or redirected Completed 302 Found in 4ms (ActiveRecord: 0.8ms)   Started GET "/admins/create_account" for 10.0.2.2 at 2017-12-02 15:13:39 +0000 Cannot render console from 10.0.2.2! Allowed networks: 127.0.0.1, ::1, 127.0.0.0/127.255.255.255 Processing by AdminsController#create_account as HTML   [1m[36mUser Load (0.7ms)[0m  [1m[34mSELECT  "users".* FROM "users" WHERE "users"."id" = $1 ORDER BY "users"."id" ASC LIMIT $2[0m  [["id", 64810987], ["LIMIT", 1]] Redirected to http://localhost:8090/users/sign_in Filter chain halted as #<Proc:0x00000002223c68@/vagrant/donation-simple/app/controllers/admins_controller.rb:6> rendered or redirected Completed 302 Found in 3ms (ActiveRecord: 0.7ms)   Started GET "/users/sign_in" for 10.0.2.2 at 2017-12-02 15:13:39 +0000 Cannot render console from 10.0.2.2! Allowed networks: 127.0.0.1, ::1, 127.0.0.0/127.255.255.255 Processing by Devise::SessionsController#new as HTML   [1m[36mUser Load (0.7ms)[0m  [1m[34mSELECT  "users".* FROM "users" WHERE "users"."id" = $1 ORDER BY "users"."id" ASC LIMIT $2[0m  [["id", 64810987], ["LIMIT", 1]] Redirected to http://localhost:8090/admins/create_account Filter chain halted as :require_no_authentication rendered or redirected Completed 302 Found in 3ms (ActiveRecord: 0.7ms)   Started GET "/admins/create_account" for 10.0.2.2 at 2017-12-02 15:13:39 +0000 Cannot render console from 10.0.2.2! Allowed networks: 127.0.0.1, ::1, 127.0.0.0/127.255.255.255 Processing by AdminsController#create_account as HTML   [1m[36mUser Load (0.9ms)[0m  [1m[34mSELECT  "users".* FROM "users" WHERE "users"."id" = $1 ORDER BY "users"."id" ASC LIMIT $2[0m  [["id", 64810987], ["LIMIT", 1]] Redirected to http://localhost:8090/users/sign_in Filter chain halted as #<Proc:0x00000002223c68@/vagrant/donation-simple/app/controllers/admins_controller.rb:6> rendered or redirected Completed 302 Found in 4ms (ActiveRecord: 0.9ms)   Started GET "/users/sign_in" for 10.0.2.2 at 2017-12-02 15:13:39 +0000 Cannot render console from 10.0.2.2! Allowed networks: 127.0.0.1, ::1, 127.0.0.0/127.255.255.255 Processing by Devise::SessionsController#new as HTML   [1m[36mUser Load (1.0ms)[0m  [1m[34mSELECT  "users".* FROM "users" WHERE "users"."id" = $1 ORDER BY "users"."id" ASC LIMIT $2[0m  [["id", 64810987], ["LIMIT", 1]] Redirected to http://localhost:8090/admins/create_account Filter chain halted as :require_no_authentication rendered or redirected Completed 302 Found in 4ms (ActiveRecord: 1.0ms)   Started GET "/admins/create_account" for 10.0.2.2 at 2017-12-02 15:13:39 +0000 Cannot render console from 10.0.2.2! Allowed networks: 127.0.0.1, ::1, 127.0.0.0/127.255.255.255 Processing by AdminsController#create_account as HTML   [1m[36mUser Load (0.7ms)[0m  [1m[34mSELECT  "users".* FROM "users" WHERE "users"."id" = $1 ORDER BY "users"."id" ASC LIMIT $2[0m  [["id", 64810987], ["LIMIT", 1]] Redirected to http://localhost:8090/users/sign_in Filter chain halted as #<Proc:0x00000002223c68@/vagrant/donation-simple/app/controllers/admins_controller.rb:6> rendered or redirected Completed 302 Found in 4ms (ActiveRecord: 0.7ms)   Started GET "/users/sign_in" for 10.0.2.2 at 2017-12-02 15:13:39 +0000 Cannot render console from 10.0.2.2! Allowed networks: 127.0.0.1, ::1, 127.0.0.0/127.255.255.255 Processing by Devise::SessionsController#new as HTML   [1m[36mUser Load (1.0ms)[0m  [1m[34mSELECT  "users".* FROM "users" WHERE "users"."id" = $1 ORDER BY "users"."id" ASC LIMIT $2[0m  [["id", 64810987], ["LIMIT", 1]] Redirected to http://localhost:8090/admins/create_account Filter chain halted as :require_no_authentication rendered or redirected Completed 302 Found in 4ms (ActiveRecord: 1.0ms)   Started GET "/admins/create_account" for 10.0.2.2 at 2017-12-02 15:13:39 +0000 Cannot render console from 10.0.2.2! Allowed networks: 127.0.0.1, ::1, 127.0.0.0/127.255.255.255 Processing by AdminsController#create_account as HTML   [1m[36mUser Load (0.8ms)[0m  [1m[34mSELECT  "users".* FROM "users" WHERE "users"."id" = $1 ORDER BY "users"."id" ASC LIMIT $2[0m  [["id", 64810987], ["LIMIT", 1]] Redirected to http://localhost:8090/users/sign_in Filter chain halted as #<Proc:0x00000002223c68@/vagrant/donation-simple/app/controllers/admins_controller.rb:6> rendered or redirected Completed 302 Found in 4ms (ActiveRecord: 0.8ms)   Started GET "/users/sign_in" for 10.0.2.2 at 2017-12-02 15:13:39 +0000 Cannot render console from 10.0.2.2! Allowed networks: 127.0.0.1, ::1, 127.0.0.0/127.255.255.255 Processing by Devise::SessionsController#new as HTML   [1m[36mUser Load (0.7ms)[0m  [1m[34mSELECT  "users".* FROM "users" WHERE "users"."id" = $1 ORDER BY "users"."id" ASC LIMIT $2[0m  [["id", 64810987], ["LIMIT", 1]] Redirected to http://localhost:8090/admins/create_account Filter chain halted as :require_no_authentication rendered or redirected Completed 302 Found in 4ms (ActiveRecord: 0.7ms)   Started GET "/admins/create_account" for 10.0.2.2 at 2017-12-02 15:13:40 +0000 Cannot render console from 10.0.2.2! Allowed networks: 127.0.0.1, ::1, 127.0.0.0/127.255.255.255 Processing by AdminsController#create_account as HTML   [1m[36mUser Load (0.7ms)[0m  [1m[34mSELECT  "users".* FROM "users" WHERE "users"."id" = $1 ORDER BY "users"."id" ASC LIMIT $2[0m  [["id", 64810987], ["LIMIT", 1]] Redirected to http://localhost:8090/users/sign_in Filter chain halted as #<Proc:0x00000002223c68@/vagrant/donation-simple/app/controllers/admins_controller.rb:6> rendered or redirected Completed 302 Found in 3ms (ActiveRecord: 0.7ms)   Started GET "/users/sign_in" for 10.0.2.2 at 2017-12-02 15:13:40 +0000 Cannot render console from 10.0.2.2! Allowed networks: 127.0.0.1, ::1, 127.0.0.0/127.255.255.255 Processing by Devise::SessionsController#new as HTML   [1m[36mUser Load (0.7ms)[0m  [1m[34mSELECT  "users".* FROM "users" WHERE "users"."id" = $1 ORDER BY "users"."id" ASC LIMIT $2[0m  [["id", 64810987], ["LIMIT", 1]] Redirected to http://localhost:8090/admins/create_account Filter chain halted as :require_no_authentication rendered or redirected Completed 302 Found in 3ms (ActiveRecord: 0.7ms)   Started GET "/admins/create_account" for 10.0.2.2 at 2017-12-02 15:13:40 +0000 Cannot render console from 10.0.2.2! Allowed networks: 127.0.0.1, ::1, 127.0.0.0/127.255.255.255 Processing by AdminsController#create_account as HTML   [1m[36mUser Load (0.8ms)[0m  [1m[34mSELECT  "users".* FROM "users" WHERE "users"."id" = $1 ORDER BY "users"."id" ASC LIMIT $2[0m  [["id", 64810987], ["LIMIT", 1]] Redirected to http://localhost:8090/users/sign_in Filter chain halted as #<Proc:0x00000002223c68@/vagrant/donation-simple/app/controllers/admins_controller.rb:6> rendered or redirected Completed 302 Found in 4ms (ActiveRecord: 0.8ms)   Started GET "/users/sign_in" for 10.0.2.2 at 2017-12-02 15:13:40 +0000 Cannot render console from 10.0.2.2! Allowed networks: 127.0.0.1, ::1, 127.0.0.0/127.255.255.255 Processing by Devise::SessionsController#new as HTML   [1m[36mUser Load (1.4ms)[0m  [1m[34mSELECT  "users".* FROM "users" WHERE "users"."id" = $1 ORDER BY "users"."id" ASC LIMIT $2[0m  [["id", 64810987], ["LIMIT", 1]] Redirected to http://localhost:8090/admins/create_account Filter chain halted as :require_no_authentication rendered or redirected Completed 302 Found in 5ms (ActiveRecord: 1.4ms)   Started GET "/admins/create_account" for 10.0.2.2 at 2017-12-02 15:13:40 +0000 Cannot render console from 10.0.2.2! Allowed networks: 127.0.0.1, ::1, 127.0.0.0/127.255.255.255 Processing by AdminsController#create_account as HTML   [1m[36mUser Load (1.6ms)[0m  [1m[34mSELECT  "users".* FROM "users" WHERE "users"."id" = $1 ORDER BY "users"."id" ASC LIMIT $2[0m  [["id", 64810987], ["LIMIT", 1]] Redirected to http://localhost:8090/users/sign_in Filter chain halted as #<Proc:0x00000002223c68@/vagrant/donation-simple/app/controllers/admins_controller.rb:6> rendered or redirected Completed 302 Found in 6ms (ActiveRecord: 1.6ms)   Started GET "/users/sign_in" for 10.0.2.2 at 2017-12-02 15:13:40 +0000 Cannot render console from 10.0.2.2! Allowed networks: 127.0.0.1, ::1, 127.0.0.0/127.255.255.255 Processing by Devise::SessionsController#new as HTML   [1m[36mUser Load (1.1ms)[0m  [1m[34mSELECT  "users".* FROM "users" WHERE "users"."id" = $1 ORDER BY "users"."id" ASC LIMIT $2[0m  [["id", 64810987], ["LIMIT", 1]] Redirected to http://localhost:8090/admins/create_account Filter chain halted as :require_no_authentication rendered or redirected Completed 302 Found in 5ms (ActiveRecord: 1.1ms)   Started GET "/admins/create_account" for 10.0.2.2 at 2017-12-02 15:13:40 +0000 Cannot render console from 10.0.2.2! Allowed networks: 127.0.0.1, ::1, 127.0.0.0/127.255.255.255 Processing by AdminsController#create_account as HTML   [1m[36mUser Load (0.8ms)[0m  [1m[34mSELECT  "users".* FROM "users" WHERE "users"."id" = $1 ORDER BY "users"."id" ASC LIMIT $2[0m  [["id", 64810987], ["LIMIT", 1]] Redirected to http://localhost:8090/users/sign_in Filter chain halted as #<Proc:0x00000002223c68@/vagrant/donation-simple/app/controllers/admins_controller.rb:6> rendered or redirected Completed 302 Found in 4ms (ActiveRecord: 0.8ms) 

enter image description here

1 Answers

Answers 1

I have solved it my self and now I am sharing this Answer so that it may help anyone else in the future.

The problem was the current_user variable which was overriding in the application controller.

def current_user  @current_user ||= User.find(session[:user_id]) if session[:user_id] end 

Devise set the current_user variable itself and this code was overriding the current_user variable. The strange thing is that the same code was working before without any problem. I think devise has updated. However after comment the above code every thing is working fine.

Read More

Tuesday, November 21, 2017

Stub authentication in request specs

Leave a Comment

I'm looking for the way to do this but in request specs. I need to log in and log out a double or instance_double to Devise instead of an actual ActiveModel/ActiveRecord.

By using the code in the wiki page:

module RequestSpecHelpers     def sign_in(user = double('user'))       if user.nil?         allow(request.env['warden']).to receive(:authenticate!).and_throw(:warden, {:scope => :user})         allow(controller).to receive(:current_user).and_return(nil)       else         allow(request.env['warden']).to receive(:authenticate!).and_return(user)         allow(controller).to receive(:current_user).and_return(user)       end     end   end 

I get this error: undefined method 'env' for nil:NilClass

I saw this question and this wiki, but if I want to use doubles of the user those two don't work. I was using the last one, works fine with a real user but with a double it doesn't log it in.

The tests:

RSpec.describe 'new shipment', type: :request do   describe 'authenticated as user' do     before do       @user = double(:user, id: 1, email: 'user@gmail.com', password: 'password',                       id_card: '4163649-1', first_name: 'Jane', last_name: 'Doe')        sign_in @user     end   end end 

If I include:

RSpec.configure do |config|   config.include Devise::TestHelpers, :type => :requests end 

I get this error:

Failure/Error: @request.env['action_controller.instance'] = @controller       NoMethodError:        undefined method `env' for nil:NilClass      # /root/.rbenv/versions/2.4.2/lib/ruby/gems/2.4.0/gems/devise-4.3.0/lib/devise/test/controller_helpers.rb:40:in `setup_controller_for_warden' 

Problem with Frederick Cheung answer

If I do that the login_asmethod doesn't fail but it doesn't really log the user in. So when I try to access a path that has a before_action :authenticate_user! callback it fails.

Here is my code based on his answer:

require 'rails_helper'  RSpec.describe 'new shipment', type: :request do   describe 'authenticated as user' do     include Warden::Test::Helpers      before(:each) do       Warden.test_mode!       #stub more methods as needed by the pages you are testing       user = instance_double(User, to_key: 1, authenticatable_salt: 'example')       login_as(user, scope: 'user')     end      it 'returns 200 Ok' do       get new_shipment_path       expect(response).to have_http_status(:ok)     end   end end 

And this is the response when running rspec:

 1) new shipment authenticated as user returns 200 Ok      Failure/Error: expect(response).to have_http_status(:ok)        expected the response to have status code :ok (200) but it was :found (302)      # ./spec/requests/shipments_requests_spec.rb:41:in `block (3 levels) in <top (required)>' 

As you can see instead of allowing me to access the path it redirects me, this is the usual behavior when the user is not allowed to access the path.

It I change the instance_double for a real User saved in the database this approach works correctly:

# only changed this line in the before hook user = User.create(email: 'user@gmail.com', password: 'password',id_card: '4163649-1', first_name: 'Jane', last_name: 'Doe') 

Result:

Finished in 3.23 seconds (files took 33.47 seconds to load) 1 example, 0 failures 

1 Answers

Answers 1

It sounds like you're using Devise 3.x ( since Devise::TestHelpers was renamed in devise 4), Devise::TestHelpers is only designed to work with controller specs.

If you can upgrade to devise 4, it has separate helpers for request specs and controller tests. This is just a very thin wrapper around what warden provides, which hides all the messing around with env.

There are some extra complications when using a double - you need to stub out various methods devise calls that you might not realise.

The following worked for me

describe 'example' do   include Warden::Test::Helpers    before(:each) do     Warden.test_mode!     #stub more methods as needed by the pages you are testing     user = instance_double(User, to_key: 1, authenticatable_salt: 'example')     login_as(user, scope: 'user')   end end 
Read More

Monday, November 20, 2017

different prefix: “/” and “http:/localhost:3000” with devise

Leave a Comment

I'm using devise with rails 5.1.4. When I'm trying to sign in with a wrong password I get this error:

different prefix: "/" and "http:/localhost:3000"

I'm using rbenv with ruby 2.4.1. I had the same problem with ruby 2.3.3.

Here's the full stacktrace:

    /home/badr/.rbenv/versions/2.4.1/lib/ruby/2.4.0/pathname.rb:520:in `relative_path_from' devise (4.3.0) lib/devise/failure_app.rb:58:in `recall' devise (4.3.0) lib/devise/failure_app.rb:39:in `respond' actionpack (5.1.4) lib/abstract_controller/base.rb:186:in `process_action' actionpack (5.1.4) lib/abstract_controller/base.rb:124:in `process' actionpack (5.1.4) lib/action_controller/metal.rb:189:in `dispatch' actionpack (5.1.4) lib/action_controller/metal.rb:242:in `block in action' devise (4.3.0) lib/devise/failure_app.rb:21:in `call' devise (4.3.0) lib/devise/delegator.rb:5:in `call' warden (1.2.7) lib/warden/manager.rb:143:in `call_failure_app' warden (1.2.7) lib/warden/manager.rb:129:in `process_unauthenticated' warden (1.2.7) lib/warden/manager.rb:44:in `call' rack (2.0.3) lib/rack/etag.rb:25:in `call' rack (2.0.3) lib/rack/conditional_get.rb:38:in `call' rack (2.0.3) lib/rack/head.rb:12:in `call' rack (2.0.3) lib/rack/session/abstract/id.rb:232:in `context' rack (2.0.3) lib/rack/session/abstract/id.rb:226:in `call' actionpack (5.1.4) lib/action_dispatch/middleware/cookies.rb:613:in `call' activerecord (5.1.4) lib/active_record/migration.rb:556:in `call' actionpack (5.1.4) lib/action_dispatch/middleware/callbacks.rb:26:in `block in call' activesupport (5.1.4) lib/active_support/callbacks.rb:97:in `run_callbacks' actionpack (5.1.4) lib/action_dispatch/middleware/callbacks.rb:24:in `call' actionpack (5.1.4) lib/action_dispatch/middleware/executor.rb:12:in `call' actionpack (5.1.4) lib/action_dispatch/middleware/debug_exceptions.rb:59:in `call' web-console (3.5.1) lib/web_console/middleware.rb:135:in `call_app' web-console (3.5.1) lib/web_console/middleware.rb:28:in `block in call' web-console (3.5.1) lib/web_console/middleware.rb:18:in `catch' web-console (3.5.1) lib/web_console/middleware.rb:18:in `call' actionpack (5.1.4) lib/action_dispatch/middleware/show_exceptions.rb:31:in `call' railties (5.1.4) lib/rails/rack/logger.rb:36:in `call_app' railties (5.1.4) lib/rails/rack/logger.rb:24:in `block in call' activesupport (5.1.4) lib/active_support/tagged_logging.rb:69:in `block in tagged' activesupport (5.1.4) lib/active_support/tagged_logging.rb:26:in `tagged' activesupport (5.1.4) lib/active_support/tagged_logging.rb:69:in `tagged' railties (5.1.4) lib/rails/rack/logger.rb:24:in `call' sprockets-rails (3.2.1) lib/sprockets/rails/quiet_assets.rb:13:in `call' actionpack (5.1.4) lib/action_dispatch/middleware/remote_ip.rb:79:in `call' actionpack (5.1.4) lib/action_dispatch/middleware/request_id.rb:25:in `call' rack (2.0.3) lib/rack/method_override.rb:22:in `call' rack (2.0.3) lib/rack/runtime.rb:22:in `call' activesupport (5.1.4) lib/active_support/cache/strategy/local_cache_middleware.rb:27:in `call' actionpack (5.1.4) lib/action_dispatch/middleware/executor.rb:12:in `call' actionpack (5.1.4) lib/action_dispatch/middleware/static.rb:125:in `call' rack (2.0.3) lib/rack/sendfile.rb:111:in `call' railties (5.1.4) lib/rails/engine.rb:522:in `call' puma (3.10.0) lib/puma/configuration.rb:225:in `call' puma (3.10.0) lib/puma/server.rb:605:in `handle_request' puma (3.10.0) lib/puma/server.rb:437:in `process_client' puma (3.10.0) lib/puma/server.rb:301:in `block in run' puma (3.10.0) lib/puma/thread_pool.rb:120:in `block in spawn_thread' 

Any idea where this might come from ?

Update: I created a workspace on cloud9 to see if the project works fine on an other machine and indeed it does! So it's seems like something is wrong with my ruby installation. Before the issue started to happen I had destroyed the project folder on my linux machine and cloned it again from bitbucket.

1 Answers

Answers 1

This is the Devise Workflow of your action.

You filled the form session/new.html.erb. Devise uses the form_for to bind the form to an object and use the RESTful Routes from your routes.rb file.

<%= form_for(resource, as: resource_name, url: session_path(resource_name)) do |f| %> 

This form_for tag will generate the following html form, resource will be replaced with an object based on the User or Admin Model by your SessionsController#new action with the code self.resource = resource_class.new(sign_in_params)

<form accept-charset="UTF-8" action="/users" method="post" class="nifty_form"> 

The html form will trigger a POST request to url /users when you click on the submit button.

The POST request will be received from your Server router which based on the settings you configured on your routes.rb will decide how to respond.

If your routes.rb file is configured as with Devise guidelines, this will be your routes.rb file

devise_for :users 

which generates the following routes

user_session POST   /users/sign_in                    {controller:"devise/sessions", action:"create"} 

so when the request is received from the server it should be handled from the sessions_controller.rb#create action. You can display this controllers on Github or in your GEMS installed in your RVM or RBENV folders. You can also debug them by setting a binding.pry

  # POST /resource/sign_in   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)   end 

When you decide to use the wrong authentication, you are calling this method

def authenticate! resource  = password.present? && mapping.to.find_for_database_authentication(authentication_hash) hashed = false  if validate(resource){ hashed = true; resource.valid_password?(password) }   remember_me(resource)   resource.after_database_authentication   success!(resource) end  mapping.to.new.password = password if !hashed && Devise.paranoid fail(:not_found_in_database) unless resource end 

So when you give invalid password and email, validate(resource){ hashed = true; resource.valid_password?(password) } will return false and execute this code

mapping.to.new.password = password if !hashed && Devise.paranoid fail(:not_found_in_database) unless resource 

which for some reasons will redirect to the wrong url.

I believe this code is meant to show an error in your page, the view session/new.html.erb. So I believe there is not GET request in Devise standard controller, but just re-rendering the view with the error message.

So my question is, did you overwrite the Devise controller to give it this behavior? Could you share with us your routes.rb file and the output of rake routes?

Read More

Wednesday, October 4, 2017

Setting up SAML callback in Rails using Devise and OmniAuth-SAML

Leave a Comment

EDIT: Additional info and condensed question near the bottom ;)

I'm setting up integration between a small app I'm making and an identity provider using SAML2.0.

In general, I've been following the instructions on the Devise page, and then on the Omniauth-SAML docs.

The issue seems currently to be that no callback path has been generated. Here's the relevant code bits below; feel free to request additional information.

app/models/user.rb

class User < ActiveRecord::Base   devise :omniauthable, omniauth_providers: [:saml]    def from_omniauth(auth_hash)     puts auth_hash     new  # Stub for now I guess?   end end 

app/controllers/omniauth_callbacks_controller.rb

class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController   def saml     @user = User.from_omniauth request.env['omniauth.auth']     if @user.persisted?       sign_in_and_redirect @user, event: :authentication       set_flash_message(:notice, :success, kind: 'SAML') if is_navicational_format?     else       session['devise.saml_data'] = request.env['omniauth.auth']       redirect_to permission_denied # this isn't going to work lol     end   end    def failure     redirect_to root_path   end end 

A truncated & sanitized chunk from config/initializers/devise.rb

  config.omniauth :saml,                   idp_cert_fingerprint: 'aa:bb:cc...', # an actual fingerprint here                    idp_sso_target_url: 'https://sso.bla.thing.com/fss/idp/startSSO.ping?PartnerSpId=SAML_UID',                   issuer: 'myidpname',  # Not actually sure what this should be                   idp_entity_id: 'thingfssdp',                   assertion_consumer_service_url: 'https://myapp.com/auth/saml/callback',                   name_identifier_format: 'urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress' 

According to the docs here and here, adding more than the above (that is, putting the additional requirements into config/initializers/omniauth.rb) would be incorrect.

My controllers have before_action :authenticate_user! as their first line.

config/routes.rb has the following line at the top:

Rails.application.routes.draw do   devise_for :users, controllers: { omniauth_callbacks: 'users/omniauth_callbacks' } 

But it's possibly important to note that I haven't manually added any logic for callback handling yet

Attempting to visit my app yields an ERR_TOO_MANY_REDIRECTS; quite a few 302s all apparently pointing back to itself. Doing a GET /auth/saml/callback yields the following helpful error (not sure how or why /users/ gets prepended there; do I need to request a change in ACS URL or is this something I have control of?):

rails_error_message

Any insight or assistance would be much appreciated.

EDIT: It looks as though the issue is that user_saml_omniauth_authorize_path is being set to /users/auth/saml -- and not directly the IDP signin page. I have no explicit controller for this route, but apparently requiring signin for OTHER controllers means I am requiring signin for this one. The end result is that, as some have suggested, we get an infinite redirect loop.

1 Answers

Answers 1

About the redirect loop: Since you have before_action :authenticate_user! it leads any unauthenticated request to users sign in page. My guess is that you also have the same callback on your sign in page. Thus on every redirect to /sign_in rails puts it through this authenticate_user! and redirects it again since user is not authenticated. For it to work correctly you have to skip_before_action :authenticate_user! in the controller where you have the sign on (SessionsController I presume).

As for your second question - the correct authorization route. the answer is in the screenshot you presented, below the error. You can see there that the correct path is /users/auth/saml and users/auth/saml/callback

UPDATE: users gets prepended by default from Devise (using your devisable model name)

Read More

Thursday, September 28, 2017

Devise: override current_user (set different Class for user)

Leave a Comment

I want to do this:

application_controller.rb:

class ApplicationController < ActionController::Base   alias_method :devise_current_user, :current_user    private    def current_user     if params[:user].blank?       puts "!found user"       devise_current_user     else       puts "found user"       user = User.find_by(email: params[:user][:email])       return detect_role(user)     end   end    def detect_role(user)     roles = user.roles_name     user = if roles.include?("mentor")              user.becomes(Mentor)            elsif !roles.include?("admin") && !roles.include?("mentor")              user.becomes(Student)            else              user            end   end end 

but still does not go out to override current_user

log: https://gist.github.com/anonymous/e0a5fb593b020b16a0cd2ae9d539b92a

2 Answers

Answers 1

This helped me:

class ApplicationController < ActionController::Base   alias_method :devise_current_user, :current_user    private    def current_user     user = if params[:user].blank?              devise_current_user            else              User.find_by(email: params[:user][:email])            end     detect_role(user) if !user.blank?   end    def detect_role(user)     roles = user.roles_name     user = if roles.include?("mentor")              user.becomes(Mentor)            elsif !roles.include?("admin") && !roles.include?("mentor")              user.becomes(Student)            else              user            end   end end 

Answers 2

Use

super

keyword and then right your override code like this;

def current_user   super   ----your code goes here --- end 
Read More

Tuesday, August 29, 2017

devise token auth errors

Leave a Comment

I currently have a Rails 5 app running using Devise Token Auth, it is deployed on heroku.

Signing in and signing up all works fine, however, if restart the application, the next sign in, gives me a 500, with the following error.. Any ideas?

I have narrowed the bug down, it only happens on Heroku. When running locally in production mode it is fine.

vendor/bundle/ruby/2.3.0/gems/devise_token_auth-0.1.42/app/controllers/devise_token_auth/sessions_controller.rb:42:in `[]=' vendor/bundle/ruby/2.3.0/gems/devise_token_auth-0.1.42/app/controllers/devise_token_auth/sessions_controller.rb:42:in `create' 

devise initalizer:

Devise.setup do |config|   config.secret_key = 'xxx' 

devise token auth initalizer:

DeviseTokenAuth.setup do |config|   config.change_headers_on_each_request = false   config.token_lifespan = 1.year end 

Profile.rb (model which handles auth):

devise :database_authenticatable, :registerable,        :recoverable, :trackable, :validatable include DeviseTokenAuth::Concerns::User 

Longer stack trace

2017-08-21T13:47:59.917645+00:00 app[web.1]: I, [2017-08-21T13:47:59.917547 #4]  INFO -- : [8f6962a7-f07c-4754-959c-c51dafa37d76] Started POST "/auth/sign_in" for 82.163.112.30 at 2017-08-21 13:47:59 +0000 2017-08-21T13:48:00.165030+00:00 app[web.1]: [8f6962a7-f07c-4754-959c-c51dafa37d76] vendor/bundle/ruby/2.3.0/gems/actionpack-5.1.3/lib/action_controller/metal/basic_implicit_render.rb:4:in `send_action' 2017-08-21T13:48:00.165036+00:00 app[web.1]: [8f6962a7-f07c-4754-959c-c51dafa37d76] vendor/bundle/ruby/2.3.0/gems/activesupport-5.1.3/lib/active_support/notifications.rb:166:in `instrument' 2017-08-21T13:47:59.925118+00:00 app[web.1]: I, [2017-08-21T13:47:59.925047 #4]  INFO -- : [8f6962a7-f07c-4754-959c-c51dafa37d76] Processing by DeviseTokenAuth::SessionsController#create as */* 2017-08-21T13:48:00.165031+00:00 app[web.1]: [8f6962a7-f07c-4754-959c-c51dafa37d76] vendor/bundle/ruby/2.3.0/gems/actionpack-5.1.3/lib/abstract_controller/base.rb:186:in `process_action' 2017-08-21T13:48:00.165036+00:00 app[web.1]: [8f6962a7-f07c-4754-959c-c51dafa37d76] vendor/bundle/ruby/2.3.0/gems/actionpack-5.1.3/lib/action_controller/metal/instrumentation.rb:30:in `process_action' 2017-08-21T13:47:59.925184+00:00 app[web.1]: I, [2017-08-21T13:47:59.925134 #4]  INFO -- : [8f6962a7-f07c-4754-959c-c51dafa37d76]   Parameters: {"email"=>"rob@example.com", "password"=>"[FILTERED]", "session"=>{"email"=>"rob@example.com", "password"=>"[FILTERED]"}} 2017-08-21T13:48:00.165032+00:00 app[web.1]: [8f6962a7-f07c-4754-959c-c51dafa37d76] vendor/bundle/ruby/2.3.0/gems/actionpack-5.1.3/lib/action_controller/metal/rendering.rb:30:in `process_action' 2017-08-21T13:48:00.165036+00:00 app[web.1]: [8f6962a7-f07c-4754-959c-c51dafa37d76] vendor/bundle/ruby/2.3.0/gems/actionpack-5.1.3/lib/action_controller/metal/params_wrapper.rb:252:in `process_action' 2017-08-21T13:47:59.976389+00:00 app[web.1]: D, [2017-08-21T13:47:59.976252 #4] DEBUG -- : [8f6962a7-f07c-4754-959c-c51dafa37d76]   Profile Load (2.1ms)  SELECT  "profiles".* FROM "profiles" WHERE (email = 'rob@example.com' AND provider='email') ORDER BY "profiles"."id" ASC LIMIT $1  [["LIMIT", 1]] 2017-08-21T13:48:00.165032+00:00 app[web.1]: [8f6962a7-f07c-4754-959c-c51dafa37d76] vendor/bundle/ruby/2.3.0/gems/actionpack-5.1.3/lib/abstract_controller/callbacks.rb:20:in `block in process_action' 2017-08-21T13:48:00.165037+00:00 app[web.1]: [8f6962a7-f07c-4754-959c-c51dafa37d76] vendor/bundle/ruby/2.3.0/gems/activerecord-5.1.3/lib/active_record/railties/controller_runtime.rb:22:in `process_action' 2017-08-21T13:48:00.163017+00:00 app[web.1]: I, [2017-08-21T13:48:00.162930 #4]  INFO -- : [8f6962a7-f07c-4754-959c-c51dafa37d76] Completed 500 Internal Server Error in 238ms (ActiveRecord: 19.2ms) 2017-08-21T13:48:00.165033+00:00 app[web.1]: [8f6962a7-f07c-4754-959c-c51dafa37d76] vendor/bundle/ruby/2.3.0/gems/activesupport-5.1.3/lib/active_support/callbacks.rb:131:in `run_callbacks' 2017-08-21T13:48:00.164815+00:00 app[web.1]: F, [2017-08-21T13:48:00.164748 #4] FATAL -- : [8f6962a7-f07c-4754-959c-c51dafa37d76]    2017-08-21T13:48:00.165033+00:00 app[web.1]: [8f6962a7-f07c-4754-959c-c51dafa37d76] vendor/bundle/ruby/2.3.0/gems/actionpack-5.1.3/lib/abstract_controller/callbacks.rb:19:in `process_action' 2017-08-21T13:48:00.164880+00:00 app[web.1]: F, [2017-08-21T13:48:00.164820 #4] FATAL -- : [8f6962a7-f07c-4754-959c-c51dafa37d76] IndexError (string not matched): 2017-08-21T13:48:00.165034+00:00 app[web.1]: [8f6962a7-f07c-4754-959c-c51dafa37d76] vendor/bundle/ruby/2.3.0/gems/actionpack-5.1.3/lib/action_controller/metal/rescue.rb:20:in `process_action' 2017-08-21T13:48:00.164933+00:00 app[web.1]: F, [2017-08-21T13:48:00.164884 #4] FATAL -- : [8f6962a7-f07c-4754-959c-c51dafa37d76]    2017-08-21T13:48:00.165034+00:00 app[web.1]: [8f6962a7-f07c-4754-959c-c51dafa37d76] vendor/bundle/ruby/2.3.0/gems/actionpack-5.1.3/lib/action_controller/metal/instrumentation.rb:32:in `block in process_action' 2017-08-21T13:48:00.165028+00:00 app[web.1]: F, [2017-08-21T13:48:00.164957 #4] FATAL -- : [8f6962a7-f07c-4754-959c-c51dafa37d76] vendor/bundle/ruby/2.3.0/gems/devise_token_auth-0.1.42/app/controllers/devise_token_auth/sessions_controller.rb:42:in `[]=' 2017-08-21T13:48:00.165035+00:00 app[web.1]: [8f6962a7-f07c-4754-959c-c51dafa37d76] vendor/bundle/ruby/2.3.0/gems/activesupport-5.1.3/lib/active_support/notifications.rb:166:in `block in instrument' 2017-08-21T13:48:00.165029+00:00 app[web.1]: [8f6962a7-f07c-4754-959c-c51dafa37d76] vendor/bundle/ruby/2.3.0/gems/devise_token_auth-0.1.42/app/controllers/devise_token_auth/sessions_controller.rb:42:in `create' 

1 Answers

Answers 1

Adding this to my migration fixed it:

reversible do |direction|   direction.up do     User.find_each do |user|       user.tokens = nil       user.save!     end   end end 
Read More

Sunday, July 9, 2017

RSpec 2.3 + Devise 1.0.11

Leave a Comment

I've got a really old Rails 2.3.18, ruby 1.9.3, rspec 1.x application which we are upgrading and it had restful-authentication in it. So I've replaced that with Devise 1.0.11.

I can login to the application, but my tests will not run;

Here is the test in question

require 'spec_helper'  describe CategoriesController do   context "As a logged in user" do     before do       login_user       current_firm = mock_model(Firm, :id => 1)       controller.stub!(:current_firm).and_return(current_firm)     end      describe "#index" do       it "should render index" do         get :index         response.should render_template('index')       end     end    end end 

Here is the error I get;

NoMethodError in 'CategoriesController As a logged in user#index should render index' You have a nil object when you didn't expect it! You might have expected an instance of ActiveRecord::Base. The error occurred while evaluating nil.[]= /home/map7/code/pdfcat/spec/spec_helper.rb:18:in `login_user' spec/controllers/categories_controller_spec.rb:6:in `block (3 levels) in <top (required)>' 

The error happens on this line;

[20, 29] in /usr/local/rbenv/versions/1.9.3-p551/lib/ruby/gems/1.9.1/gems/warden-0.10.7/lib/warden/session_serializer.rb    20        key    21      end    22      23      def store(user, scope)    24        return unless user => 25        session[key_for(scope)] = serialize(user)    26      end 

The problem is 'session' is nil when I'm at this point.

I've pushed the full code to here: https://github.com/map7/pdfcat/tree/devise

My plan was to get devise working in the tests then I could jump to Rails 3.0 and continue the upgrade.

1 Answers

Answers 1

There's an old message in google groups that I think is relevant: https://groups.google.com/forum/#!topic/rspec/4AHuPtHFD34

It recommends using this:

before do   request.env['warden'].stub(:authenticate!) { double(User) } end 

I'd probably put it in rails_helper.rb so that it runs for all tests

Read More

Saturday, May 20, 2017

AJAX Devise Form with Rails not Updating Data

Leave a Comment

I have a Devise form set up on my app to update a portion of the user's information (shipping address fields) from a different page (charges#new) and the server output seems to indicate it's working:

Started PUT "/users" for ::1 at 2017-05-11 17:10:52 -0700 Processing by RegistrationsController#update as JS   Parameters: {"utf8"=>"✓", "user"=>{"street_address_1"=>"**street address**", "street_address_2"=>"", "city"=>"**city**", "state"=>"CA", "zip"=>"**zip code**", "provence"=>"", "country"=>"United States", "has_shipping"=>"true"}, "commit"=>"Calculate Shipping"}   User Load (0.1ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = ?  ORDER BY "users"."id" ASC LIMIT 1  [["id", 1]]   User Load (0.1ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = ? LIMIT 1  [["id", 1]]   Order Load (0.1ms)  SELECT  "orders".* FROM "orders" WHERE "orders"."id" = ? LIMIT 1  [["id", 5]]   CACHE (0.0ms)  SELECT  "orders".* FROM "orders" WHERE "orders"."id" = ? LIMIT 1  [["id", 5]]   Rendered charges/_shipping.html.erb (5.0ms)   Rendered devise/registrations/update.js.erb (6.4ms) Completed 200 OK in 150ms (Views: 26.1ms | ActiveRecord: 0.3ms) 

However, when I check the console it still hasn't updated the information.

My registrations_controller is this:

class RegistrationsController < Devise::RegistrationsController   respond_to :html, :js    private    def sign_up_params     params.require(:user).permit(:first_name, :last_name, :email, :password, :password_confirmation, :phone, :stripe_customer_id, :street_address_1, :street_address_2, :city, :state, :zip, :provence, :country, :has_shipping)   end    def account_update_params     params.require(:user).permit(:first_name, :last_name, :email, :password, :password_confirmation, :current_password, :phone, :admin, :stripe_customer_id, :street_address_1, :street_address_2, :city, :state, :zip, :provence, :country, :has_shipping)   end end 

I have the following update.js.erb:

$(".shipping-info").html("<%= escape_javascript(render 'charges/shipping') %>") 

This is my charges#new:

<div class="product-row black-border-row row" style="margin-bottom: 60px">   <div class="container">     <%= render "shipping" %>   </div> <!-- page container --> </div> <!-- product row --> 

And here's the _shipping.html.erb partial:

<div class="row text-center">   <h3>Shipping Address</h3>   <%= simple_form_for(@user, url: registration_path(@user), html: { method: :put }, remote: true) do |f| %>     <div class="form-inputs text-left">       <div class="form-group col-sm-6">         <%= f.label :street_address_1 %>         <%= f.text_field :street_address_1, class: "form-control" %>       </div>       <div class="form-group col-sm-6">         <%= f.label :street_address_2 %>         <%= f.text_field :street_address_2, class: "form-control" %>       </div>       <div class="form-group col-sm-6">         <%= f.label :city %>         <%= f.text_field :city, class: "form-control" %>       </div><div class="form-group col-sm-3 col-xs-6">         <%= f.label :state %>         <%= f.text_field :state, class: "form-control" %>       </div><div class="form-group col-sm-3 col-xs-6">         <%= f.label :zip %>         <%= f.text_field :zip, class: "form-control" %>       </div><div class="form-group col-sm-6">         <%= f.label :provence %>         <%= f.text_field :provence, class: "form-control" %>       </div><div class="form-group col-sm-6">         <%= f.label :country %>         <%= f.text_field :country, class: "form-control" %>       </div><div class="form-group">         <%= f.hidden_field :has_shipping, value: true %>       </div>     </div> <!-- form inputs -->       <%= f.button :submit, "Calculate Shipping" %>   <% end %> </div> <!-- shipping row --> 

Can anyone see why this isn't updating the table?

EDIT: ROUTES As requested, here are my routes:

           orders_update GET    /orders/update(.:format)          orders#update         new_user_session GET    /users/sign_in(.:format)          devise/sessions#new             user_session POST   /users/sign_in(.:format)          devise/sessions#create     destroy_user_session DELETE /users/sign_out(.:format)         devise/sessions#destroy            user_password POST   /users/password(.:format)         devise/passwords#create        new_user_password GET    /users/password/new(.:format)     devise/passwords#new       edit_user_password GET    /users/password/edit(.:format)    devise/passwords#edit                          PATCH  /users/password(.:format)         devise/passwords#update                          PUT    /users/password(.:format)         devise/passwords#update cancel_user_registration GET    /users/cancel(.:format)           registrations#cancel        user_registration POST   /users(.:format)                  registrations#create    new_user_registration GET    /users/sign_up(.:format)          registrations#new   edit_user_registration GET    /users/edit(.:format)             registrations#edit                          PATCH  /users(.:format)                  registrations#update                          PUT    /users(.:format)                  registrations#update                          DELETE /users(.:format)                  registrations#destroy                     user GET    /users/:id(.:format)              users#show               home_index GET    /home/index(.:format)             home#index                     root GET    /                                 home#index                home_info GET    /home/info(.:format)              home#info              home_export GET    /home/export(.:format)            home#export                home_kits GET    /home/kits(.:format)              home#kits                 products GET    /products(.:format)               products#index                          POST   /products(.:format)               products#create              new_product GET    /products/new(.:format)           products#new             edit_product GET    /products/:id/edit(.:format)      products#edit                  product GET    /products/:id(.:format)           products#show                          PATCH  /products/:id(.:format)           products#update                          PUT    /products/:id(.:format)           products#update                          DELETE /products/:id(.:format)           products#destroy                      tag GET    /tags/:tag(.:format)              products#index                     cart GET    /cart(.:format)                   carts#show              order_items POST   /order_items(.:format)            order_items#create               order_item PATCH  /order_items/:id(.:format)        order_items#update                          PUT    /order_items/:id(.:format)        order_items#update                          DELETE /order_items/:id(.:format)        order_items#destroy                   orders POST   /orders(.:format)                 orders#create               edit_order GET    /orders/:id/edit(.:format)        orders#edit                    order GET    /orders/:id(.:format)             orders#show                          PATCH  /orders/:id(.:format)             orders#update                          PUT    /orders/:id(.:format)             orders#update                  charges GET    /charges(.:format)                charges#index                          POST   /charges(.:format)                charges#create               new_charge GET    /charges/new(.:format)            charges#new              edit_charge GET    /charges/:id/edit(.:format)       charges#edit                   charge GET    /charges/:id(.:format)            charges#show                          PATCH  /charges/:id(.:format)            charges#update                          PUT    /charges/:id(.:format)            charges#update                          DELETE /charges/:id(.:format)            charges#destroy 

And here are my relevant routes:

  get 'orders/update'    devise_for :users, :controllers => { registrations: 'registrations' }   resources :users, only: [:show]    get 'home/index'   root 'home#index'    get 'home/info'   get 'home/export'   get 'home/kits'    resources :products   get 'tags/:tag', to: 'products#index', as: :tag   resource :cart, only: [:show]   resources :order_items, only: [:create, :update, :destroy]   resources :orders, only: [:update, :edit, :show, :create]    resources :contacts   put "contacts/:id/archive" => "contacts#archive", as: "archive_contact"   put "contacts/:id/unarchive" => "contacts#unarchive", as: "unarchive_contact"    resources :charges 

1 Answers

Answers 1

I ended up putting an update method in my users_controller (as opposed to my registrations_controller):

  def update     @user = User.find(params[:id])     @user.update(account_update_params)   end    def account_update_params     params.require(:user).permit(:first_name, :last_name, :email, :password, :password_confirmation, :current_password, :phone, :admin, :stripe_customer_id, :street_address_1, :street_address_2, :city, :state, :zip, :provence, :country, :has_shipping)   end 

Adding the appropriate route, and changing the simple_form_for line to:

d<%= simple_form_for(@user, url: user_path(@user), html: { method: :put }, remote: true) do |f| %> 

And that did it.

Read More

Friday, May 5, 2017

Can't retrieve access_token, refresh_token from Omniauth authentication strategy with Devise, Rails 4

Leave a Comment

I am using such gems:

gem "omniauth-yandex" gem 'devise' 

My setup.

Routes:

 devise_for :users, :controllers => { :omniauth_callbacks => "callbacks" } 

Devise initializer:

config.omniauth :yandex, Rails.application.secrets.client_id , Rails.application.secrets.password, callback_url: "http://cb2bcdc4.ngrok.io/users/auth/yandex/callback" 

User model:

devise :database_authenticatable, :registerable,          :recoverable, :rememberable, :trackable, :validatable, :omniauthable, :omniauth_providers => [:yandex]      def self.from_omniauth(auth)         where(provider: auth.provider, uid: auth.uid).first_or_create do |user|         user.provider = auth.provider         user.uid = auth.uid         user.email =  auth.info.email         user.password = Devise.friendly_token[0,20]       end    end 

CallbackController:

class CallbacksController < Devise::OmniauthCallbacksController              def yandex        #empty     end end 

View:

  <%= link_to "Sign in with Yandex", user_yandex_omniauth_authorize_path, id: "sign_in" %> 

When I click "Sign in with Yandex" my application prompts for user permission and then redirects back to my application. User is created in my database with such fields-- e-mail,provider, uid. But I would like to have also access_token, refresh_token and expires_at because I am using few Yandex API's.

When I httlog'ed above action (From clicking "Sign in .." to the redirect back to my application) I received these results:

D, [2017-04-26T19:17:42.091838 #24865] DEBUG -- : [0;30;101m[httplog] Connecting: oauth.yandex.ru:443[0m D, [2017-04-26T19:17:42.266645 #24865] DEBUG -- : [0;30;101m[httplog] Sending: POST http://oauth.yandex.ru:443/token[0m D, [2017-04-26T19:17:42.267040 #24865] DEBUG -- : [0;30;101m[httplog] Data: client_id=097253682f9f41289ec5exxxxxxx&client_secret=xxxxxxdb4fxx0eadcbb8a4143&code=xxxxx327&grant_type=authorization_code&redirect_uri=http%3A%2F%2Fcb2bcdc4.ngrok.io%2Fusers%2Fauth%2Fyandex%2Fcallback%3Fstate%xxxxxxxxxxx%26code%xxxx[0m D, [2017-04-26T19:17:42.410712 #24865] DEBUG -- : [0;30;101m[httplog] Status: 200[0m D, [2017-04-26T19:17:42.410945 #24865] DEBUG -- : [0;30;101m[httplog] Benchmark: 0.143445 seconds[0m D, [2017-04-26T19:17:42.411168 #24865] DEBUG -- : [0;30;101m[httplog] Response: {"token_type": "bearer", "access_token": "xxxxxxxxuyBwtcyAFjkBZo3F3MCiIaTI", "expires_in": 31528753, "refresh_token": "xxxxxxxxxxxClSH:Pts0u-Mfls-vdEc7-zTOod9ZWzegNFRxxxxxxxxxxxxxKHpwsqBFUHHKtg"}[0m D, [2017-04-26T19:17:42.414748 #24865] DEBUG -- : [0;30;101m[httplog] Connecting: login.yandex.ru:443[0m D, [2017-04-26T19:17:42.609376 #24865] DEBUG -- : [0;30;101m[httplog] Sending: GET http://login.yandex.ru:443/info?format=json[0m D, [2017-04-26T19:17:42.609720 #24865] DEBUG -- : [0;30;101m[httplog] Data: [0m D, [2017-04-26T19:17:42.675702 #24865] DEBUG -- : [0;30;101m[httplog] Status: 200[0m D, [2017-04-26T19:17:42.675972 #24865] DEBUG -- : [0;30;101m[httplog] Benchmark: 0.065791 seconds[0m D, [2017-04-26T19:17:42.676211 #24865] DEBUG -- : [0;30;101m[httplog] Response: {"first_name": "xxxxxxxxxxx9", "last_name": "xxxxxxxxxxxxxxx", "display_name": "xxxxx", "emails": ["xxxxxx@yandex.ru"], "default_email": "xxxxx@yandex.ru", "real_name": "xxxxxx2", "login": "xxxxxxx", "sex": "male", "id": "xxxx123"}[0m 

Question: How can I save access_token, refresh token from Omniauth authentication process to user as it is clearly visible (7th line ) that it does retrieve it using my client_id and secret without any of my code.

Thanks.

What I have tried:

Added user.access_token = auth.credentials.token to self.from_omniauth(auth) method in user model. But there wasn't any positive change.

UPDATE:

The same problem exists on different platforms. For example, on shared hosting and in Linux Ubuntu (completely fresh project from scratch).

1 Answers

Answers 1

The access token is provided by the yandex gem, or to be more specific, the omniauth-oauth2 gem.

Just get inside your controller by doing: request.env["omniauth.auth"].credentials.token

Boom. That's your access token. (refresh token and expiry data is also in the credentials hash, just print it out)

Read More

Monday, March 27, 2017

Rails, Devise, and Facebook Oauth: request.env[“omniauth.auth”] is nil

Leave a Comment

I'm using Facebook Oauth and Devise in my rails app. I successfully get taken to the facebook login page, but then get an error on the callback action. Tracked it down to request.env["omniauth.auth"] returning nil in my callback action.

Gemfile:

gem 'devise' gem 'omniauth' gem 'omniauth-facebook' 

Routes:

  devise_scope :user do     get '/users/auth/facebook/callback', to: 'users/omniauth_callbacks#facebook'   end    resources :users   devise_for :users, path: '', path_names: { sign_up: 'register', sign_in: 'login', sign_out: 'logout'}, :controllers => { :omniauth_callbacks => "users/omniauth_callbacks" } 

initializers/devise.rb:

config.omniauth :facebook, Figaro.env.facebook_key, Figaro.env.facebook_secret,                 scope: 'email,public_profile',                 callback_url: Figaro.env.facebook_callback_url 

User.rb:

class User < ActiveRecord::Base   devise :database_authenticatable, :registerable,            :recoverable, :rememberable, :trackable, :confirmable, :validatable,           :omniauthable, :omniauth_providers => [:facebook] 

omniauth_callbacks_controller.rb:

class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController   def facebook      @user = User.from_omniauth(request.env["omniauth.auth"])      request.env["omniauth.auth"]   ## <<=== this is nil 

Any idea why my request.env["omniauth.auth"] is returning nil?

1 Answers

Answers 1

So finally i am able to find the issue, i was also struggling with the same issue for a long time, but now we have the solution, ok here we go:

The problem is with the devise.rb, just remove this from the file:

config.omniauth :facebook, Figaro.env.facebook_key, Figaro.env.facebook_secret,                 scope: 'email,public_profile',                 callback_url: Figaro.env.facebook_callback_url 

and then restart your server and then try to get login through facebook.

You will get what you want :)

Thanks, Enjoy Coding :)

Read More

Monday, February 13, 2017

Rails & Devise: Failed registration attempt redirects to root url

Leave a Comment

When a user has a failed registration attempt, my app is redirecting them to the root url instead of rendering the registration page.

Routes:

resources :users   devise_for :users, path: '', path_names: { sign_up: 'register', sign_in: 'login', sign_out: 'logout'}, :controllers => { :omniauth_callbacks => "callbacks" } 

registrations#new:

<%= form_for(resource, as: resource_name, url: registration_path(resource_name), :html => {:class => "col s12 form-text", autocomplete: "off"}) do |f| %>    <%# render 'devise/errors', resource: resource %>   <div class="row">     <div class="input-field col s12">       <%= f.text_field :full_name, :class => "validate", :required => true, :placeholder => "Robin Smith" %>        <%# f.label :full_name, :class => "allcaps active", :data => {:error => 'Name Required'} %>       <%= f.label :full_name, :class => "allcaps" %>     </div>   </div>    <div class="row">     <div class="input-field col s12">       <%= f.email_field :email, :class => "validate", :required => true, placeholder: " " %>       <%= f.label :email, :class => "allcaps" %>     </div>   </div>    <div class="row">     <div class="input-field col s12">       <%= f.password_field :password, :class => "validate", :required => true, placeholder: " " %>       <%= f.label :password, "password (min 6 chars)", :class => "allcaps" %>     </div>   </div>    <div class="button-container">     <button class="waves-effect btn-flat btn-large"type="submit" name="action">Join Now</button>   </div> <% end %> 

I can go to mydomain.com/register and the form appears. But when there's a failed registration attempt, they're redirected to the root url instead of re-rendering the register page with errors.

I feel like there must be something wrong in my routes. Any idea?

1 Answers

Answers 1

You can override the Devise create action and redirect to your url:

# registrations_controller.rb class RegistrationsController < Devise::RegistrationsController    def create     build_resource(sign_up_params)      resource.save     yield resource if block_given?     if resource.persisted?       if resource.active_for_authentication?         set_flash_message! :notice, :signed_up         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}"         expire_data_after_sign_in!         respond_with resource, location: after_inactive_sign_up_path_for(resource)       end    else      clean_up_passwords resource      set_minimum_password_length      resource.errors.full_messages.each {|x| flash[x] = x}      #redirect_to your_path    end   end  end  

And in routes:

resources :users   devise_for :users, path: '', path_names: { sign_up: 'register', sign_in: 'login', sign_out: 'logout'}, :controllers => { :registrations => "registrations", :omniauth_callbacks => "callbacks" } 
Read More