2016年5月31日火曜日

To implement the Facebook login (OAuth) in Rails

Overview

To implement the Facebook login (OAuth) in Rails. 

Operating environment

  • Ruby on Rails 4.2.0
  • Ruby 2.1.0p0  
  • Devise 3.4.1
  • OmniAuth 1.2.2
  • OmniAuth-facebook 2.0.0
  • OmniAuth-twitter 1.1.0

Advance preparation

To get the Facebook API key.
Once created, select the "Add Platform" → "Website" than the set.
To enter the URL to the site URL.
Gemfile
  gem 'omniauth-facebook'

config / initializers / omniauth.rb
  Rails. Application. Config. Middleware. Use OmniAuth :: Builder do
   provider: facebook, ENV [ 'FACEBOOK_KEY '], ENV [ 'FACEBOOK_SECRET']
 end

Since FACEBOOK_KEY and FACEBOOK_SECRET the environment variables, in the case of Toka CentOS to edit something the ~ / .bash_profile.

Configuring Routing

Page failure, when the login fails for some reason, to be displayed.
Link on the login button /auth/facebook is.
  get '/ auth /: provider / callback', to: 'users # create', as:: auth_callback
 get '/ auth / failure', to: 'users # auth_failure', as:: auth_failure

controller

Use the information that comes back from Facebook, or create a user, you or to log.
Every time of login, because I want to update the icon and name, and @ user.save.
It does not describe sign_in method, but only by substituting the user of the relevant to current_user.

from_omniauth methods and context: :facebook_login For by model.
Because it contains a variety information in env [ 'omniauth.auth'], it passes as an argument.
controllers / users_controller.rb
  def create

         if env [ 'omniauth.auth']. present?
             # Facebook login
             @user = User. from_omniauth (env [ 'omniauth.auth'])
             . result = @user save (context: : facebook_login)
             fb = "Facebook"
         else
             # Usually sign up
             @user = User. new (strong_params)
             result = @user. save
             fb = ""
         end
         if result
             sign_in @user
             flash [: success] = "# {fb} has been logged." 
             redirect_to @user
         else
             if fb. present?
                 redirect_to auth_failure_path
             else
                 render 'new'
             end
         end
     end

model

If it finds the same e-mail address, log in as that user.
Otherwise, you create a new user.
In the Facebook login, so you do not need a password, you have to disable the password validation.
on: :facebook_login Keep in addition to, when you save the controller@user.save(context: :facebook_login) and to have.
models / user.rb
  validates: password, presence: false, on:: facebook_login

     def self. from_omniauth (auth)
         # Provision of email is mandatory
         user = User. where ( 'email =?', auth. info. email). first
       if user. blank?
         user = User. new
       end
     user. uid = auth. uid
     user. name = auth. info. name
     user. email = auth. info. email
     user. icon = auth. info. image
     user. oauth_token = auth. credentials. token
     user. oauth_expires_at = Time. at ( auth. credentials. expires_at)
     user
     end

/ If you hit the auth / facebook, it is displayed dialog of Facebook.

1. implementation of the email address authentication

Editing of Gemfile

Gemfile
  gem 'devise'

Installation

  $ Bundle install

Generates each file of Devise

  $ Rails g devise: install
 create config / initializers / devise.rb 
  create config / locales / devise.en.yml

================================================== =============================


Some setup you must do manually if you have not yet:


. 1. Ensure you have defined default url options in your environments files Here is an example of default_url_options appropriate for a development environment in config / environments / development.rb:

config.action_mailer.default_url_options = {host: 'localhost', port: 3000}

In production,: host should be set to the actual host of your application.


2. Ensure you have defined root_url to * something * in your config / routes.rb.

For example:

root to: "home # index"

3. Ensure you have flash messages in app / views / layouts / application.html.erb.

For example:

      
<% = Notice%>

      
<% = Alert%>

4. If you are deploying on Heroku with Rails 3.2 only, you may want to set:


config.assets.initialize_on_precompile = false


On config / application.rb forcing your application to not access the DB

or load models when precompiling your assets.

5. You can copy Devise views (for customization) to your app by running:

rails g devise: views

================================================== =============================

Japanese the Devise

config/locales/devise.en.yml the devise.ja.yml to rename to.
To copy and paste the following contents.
Of course Rails app itself it is necessary to Japanese localization.
  • devise-i18n / ja.yml at master · tigrish / devise-i18n
config / locales / devise.ja.yml
  ja:
   devise:
     confirmations:
       confirmed: I registered the account.
       send_instructions: will contact you by e-mail how to register within a few minutes.
       send_paranoid_instructions: If the e-mail address is registered, you will receive an e-mail how to verify your account within a few minutes have been described.

           :

Generate a User model

By using the generator Devise, it can be generated in advance model file module is defined.
  $ Rails g devise User

Edit the module to use

In the User model, devise can define whether to use any module by the method.
In order to enable the Fasebook authentication devise the method:omniauthable also want to add.
app / models / user.rb
  # Include default devise modules Others available are .:
 #: Confirmable,: lockable,: timeoutable and: omniauthable
 devise: database_authenticatable,: registerable,
        : recoverable,: rememberable,: trackable ,: validatable, 
          : omniauthable, omniauth_providers: [: facebook ] 


See the README for the available modules.

Execution of migration

rails g devise User when the db/migrate/xxx_devise_create_users.rbhas also been generated migration file named.
This will be applied to the DB.
  $ Rake db: migrate
  == 20150207093749 DeviseCreateUsers: migrating ================================ - create_table (: users)
 -> 0.0800s
 - Add_index (: users,: email, {: unique => true})
 -> 0.0659s
 - Add_index (: users,: reset_password_token, {: unique => true})
 -> 0.0528s
 == 20150207093749 DeviseCreateUsers: migrated (0.1993s) =======================


Display of the sign-up form

Or more, http://〜/users/sign_up access to when the form is displayed.
It is ready for user registration at this time.

2. authentication with OAuth

Editing of Gemfile

Gemfile
  gem 'omniauth'
 gem 'omniauth-facebook'
 gem 'omniauth-twitter'

Installation

  $ Bundle install

Creating a 8.Authentication model

  But may be added a column to the originally a User model, we want to be able to log in to the same account in the accounts of a number of services, User model to create the Authentication model that has_many.
  $ Rails g model authentication user_id: integer provider: string uid: string
invoke active_record

create db / migrate / 20150208043543_create_authentications.rb

create app / models / authentication.rb

invoke test_unit

create test / models / authentication_test.rb

create test / fixtures / authentications.yml


The User model as a "has_many", and "belongs_to" in the User model the Authentication model.
  app / models / user.rb

  class User 
   has_many: authentications, dependent:: destroy
 end

  app / models / authentication.rb

  class Authentication  
    belongs_to: user 
  end 

Execution of migration

  $ Rake db: migrate
== 20150207101521 AddColumnsToUsers: migrating ================================

- Add_column (: users,: uid ,: string)

-> 0.1305s

- Add_column (: users,: provider ,: string)

-> 0.1113s

== 20150207101521 AddColumnsToUsers: migrated (0.2424s)=======================


Acquisition of API key

Facebook

To create a more applications below.
Once created, select the "Add Platform" → "Website" than the set.
To enter the URL to the site URL (for example: http://192.168.33.10:3000/ ).

Twitter

To create a more applications below.
Once created, make the following settings from the "Settings".
  1. Callback URL
    • Example: http://〜/users/auth/twitter
  2. The following is checked:
    • Allow this application to be used to Sign in with Twitter
reference

Setting of Devise

Setting each key of the provider that acquired above.
Each API key for security is set to environment variable.
If you divide the development / staging / production environmentRails.env.production? Distributed by using a.
config / initializers / devise.rb
  Devise. Setup    do    | Config | 
    # ... 

    config. omniauth    : facebook, ENV [ 'FACEBOOK_ID' ], ENV [ 'FACEBOOK_SECRET'] 
    config. omniauth    : twitter, ENV [ 'TWITTER_KEY' ], ENV [ 'TWITTER_SECRET'] 
  end

Implement the find method in the User model

uid and provider combination of is unique, thereby obtaining the user.
If that does not exist in the record to be created.
app / models / user.rb
  class User 
   # ...

   def self. find_for_oauth (auth)
     . user = User where. (uid :. auth uid, provider:. auth provider) first

     unless user
       user = User. create (
         uid:. auth uid,
         provider:. auth provider,
         email:. User dummy_email (auth) ,
         password:. Devise friendly_token [0, 20]
       )
     end

     user
   end

   private

   def self. dummy_email (auth)
     "# {Auth uid.} - # {. Auth provider} @ example.com"
   end
 end
If the authentication of the e-mail address has been implemented, it is necessary that the time of authentication with OAuth also to save the e-mail address.
Here, uid and the provider using a combination of that unique,self.dummy_email are generated as.

reference

Implement a callback processing to User controller

app/controllers/users ディレクトリを作成し、omniauth_callbacks_controller.rb を作ります。
provider there is a need to define a method with the same name as the.
However, because the call-back process of basically each provider are common,callback_from are unified in method.
app / controllers / users / omniauth_callbacks_controller.rb
  class Users :: OmniauthCallbacksController 
   def facebook
     callback_from: facebook
   end

   def twitter
     callback_from: twitter
   end

   private

   def callback_from (provider)
     provider = provider. to_s

     @user = User. find_for_oauth (request. env [ 'omniauth.auth'])

     if @user. persisted?
       flash. [: notice] = I18n t ( 'devise.omniauth_callbacks.success', kind:. provider capitalize)
       sign_in_and_redirect @user, event:: authentication
     else
       session [ "devise. # {provider } _data"] = request. env [ 'omniauth.auth']
       redirect_to new_user_registration_url
     end
   end
 end

Routing processing

As follows, to set the routing for OAuth callback.
config / routes.rb
  Rails. Application. Routes. Draw    do 
    devise_for    : users,    controllers:    { 
  omniauth_callbacks:    'users / omniauth_callbacks' 
  } 

    # ... 
  end 

Add the authentication link

Link by the following is generated.
Describing it in any position of view.
  user_omniauth_authorize_path (: facebook)
 user_omniauth_authorize_path (: twitter)

Summary