parm530
12/5/2017 - 8:52 PM

Devise

How to devise (for authentication)

Devise (Authentication)

  • Useful for authentication needs (allowing for signing in, sending password recovery ...)
  • Rails engine (rails app that belongs within another rails app)
    • contains its own set of views and controllers (but no models)

Installation and Use

  • Add to gemfile: gem 'devise
  • Run bundle install
  • Run the installer: rails generate devise:install
    • This creates a massive file in config/initializers/devise.rb
  • Generate the User model with: rails g devise User
  • Run rake routes
  • Run rake db:migrate
  • rails g controller Home
    • create home#index with views
  • Head into config/routes.rb, add root 'home#index'
  • Test out the authorization, place before_action :authenticate_user! above index action
    • Upon refresh, should take you to the login page
    • Preferrable to add this filter in ApplicationController to keep code dry instead of adding to every controller
Implementing a Sign Out Option
<%= link_to("Sign Out", destroy_user_session_path, :method => 'delete') %>

Adding the user model migration Rails (5.x.x)

  • rails g migration AddUserRefToTableName user:references (where TableName refers to the table that belongs_to a user)
  • Add the following:
def change
  add_reference :posts, :user, foreign_key: true, index: true
  # add_foreign_key :posts, :users
end
  • May not need the add_foreign_key part, error when migrating
  • The example uses the users table
    • The add_foreign_key is used to ensure referential integrity
    • It adds a foreign_key to the user_id column of the posts table
    • The key then references the id column of the users table

Helpers:

  • before_action :authenticate_user!: authenticates the user to be able to access certain pages
    • If your model is not User, say Person, just replace _user with _person , generically _modelname
  • user_signed_in: verifies if a user is signed in
  • current_user: to find the currently signed in user

Overriding Default Path for signing in and signing out

  • Devise looks for a root_path to redirect to after signing in and signing out.
    • If using a User model, Devise will look for the path: user_root_path if it exists, otherwise, the default root_path will be used
  • You can override this redirection by using: after_sign_in_path and after_sign_out_path

Devise Password

  • Devise's users table contains a field for encrypted_password
  • Inside of database_authenticatable, password is also used as an attr_reader and setter method:
# Generates a hashed password based on the given value.
# For legacy reasons, we use `encrypted_password` to store
# the hashed password.
def password=(new_password)
  @password = new_password
  self.encrypted_password = password_digest(@password) if @password.present?
end
  • You can refer to password in you DB seeding and encrypted_password will also be set!

Using Omniauth

  • Add this line to user.rb:
devise :omniauthable, :omniauth_providers => [:facebook]
  • You'll need to set the callback url in facebook to your website/users/auth/facebook/callback
  • You can add this line to the welcome view:
<%= link_to "Sign in with Facebook", user_facebook_omniauth_authorize_path %>
  • Once this link is clicked, you'll need to add the following code to fix the error:
devise_for :users, :controllers => { :omniauth_callbacks => 'users/omniauth_callbacks' }
  • Create a file in app/controllers/users/omniauth_callbacks_controller.rb
class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController
  def facebook
    @user = User.from_omniauth(request.env["omniauth.auth"])
    sign_in_and_redirect @user  
  end
end

# in the User model:
class User < ActiveRecord::Base
  def self.from_omniauth(auth)
    where(provider: auth.provider, uid: auth.uid).first_or_create do |user|
      user.email = auth.info.email
      user.password = Devise.friendly_token[0,20]
    end
  end
end
  • the sign_in_and_redirect method, unfortunately doesn't tell devise where to redirect:
# in ApplicationController
def after_sign_in_path_for(resource)
  request.env['omniauth.origin'] || root_path
end

Editing Devise Views

  • rails g devise:views
    • Installs views/devise to better customize forms