I'm using devise with my rails 3 app. For some reason the sign in with Remember Me is not working.
Could this be due to testing on localhost:3000 ?
in devise.rb, I have the following set:
config.remember_for = 2.weeks
In the logs, when I post a signin I see:
Started POST "/users/sign_in" for 127.0.0.1 at Thu May 12 20:53:04 -0700 2011
Processing by SessionsController#create as HTML
Parameters: {"signIn"=>"LOG IN", "authenticity_token"=>"GR09TIq4uSbu6UWxDRhpfQeLWp7qtJTxkCFksLmFzdE=", "utf8"=>"✓", "user"=>{"remember_me"=>"on", "password"=>"[FILTERED]", "email"=>"xxxx#xxxxxxx-inc.com"}}
Is there anything wrong there?
I also have the following in my sessions_controller.rb
class SessionsController < Devise::SessionsController
prepend_before_filter :require_no_authentication, :only => [ :new, :create ]
include Devise::Controllers::InternalHelpers
# GET /resource/sign_in
def new
clean_up_passwords(build_resource)
render_with_scope :new
end
# POST /resource/sign_in
def create
resource = warden.authenticate!(:scope => resource_name, :recall => "new")
#set_flash_message :notice, :signed_in
sign_in_and_redirect(resource_name, resource)
end
# GET /resource/sign_out
def destroy
#set_flash_message :notice, :signed_out if signed_in?(resource_name)
sign_out_and_redirect(resource_name)
end
protected
def after_sign_in_path_for(resource)
if resource.is_a?(User) && resource.banned?
sign_out resource
flash[:error] = "This account has been suspended."
root_path
else
super
end
end
end
Any ideas why signing in and remembering is not working? Thanks
This happens because remember_me comes in params as "on", but is compared to Devise::TRUE_VALUES, which are [true, 1, '1', 't', 'T', 'true', 'TRUE'].
The easiest way is to make it work is to insure your remember_me comes as one of that values. Example of check-box(notice value="1"):
<input type="checkbox" name="user[remember_me]" value="1" checked="checked" />
Another way if you want to make it work with "on" value you can add "on" to Devise::TRUE_VALUES.
So in your config/initializers/devise.rb just add as the first line:
Devise::TRUE_VALUES << ["on"]
The Devise remember_user_token cookie could be set to 'secure only', in which case it doesn't work with the development rails server on http (browser never sends it back to the server).
Check initializers/devise.rb for rememberable_options = {:secure => true}
Do you have the sessions set aswell with config.timeout_in = 10.minutes?
If so see this contribution on stackoverflow which solves it solution
My problem with this was this single line in User.rb (I updated from Michael Hartl login mechanism to devise)
before_save :create_remember_token
I commented it out and it worked.
I also have :
User.rb
class User < ActiveRecord::Base
# Include default devise modules. Others available are:
# :token_authenticatable, :confirmable,
# :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,:token_authenticatable,
:recoverable, :rememberable, :trackable, :validatable
On devise.rb, I only added Devise::TRUE_VALUES << ["on"] and uncommented config.remember_for = 2.weeks
Related
I'm trying to add an external login with facebook, but whenever I realize the home page correctly directs me to the facebook page, but then I get the following error, and could not understand what it can be.
"Users::OmniauthCallbacksController#facebook is missing a template for this request format and variant. request.formats: ["text/html"] request.variant: [] NOTE! For XHR/Ajax or API requests, this action would normally respond with 204 No Content: an empty white screen. Since you're loading it in a web browser, we assume that you expected to actually render a template, not nothing, so we're showing an error to be extra-clear. If you expect 204 No Content, carry on. That's what you'll get from an XHR or API request. Give it a shot."
"That's what you'll get from an XHR or API request. Give it a shot."
raise ActionController::UnknownFormat, message
else
logger.info "No template found for #{self.class.name}\##{action_name}, rendering head :no_content" if logger
super
this is my controller
class Users::OmniauthCallbacksController < ApplicationController
def facebook
#User = User.from_omniauth(request.env["omniauth.auth"])
if #User.persisted?
#User.remember_me = true
sign_in_and_redirect #User, event: :authentication
end
end
end
this is the user model.
class User < ActiveRecord::Base
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable,:omniauthable, :omniauth_providers => [:facebook]
def self.from_omniauth(auth)
where(provider: auth[:provider], uid: auth[:uid]).first_or_create do |user|
if auth[:info]
user.email = auth[:info][:email]
user.name = auth[:info][:name]
end
user.password = Devise.friendly_token[0,20]
end
end
has_many :articles , :dependent => :destroy
end
and i put this line in config/initializers/divise.rb
config.omniauth :facebook, '504432376574467', 'b2bb80641fcc2ca4d28e48c5ce*******'
My guess would be that User.from_omniauth fails to create the user (possibly due to user.password and user.password_confirmation not matching), which causes the Users::OmniauthCallbacksController#facebook to reach the end of the method without going inside the if clause.
To check, you could for example add an else clause to your Facebook callback and raise an error in there.
Update from comments: I want to do Stateless of Restful in HTTP connection. My goal is making a system that when users log into my app, my app's server assigns an id to them. Because if my app cannot do so, other users can access a user pages (if they write something in the URL randomly). And my server's model have users' ids in that time. When users log out my app, these ids are deleted from model. Maybe I can call these system 'Session'.
I am performing user authentication in my app. I want to realize Stateless in there that it pass id to users when they log in my app. However, I'm a beginner, so I don't know how to do it. I think I should write these codes in the main controller, but am not sure. How can I do this process?
home_controller
class HomeController < ApplicationController
before_filter :find_user, only: [:index]
def index
#id = params[:id]
#email = [:email]
if #id == #user.id && #email == #user.email
render :text => "sucsess"
else
render :text => "fail"
end
end
def create
userData = UserData.new(create_params)
user = User.find(params[:id]).to_json
# エラー処理
unless userData.save
#error_message = [memo.errors.full_messages].compact
end
end
private
def find_user
#user = User.find(params[:id]) # You should specify this code what are your comparing to.
end
end
routes.rb
Rails.application.routes.draw do
devise_for :users
root to: "home#index"
get 'home/index'
namespace :home, default: {format: :json} do
resources :index, only: :create
end
end
user.rb
class User < ActiveRecord::Base
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable, :omniauthable
end
When a user logs in via Devise, they are given a unique token that is stored in the browser's session. When the next page is rendered, the browser will pass it's session cookie to the rails app, and Devise will gather the user token from the session cookie and populate the current_user variable with the user object that matches that token.
So if you follow the instructions for setting up Devise in its README, then once a user has logged in, you can access that logged in user's User object via current_user
Very new to programming in general, little experience with Rails and very little to no experience with ember. I may be taking embers "build ambitious web apps" too ambitiously at the moment. I can not get authentication with Devise setup properly. It is always returning 401 when trying to authenticate via ember and for some reason when I make some of the required changes for ember-simple-auth to work via the github page, my rails "flash messages" start error out. As in when I visit /users/sign_in via the browser for html. More detail on that after I show the code I feel is relevant. Let me know what else I need to post if I leave anything out.
I'm using the following setup:
ember-cli: 0.1.15
ember-cli-simple-auth: 0.7.3
ember-cli-simple-auth-devise: 0.7.3
do I need both simple-auth and simple-auth-devise, or does ember-cli-simple-auth-devise encompass/contain ember-cli-simple-auth?
rails: 4.2.0
devise: 3.4.1
Ember Code
config/environment.js
ENV['simple-auth'] = {
authorizer: 'simple-auth-authorizer:devise',
};
ENV['simple-auth-devise'] = {
crossOriginWhitelist: ['*'],
//serverTokenEndpoint: 'http://localhost:3000/users/sign_in'
//I have tried it both with and with out serverTokenEndpoint
};
app/controllers/login.js
import Ember from 'ember';
import LoginControllerMixin from 'simple-auth/mixins/login-controller-mixin';
export default Ember.Controller.extend(LoginControllerMixin, {
authenticator: 'simple-auth-authenticator:devise'
});
app/routes/application.js
import Ember from 'ember';
import ApplicationRouteMixin from 'simple-auth/mixins/application-route-mixin';
export default Ember.Route.extend(ApplicationRouteMixin);
app/routes/protected.js
import Ember from 'ember';
import AuthenticatedRouteMixin from 'simple-auth/mixins/authenticated-route-mixin';
export default Ember.Route.extend(AuthenticatedRouteMixin);
app/templates/login.hbs
Login
{{input value=identification placeholder='Enter Login'}}
Password
{{input value=password placeholder='Enter Password' type='password'}}
Login
Rails Code
app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
# Prevent CSRF attacks by raising an exception.
# For APIs, you may want to use :null_session instead.
#commented the following out to rule it out.
#protect_from_forgery with: :null_session
before_filter :authenticate_user_from_token!
before_filter :authenticate_user!
def authenticate_user_from_token!
authenticate_with_http_token do |token, options|
user_email = options[:user_email].presence
user = user_email && User.find_by_email(user_email)
if user && Devise.secure_compare(user.authentication_token, token)
sign_in user, store: false
end
end
end
end
app/controllers/sessions_controller.rb
class SessionsController < Devise::SessionsController
respond_to :html, :json
def create
super do |user|
if request.format.json?
data = {
token: user.authentication_token,
user_email: user.email
}
render json: data, status: 201 and return
end
end
end
end
app/models/user.rb
class User < ActiveRecord::Base
before_save :ensure_authentication_token
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
def ensure_authentication_token
if authentication_token.blank?
self.authentication_token = generate_authentication_token
end
end
private
def generate_authentication_token
loop do
token = Devise.friendly_token
break token unless User.where(authentication_token: token).first
end
end
end
config/routes.rb
resources :specials
#devise_for :users
devise_for :users, controllers: { sessions: 'sessions' }
root to: 'specials#index'
When I submit the ember login form no matter what I submit I receive back:
Started POST "/users/sign_in" for 127.0.0.1 at 2015-03-04 19:50:57 -0500
Processing by SessionsController#create as JSON
Parameters: {"user"=>{"password"=>"[FILTERED]", "user_email"=>"foo#bar.com"}}
Completed 401 Unauthorized in 7ms
If I visit http://localhost:3000/users/sign_in I receive the following:
/app/views/layouts/application.html.erb where line #11 raised:
undefined method `flash' for #<ActionDispatch::Request:0x007fec37dfe708>
app/views/layouts/application.html.erb
<p class="notice"><%= notice %></p>
<p class="alert"><%= alert %></p>
<%= yield %>
this error appeared after I made the advised changes from https://github.com/simplabs/ember-simple-auth/tree/master/packages/ember-simple-auth-devise
This an issue that has been fixed by https://github.com/simplabs/ember-simple-auth/pull/456. Just update your app/controllers/application_controller.rb and your app/controllers/sessions_controller.rb according to the revised https://github.com/simplabs/ember-simple-auth/blob/master/packages/ember-simple-auth-devise/README.md.
Also add
identificationAttributeName: 'email'
to ENV['simple-auth-devise'] in config/environment.js. This last addition can be removed again, onces the changes of the pull request are released.
Problem statement
In my controller I am trying to change a boolean attribute not_registered in Update using resource.update_attribute(resource.not_registered, 1), but am getting NoMethodError: undefined method 0= for #<User:0x007fe470de2950. Any idea why this is happening? Appreciate any ideas you may have, this is the last bug I need to work out before completing this project, which will be my first in RoR!
My progress
I looked into using save instead, but can't because I need to skip validation. Also seems inefficient since I am only updating one attribute
In the shell, resource (created by Devise) appears to be an instance of the current User
Before executing the Update controller resource.not_registered
=> 0, which is correct... so I know that resource.not_registered is correct syntax.
If I run resource.update_attribute, I get !! #<ArgumentError: wrong number of arguments (0 for 2)> ... so I know that update_attribute exists as a method for resource
Finally, I found the documentation for update_attribute here.
Code
/app/controllers/registrations_controller.rb:
class RegistrationsController < Devise::RegistrationsController
def update
if resource.update_with_password(params[resource_name])
resource.update_attribute(resource.not_registered, "1") # this is where I get my error message
set_flash_message :notice, :updated
sign_in resource_name, resource, :bypass => true
redirect_to after_update_path_for(resource)
else
clean_up_passwords(resource)
render "edit"
end
end
# Code removed for brevity
end
/app/models/user.rb:
class User < ActiveRecord::Base
devise :database_authenticatable, :registerable, :token_authenticatable,
:recoverable, :rememberable, :trackable, :validatable, :confirmable
attr_accessible :email, :password, :password_confirmation,
:remember_me, :not_registered, :pay_method, :pay_desc, :email_instructions, :current_password
def update_with_password(params={})
# code removed for brevity
end
end
Change following line
resource.update_attribute(resource.not_registered, "1") # this is where I get my error message
to
resource.update_attribute(:not_registered, "1") # this is where I get my error message
and read some solid Rails documentation. I suggest going through http://guides.rubyonrails.org/index.html
My rails application uses devise to handle registration, authentication, etc. I'm using the confirmable module. The bug is this– when a user registers with email, Devise is sending two confirmation emails with different confirmation links. One link works, the other directs the user to an error page.
Devise spits out a message associated with the error: "Confirmation token is invalid" and takes the user to the Resend Confirmation Email page.
I'm hosting with heroku and using sendgrid to send the emails. update: The bug also occurs on localhost.
I have no idea where the root of this bug is, and this might be more code than what you need to see:
models/user.rb
...
devise :database_authenticatable, :registerable, :omniauthable,
:recoverable, :rememberable, :trackable, :validatable,
:confirmable, :authentication_keys => [:login]
...
## callbacks
after_create :account_created
# called after the account is first created
def account_created
# check if this activiy has already been created
if !self.activities.where(:kind => "created_account").blank?
puts "WARNING: user ##{self.id} already has a created account activity!"
return
end
# update points
self.points += 50
self.save
# create activity
act = self.activities.new
act.kind = "created_account"
act.created_at = self.created_at
act.save
end
...
def confirmation_required?
super && (self.standard_account? || self.email_changed)
end
...
controllers/registrations_controller.rb
class RegistrationsController < Devise::RegistrationsController
def update
unless #user.last_sign_in_at.nil?
puts "--------------double checking whether password confirmation is required--"
## if the user has not signed in yet, we don't want to do this.
#user = User.find(current_user.id)
# uncomment if you want to require password for email change
email_changed = #user.email != params[:user][:email]
password_changed = !params[:user][:password].empty?
# uncomment if you want to require password for email change
# successfully_updated = if email_changed or password_changed
successfully_updated = if password_changed
params[:user].delete(:current_password) if params[:user][:current_password].blank?
#user.update_with_password(params[:user])
else
params[:user].delete(:current_password)
#user.update_without_password(params[:user])
end
if successfully_updated
# Sign in the user bypassing validation in case his password changed
sign_in #user, :bypass => true
if email_changed
flash[:blue] = "Your account has been updated! Check your email to confirm your new address. Until then, your email will remain unchanged."
else
flash[:blue] = "Account info has been updated!"
end
redirect_to edit_user_registration_path
else
render "edit"
end
end
end
end
controllers/omniauth_callbacks_controller
class OmniauthCallbacksController < Devise::OmniauthCallbacksController
skip_before_filter :verify_authenticity_token
def facebook
user = User.from_omniauth(request.env["omniauth.auth"])
if user.persisted?
flash.notice = "Signed in!"
# if the oauth_token is expired or nil, update it...
if (DateTime.now > (user.oauth_expires_at || 99.years.ago) )
user.update_oauth_token(request.env["omniauth.auth"])
end
sign_in_and_redirect user
else
session["devise.user_attributes"] = user.attributes
redirect_to new_user_registration_url
end
end
end
config/routes.rb
...
devise_for :users, controllers: {omniauth_callbacks: "omniauth_callbacks",
:registrations => "registrations"}
...
I'm happy to provide more information if needed. I'm also open to customizing/overriding the devise mailer behavior, but I don't know how to go about that.
Much thanks!
Solved!
I was able to override Devise::Mailer and force a stack trace to find out exactly what was causing duplicate emails. Devise::Mailer#confirmation_instructions was being called twice, and I found out that the problem was with my :after_create callback, shown below:
in models/user.rb...
after_create :account_created
# called after the account is first created
def account_created
...
# update points
self.points += 50
self.save
...
end
Calling self.save somehow caused the mailer to be triggered again. I solved the problem by changing when the points are added. I got rid of the after_create call and overrode the confirm! method in devise to look like this:
def confirm!
super
account_created
end
So now the user record doesn't get modified (adding points) until after confirmation. No more duplicate emails!
I originally went with Thomas Klemm's answer but I went back to look at this when I had some spare time to try and figure out what was happening as it didn't feel right.
I tracked the 'problem' down and noticed that it only happens when :confirmable is set in your devise (User) model and reconfirmable is enabled in the devise initializer - which in hindsight makes a lot of sense because essentially in the after_create we ARE changing the User model, although we aren't changing the email address - I suspect Devise may do this because the account isn't confirmed yet, but in any case it is easy to stop the second email just by calling self.skip_reconfirmation! in the after_create method.
I created a sample rails project with a couple of tests just to ensure the correct behaviour. Below are the key excerpts. If you have far too much time on your hands, you can see the project here: https://github.com/richhollis/devise-reconfirmable-test
app/models/User.rb
class User < ActiveRecord::Base
# Include default devise modules. Others available are:
# :token_authenticatable, :confirmable,
# :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable, :confirmable
# Setup accessible (or protected) attributes for your model
attr_accessible :email, :password, :password_confirmation, :remember_me
after_create :add_attribute
private
def add_attribute
self.skip_reconfirmation!
self.update_attributes({ :status => 200 }, :without_protection => true)
end
end
initializers/devise.rb
# Use this hook to configure devise mailer, warden hooks and so forth.
# Many of these configuration options can be set straight in your model.
Devise.setup do |config|
..
..
# If true, requires any email changes to be confirmed (exactly the same way as
# initial account confirmation) to be applied. Requires additional unconfirmed_email
# db field (see migrations). Until confirmed new email is stored in
# unconfirmed email column, and copied to email column on successful confirmation.
config.reconfirmable = true
..
..
end
spec/models/user_spec.rb
require 'spec_helper'
describe User do
subject(:user) { User.create(:email => 'nobody#nobody.com', :password => 'abcdefghijk') }
it "should only send one email during creation" do
expect {
user
}.to change(ActionMailer::Base.deliveries, :count).by(1)
end
it "should set attribute in after_create as expected" do
user.status.should eq(200)
end
end
Running the rspec tests to ensure only one email is sent confirms the behaviour:
..
Finished in 0.87571 seconds 2 examples, 0 failures
Thanks for your great solution, Stephen! I've tried it and it works perfectly to hook into the confirm! method. However, in this case, the function is being called (as the name says) when the user clicks the confirmation link in the email he receives.
An alternative is to hook into the generate_confirmation_token method, so your method is called directly when the confirmation token is created and the email is sent.
# app/models/user.rb
def generate_confirmation_token
make_owner_an_account_member
super # includes a call to save(validate: false),
# so be sure to call whatever you like beforehand
end
def make_owner_an_account_member
self.account = owned_account if owned_account?
end
Relevant source of the confirmation module.