Rails & Devise: Firing an event on a new session - ruby-on-rails

I want to track user sessions in my Rails app. In my Devise Sessions controller, I can track when a session is created... But that only tracks when a user specifically submits the login form. And I'm wanting to track the number of times a user returns to the app (most of the time, they won't need to resubmit the login form).
Where can I put my tracking code to achieve this?
Here's my sessions controller:
class SessionsController < Devise::SessionsController
def create
super do
mixpanel.track("Sign In")
end
end
def destroy
super do
mixpanel.track("Sign Out")
end
end
def new
super
end
end
To clarify:
user#gmail.com visits myapp.com and logs in to the webapp using password/email login (hits sessions#create)
The next day the user returns to myapp.com and does not need to manually login because the app has kept them logged in
The above should register as 2 "sessions" for this user so I am trying to figure out where I can fire the events.

I am not sure that I understand very well what you want but I will try.
In config/routes.rb you have different routes. You should find root to: 'home#index' or something like that. In this case, you can add the tracking code in HomeController#index. When the user loads the web app, it will open this endpoint, so you can track the event there.

In application_controller.rb, make a custom before_action, like the following:
class ApplicationController < ActionController::Base
before_action :check_visit
private
def check_visit
if current_user.nil?
# You've got a visit that wasn't logged in
else
# You've got a visit that was logged in
end
end
end
We can't just put it in home#index or whatever your root is, since the might have a different page bookmarked (for example).

Related

Using devise for user registration/login redirect user to a different form page based on their response

I am using devise for user registration/login, after the user has successfully signed up, I want to show a page/a dialog box and redirect to another page based on user response. How can I do that?
User Model (By devise)
username
password
Student Model
name
student_id
Teacher Model
name
grade
First_page:
signup signin links
Signup link will show the devise views/devise/registrations/new.html.erb page.
After successful signup, it takes the user to root page. I have defined the root page in routes.rb:
` Rails.application.routes.draw do
devise_for :users
resources :students, :teachers
# For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html
root to: "students#index"
end `
At this point, the application doesn't have any idea who the user is.
So, I want to get the identity information(student/teacher) from the user.
How will I get this information?
Student/Teacher controller:
`class StudentsController < ApplicationController
before_action :authenticate_user!, only: [:new, :create]
def index
#students = Student.all
end
def new
#student = Student.new
end
def create
current_user.create_student(student_params)
redirect_to root_path
end
private
def student_params
params.require(:student).permit(:name, :skypid)
end
end`
After the user has successfully signed in, I want to ask if the user is a student or teacher. Based on what they select, redirect them to a student form page or teacher form page.
How can I do that in rails?
Thank you
You can write a custom after_sign_in_path_for function in your ApplicationController assuming you're using all the default Devise controllers otherwise. Any named path helper or other route that it returns will be where the user is redirected, so you could do something simple like always redirect to a selection page that presents the options and handles the choice on a subsequent action:
def after_sign_in_path_for(resource)
user_type_selection_path # whatever route in your app manages the selection
end
Alternately, you could invoke a custom method on the user model in that function to make a choice right there:
def after_sign_in_path_for(resource)
resource.student? ? student_path : teacher_path
end
You could hybridize these of course as well to do the latter when the selection has already been made and redirect otherwise, with something similar to the following:
def after_sign_in_path_for(resource)
if resource.user_type_chosen?
resource.student? ? student_path : teacher_path
else
user_type_selection_path
end
Bear in mind that none of those functions or paths are real, since I can't be more specific on the information you've provided, but hopefully this gets you headed in the right direction. The after_sign_in_path_for hook is your primary tool here, unless you get into the world of overriding the default devise controllers and interrupting the usual workflow there to accommodate this step, which doesn't seem strictly necessary by your description.

How do I generate a temporary page like confirmation page in rails?

I am using devise and want to redirect users to a confirmation page upon signup, this is what I am doing right now:
users/registrations_controller.html.erb
class Users::RegistrationsController < Devise::RegistrationsController
def confirm_email
end
private
def after_inactive_sign_up_path_for(resource)
users_confirmyouremail_path
end
end
config/routes.rb
devise_scope :user do
get 'users/confirmyouremail' => 'users/registrations#confirm_email'
end
I have no problem with redirecting the page after signup. However, I think it is quite weird that anyone can visit the page with url like `host.com/confirmyouremail' and see the confirmation page. Are there any ways I can write a route that will use random code that is allow only for one time visit? Thanks in advance.
Maybe something like this:
before_action :authenticate_user!
def confirm_mail
redirect_to root_path if current_user.confirmed
...
end
You are storing in the database if the user has already confirmed his account. If his account is confirmed then he won't be able to access this page. You can redirect to whatever page you want. A user without any account won't be able to access this page because of the before action
In case the user is not logged in when he accesses this confirm_mail page you have different possibilities. You could use a session or a cookie:
# after sign up:
session[:confirm] = true
# alternatively a cookie
cookies[:confirm] = true
Then in the confirm mail action:
def confirm_mail
if session[:confirm].blank? # or cookies[:confirm].blank?
redirect_to root_path
end
# otherwise delete the field from the session
session.delete(:confirm)
# alternatively the cookie
cookies.delete(:confirm)
end
Another way would be by using a Token. You create a new model like ConfirmMailToken. Then on sign up you create a new token and redirect the user to the confirm page with the token as a URL param. Then in the confirm_mail action you check if a token is available and delete it if it is. This way you ensure that the page is only shown after redirect.

How to check if a session has timed out and redirect the user back to the login screen in rails

I have configured authentication in Rails 3.2.17 without Devise. I want to redirect the user back to the login screen after the session has ended which is 30 minutes and let them know that the session has ended and they must login again.
I attempted to add an attribute :login_expiry onto the user and set the time 10 minutes after the session end time, in my session controller after I have found the user:
user.login_expiry = 40.minutes.from_now
user.save
where login_expiry is a datetime attribute.
If the user session has ended and it is within 10 minutes after the session ended then the user should be presented with the login screen and a flash "your session has ended. Please log in again."
However what I have found is that the login_expiry is not being updated on the user object. The database is rolling back.
I would really like anyones help on this!
Thanks in advance!
at user's successful login you can do following:
session[:user_id] = user.id
session[:expires_at] = Time.current + 30.minutes
Then, every time authenticated user do something do something, do something like following:
if session[:expires_at] < Time.current
# sign out and redirect to login page
end
Usually we check whether user is logged in or not at every page, in that case we can put them in ApplicationController and use before_action hook of rails controller.
class ApplicationController < ActionController::Base
before_action :check_login_session
private
def check_login_session
if session[:expires_at] < Time.current
# sign out and redirect to login page
end
end
end
As ApplicationController is usually the parent of every controller, so before every controller call it will check_login_session.

Ruby on Rails app planning - login system routing when not logged in

I'm building a rails app, and so far I've set up a pretty basic user registration/login system, mainly by following this railcast I found thanks to stack overflow. I've left everything the same as the railcast, only used strong parameters instead of attr_accessible and added some additional fields (username, bio, img url) to the table.
Now I want my app to redirect users onto my login page if they're not logged in, no matter what page they try to access, and if they are, then redirect to the normal path. My loging page is currently my root_path. Do I need to do this in all the controllers separately or can I just write this into my appController? How would I go about writing the controller? I was thinking something like this:
if session[:user_id] == nil
redirect_to login_path
else
redirect_to current_controller_path
end
Now how do I check if user is logged in, and how do I redirect to current controller path (for instance articles_index_path?
I am new to ruby on rails, and still trying to wrap my head around models, views and controllers, so please assume I know nothing when writing up explanations :) Thanks for the help
Oh I'm using Rails 4 with ruby 2.2.1
You need to add a before_filter in your ApplicationController to check user's authentication.
class ApplicationController < ActionController::Base
...
before_filter :authenticate_user!
...
private
def authenticate_user!
redirect_to login_path unless session[:user_id]
end
end
Now it will make sure that user should be logged in for accessing any action of any controller, including signup, signin and other actions which should be accessible to non-logged in users too.
You need to make sure that you skip above before_filter where you don't want user to be logged in such as signup, signin, about us, contact us etc actions like this.
For Example:
class SessionsController < ApplicationController
skip_before_filter :authenticate_user!, :except => :destroy
...
def new
...
end
def create
...
end
...
end
You can read more about skip_before_filter on APIDock

Devise - Redirect automatically from root url if signed in

A user named Carly is not signed up and arrives at the index page. She signs up and gets automatically redirected to the main dashboard page. The user Carly which is now named CarlyCoolness123 closes her browser and goes to eat dinner. She gets on her PC again, but this time since she only remembers the actual index page called: coolness.com, and not coolness.com/index-dashboard. I want this user to be automatically redirect to the dashboard page if the user is signed in. That means that the user can't access the index page if the user is signed in. How do I do this? I've tried a couple of things, but since I assume that you people here have a lot better understanding with this than me I assume that my mistakes don't need to be included here.
This assumes that you have setup Devise correctly and you have a Dashboard controller that is responsible for rendering the dashboard view. In your app/controllers/dashboard_controller.rb do this:
class DashboardController < ApplicationController
before_filter :authenticate_user!
...
end
Then in your config/routes.rb file add the following:
resources :dashboard
authenticated :user do
root :to => "dashboard#index"
end
root :to => redirect("/users/sign_in")
If you have an index view for the dashboard, accessing the root of your app should automatically render it (if the user is signed in). If the user is not signed in, it will redirect to devise default sign_in view (if you haven't redefined it)
I prefer to use the below method, as it seems counterintuitive to me to have multiple root paths [read: "I was getting an error when I tried the other mentioned ways, so I tried something else."].
I think this works better and is actually what #MFCS was originally asking, since it doesn't make the root point somewhere else conditionally. Instead redirects to a different path when the signed in user visits the root_path:
config/routes.rb:
root to: 'welcome#index'
app/controllers/welcome_controller.rb:
class WelcomeController < ApplicationController
def index
if user_signed_in?
redirect_to dashboard_path
end
end
end
I prefer this, since the dashboard will still have the dashboard url show up in the browser (instead of the root url), although this may not be the preference of others, and also depends on your preferred user experience.
EDIT: I made a mistake in the code, referencing the DashboardController instead of the controller of the root resource.
While this is an old post, I kinda ran into a similar problem and since I didn't use devise and use bcrypt instead, then this was the solution. I defined a current_user in my application controller and also authorize.
def current_user
#current_user ||= User.find(session[:user_id]) if session[:user_id]
end
helper_method :current_user
def authorize
redirect_to 'home/index' unless current_user
end
I then route all people to their secure dashboard and run before filter on all secure controllers like so before_filter :authorize, then if they fail, they are routed to the home page. Hope this helps someone in the future!

Resources