I am currently making Rails app using devise.
After a new user signs up, I need to access newly singed up user's information to run my customized function.
However it seems like devise's current_user is nil since the user is not logged in yet until the user confirms the email.
I essentially just need to check whether user's confirmed_at is nil.
Is there a way to do this?
I would do this in the User model with a callback:
# in app/model/user.rb (assuming you have a User model)
after_create :run_customized_function
private
def run_customized_function
# whatever need to be done with this user, for example:
# Rails.logger.info("User##{id} just signed up with email '#{email}'")
end
Related
I have a user table with guest column(boolean) to distinguish user's identity.
I don't want user with guest? => true able to login.
Is it possible to override Sorcery's login method?
I want it work like
User.where(guest: false).authenticate(email, password)
Another way I'm thinking is to separate User and GuestUser using polymorphic association.
However, I don't really want to create GuestUser with same columns as User.
Please give me some suggestions.
# find specific user and check guest or not
user = User.find_by_email(email)
if user && user.guest == false
User.authenticate(email, password)
end
I let users log in initially without confirming their email address - but after 7 days, if they haven't confirmed - I block access until they confirm their address.
(Note - this is achieved by setting config.allow_unconfirmed_access_for = 7.days in the Devise initialiser)
If they hit the 'grace' limit (e.g. they don't confirm and 7 days pass) then I want to:
send them to a page which explains what is going on (I can do this
part)
automatically re-send the confirmation email
to do #2 I need to access the user to get the email address.
Devise obviously 'knows' who the user is - that's how it knows they have passed the confirmation expiry.
If the user has just tried to log in, then I can get this by looking in the params.
However if the user already has a live login token in their session, then when they pass the magical week - they'll suddenly start being rejected by devise. How do I access the user in this case?
#based on
#https://github.com/plataformatec/devise/wiki/How-To:-Redirect-to-a-specific-page-when-the-user-can-not-be-authenticated
#https://stackoverflow.com/questions/9223555/devise-with-confirmable-redirect-user-to-a-custom-page-when-users-tries-to-sig
class CustomFailure < Devise::FailureApp
def redirect_url
if warden_message == :unconfirmed
user = User.find_by_email(params.dig(:user,:email))
user&.send_confirmation_instructions
if user.nil?
#if the user had a valid login session when they passed the grace period, they will end up here
!! how do I get the user in this scenario !!
end
confirmation_required_info_path(params: {found: !user.nil?})
elsif warden_message == :invalid
new_user_session_path(user:{email: params.dig(:user,:email)})
else
super
end
end
# You need to override respond to eliminate recall
def respond
if http_auth?
http_auth
else
redirect
end
end
end
This achieves goal #1, but it only achieves goal #2 if if the failure is the result of new signup
is there a direct way to access the user when they have a live session, but have passed the expiry date?
(current_user is not available, env['warden'].user is nil)
thank you
Rails 5.0.6
devise 4.2
edit: Updating to clarify with an example scenario where I need help:
day 0: User signs up with email/password. I let them in without confirming their email. They have a 7-day grace period to confirm their email.
day 2: They log out
day 7 (morning): They log in again
day 7 (later in the day): They do some action. Their login token is still valid - devise recognises it, finds their user record and checks if they have confirmed their email address. They have not - so devise refuses to authorise the action, giving the error :unconfirmed
In this scenario - they come through to the failure app. I will redirect them to a page which says 'you have passed your 7-day grace period, you really need to confirm your email address now'.
In the failure app, I want to know what their email address is so that I can automatically re-send the confirmation email. How do I get this?
Note - in this scenario, devise has refused authorisation. current_user is nil. However Devise clearly 'knows' who the user is - because it was able to look up their record in the database, and check that they had gone past the grace period for unconfirmed email addresses. How do I access that same 'knowledge'
I think there are better ways of achieving the same result without creating a Devise::FailureApp:
This could be achieved by overriding the confirmed? method from Devise's resource extension present in the Confirmable module.
A simple example would be:
Add a delayed_confirmation_expiry_date datetime field to your model's table, via migration.
This field will be used to store the expiry datetime when the user first registers into your app. You will have to override the SessionsController#create method for that, so it can call the #delay_confirmation! method on your resource.
Add inside your User model equivalent :
# Will update the field you have added with the new temporary expiration access datetime
def delay_confirmation!(expiry_datetime=7.days.from_now)
self.delayed_confirmation_expiry_date = expiry_datetime
self.save
end
# Override that will make sure that, once the user is confirmed, the delayed confirmation information is cleared
def confirm(args={})
clear_delay_confirmation!
super
end
# Self-explanatory
def clear_delay_confirmation!
self.delayed_confirmation_expiry_date = nil
self.save
end
# Used on your controllers to show messages to the user warning him about the presence of the confirmation delay
def confirmation_is_delayed?
self.confirmed? && !self.confirmed_at.present?
end
# Overrides the default implementation to allow temporary access for users who haven't confirmed their accounts within the time limit
def confirmed?
if !self.confirmation_is_delayed?
super
else
self.delayed_confirmation_expiry_date >= DateTime.now.in_time_zone
end
end
In my rails app, I am using devise for my authentication system. I have a situation in which I want to encrypt some data using a key that is based off of the user's password. The easiest way that I can think to do this is during a successful sign in, to generate the user's private key from their plain-text password (passed in from the login form) and store that in the user's session. I don't really want to ask the user to enter their password more than once.
Does devise provide a way to specify a callback function after a successful login? Or is there a better way to do this?
http://rubydoc.info/github/plataformatec/devise/master/Devise/Models/DatabaseAuthenticatable#after_database_authentication-instance_method
In the user model where you're using devise create a after_database_authentication instance method.
Assume you have Devise resourse User with attribut password, then you can access user password after login in after_sign_in_path_for, which is called after sucessful login.
# app/control,lers/application_controller.rb
class ApplicationController < ActionController::Base
def after_sign_in_path_for(resource)
password = param[:user][:password]
do_cool_stuf_with_password(password)
#...
return url_for_root
end
end
I would like to manually create new Users, without forcing them to verify their email address.
The idea is to allow existing users to automatically add their friends without requiring their registration. It makes sense for the business case I'm working to solve.
How can this be achieved with Devise?
The skip_confirmation! method is available to any confirmable model.
#user = User.new params[:user]
#user.skip_confirmation! # Sets confirmed_at to Time.now, activating the account
#user.save
The user account will be activated though. If you don't want that, continue reading.
Devise uses conditional callbacks to generate the confirmation token and send the email. The callbacks will be called only if confirmation_required? returns true. Redefine it on your model:
def confirmation_required?
false
end
However, this will make the active_for_authentication? method always return true because it takes whether or not confirmation is required into account. We have to redefine that as well:
def active_for_authentication?
confirmed? || confirmation_period_valid?
end
This way, the account will stay inactive and no confirmation email will be sent. You will have to manually activate the user by calling confirm! on the record or just setting confirmed_at to any date.
It's quite a hack, but it should work.
For reference: confirmable.rb
I just want to add for future reference that since Devise 2.2 there is now a skip_confirmation_notification! method available as well which basically does everything from Matheus' post without redefining the methods in the model.
I would like to set a boolean flag upon user confirmation via Devise. Essentially I want to set the user as 'active'. However, Devise simply confirms the account and logs them in.
How can I create a callback of some sorts to update my user record to set that 'active' column to true upon confirmation?
Any help very appreciated!
Presuming that your authentication model is called User, you can do this:
class User < ActiveRecord::Base
def active?
super and (not self.confirmed_at.nil?)
end
end
With this, Devise will not login the user but will wait until the user confirms (the confirmed_at field will be non-NULL if a user has confirmed)
For your particular question, you're better off implementing your active? attribute as confirmed_at being nil, as suggested by Zabba.
But here is how to do what you're asking, since it may be helpful to people trying to set other values on the user after confirmation.
class Users::ConfirmationsController < Devise::ConfirmationsController
def show
# let Devise actually confirm the user
super
# if they're confirmed, it will also log them in
if current_user then
# and you can do whatever you want with their record
current_user.active = true
end
end
end
This is basically a comment on Turadg's Answer below. If you follow that suggestion (which I did) you will have a small problem when users attempt to use an invalid confirmation_token. You will get a "Missing template users/confirmations/new". What the Devise::ConfirmationsController is doing here is sending you to devise/confirmations/new to inform you the token is invalid and allow you to send another one.
Since I had already customized the Devise views, what I ended up doing to get around this minor issue is moving the devise/confirmations/new.html.haml file into the now expected location under user/confirmations/new.html.haml.