ruby on rails - Devise_token_auth and Devise login with username and email -
i continuously errors when trying add token authentication existing rails application , it's stemming devise authentication_keys. in rails 4 app allow users login either username or email , i'd provide same functionality api.
the error i'm getting when logging in api follows
started post "/api/v1/auth/sign_in" 127.0.0.1 @ 2016-04-19 23:40:26 -0400 processing devisetokenauth::sessionscontroller#create json parameters: {"login"=>"email@example.com", "password"=>"[filtered]", "session"=>{"login"=>"email@example.com", "password"=>"[filtered]"}} can't verify csrf token authenticity geokit using domain: localhost user load (1.3ms) select "users".* "users" (login = 'email@example.com' , provider='email') order "users"."id" asc limit 1 sqlite3::sqlexception: no such column: login: select "users".* "users" (login = 'email@example.com' , provider='email') order "users"."id" asc limit 1 completed 500 internal server error in 5ms sqlite3::sqlexception - no such column: login:
the code user model below. problem stemming user load not converting login parameter search via email (or username). code below works totally fine regular devise logins.
#user.rb devise :database_authenticatable, :registerable, :confirmable, :recoverable, :rememberable, :trackable, :validatable, :authentication_keys => [:login] include devisetokenauth::concerns::user def self.find_for_database_authentication(warden_conditions) conditions = warden_conditions.dup if login = conditions.delete(:login) where(conditions.to_h).where(['lower(username) = :value or lower(email) = :value', { :value => login.downcase }]).first else where(conditions.to_h).first end end
thanks or guidance!
edit when sending params email , password there error.
processing devisetokenauth::sessionscontroller#create json parameters: {"email"=>"email@example.com", "password"=>"[filtered]", "session"=>{"email"=>"email@example.com", "password"=>"[filtered]"}} can't verify csrf token authenticity unpermitted parameters: email, format, session completed 401 unauthorized in 49ms (views: 0.3ms | activerecord: 0.0ms | solr: 0.0ms)
from devise doc, need following:
app/models/user.rb
attr_accessor :login
config/initializers/devise.rb
devise.setup |config| config.authentication_keys = [:login] config.case_insensitive_keys = [:login] end
devise_token_auth
unmaintained (cf unmerged pr) , therefore out-of-sync devise, why need override sessionscontroller:config/routes.rb
rails.application.routes.draw namespace :api scope module: :v4 mount_devise_token_auth_for "user", at: "auth", controllers: { sessions: "api/v4/devise_token_auth/sessions" } end end end
app/controllers/api/v4/devise_token_auth/sessions_controller.rb
module api module v4 module devisetokenauth class sessionscontroller < ::devisetokenauth::sessionscontroller def create # check field = (resource_params.keys.map(&:to_sym) & resource_class.authentication_keys).first @resource = nil if field q_value = resource_params[field] if resource_class.case_insensitive_keys.include?(field) q_value.downcase! end q = "#{field.to_s} = ? , provider='email'" if activerecord::base.connection.adapter_name.downcase.starts_with? "mysql" q = "binary " + q end # log in email , username if field == :login @resource = resource_class.where("email = ? or username = ?", q_value, q_value).first else @resource = resource_class.where(q, q_value).first end # log in email , username end end if @resource && valid_params?(field, q_value) && (!@resource.respond_to?(:active_for_authentication?) || @resource.active_for_authentication?) valid_password = @resource.valid_password?(resource_params[:password]) if (@resource.respond_to?(:valid_for_authentication?) && !@resource.valid_for_authentication? { valid_password }) || !valid_password render_create_error_bad_credentials return end # create client id @client_id = securerandom.urlsafe_base64(nil, false) @token = securerandom.urlsafe_base64(nil, false) @resource.tokens[@client_id] = { token: bcrypt::password.create(@token), expiry: (time.now + ::devisetokenauth.token_lifespan).to_i } @resource.save sign_in(:user, @resource, store: false, bypass: false) yield @resource if block_given? render_create_success elsif @resource && !(!@resource.respond_to?(:active_for_authentication?) || @resource.active_for_authentication?) render_create_error_not_confirmed else render_create_error_bad_credentials end end end end end end
test example
resource "user sign in" let(:user) { build(:user) } before(:each) user.create_new_auth_token end post "/api/auth/sign_in" parameter :email, "email address" parameter :password, "password user" "fails incorrect login , password" do_request(login: "user.does.not@exist.com", password: "wrong_password") expect(status).to eq(401) expect(json.parse(response_body)["errors"]) .to include("invalid login credentials. please try again.") end "logs in correct username , password" user.save! do_request(login: user.username, password: user.password) expect(status).to eq(200) expect(json.parse(response_body)["data"]["email"]).to include(user.email) end "logs in correct email , password" user.save! do_request(login: user.email, password: user.password) expect(status).to eq(200) expect(json.parse(response_body)["data"]["email"]).to include(user.email) end end end
Comments
Post a Comment