📜 ⬆️ ⬇️

Authentication in rails applications using facebook, vkontakte

Authentication in rails applications via facebook, vkontakte



This article will explain how to do the simplest authentication in the rails application through social networks vkontakte and facebook, help with this omniauth, omniauth-facebook, omniauth-vkontakte. The material is designed for a beginner. Although this will be an educational application, we use bootstrap using the twitter-bootstrap-rails gem to make it more complete.


Frame


Create a new application (I will omit the bundle exec before the commands):
rails new authproviders 

We write the necessary gems in the Gemfile
 source 'https://rubygems.org' gem 'rails', '3.2.3' gem 'sqlite3', :group => :development gem 'pg', :group => :production group :assets do gem 'sass-rails', '~> 3.2.3' gem 'coffee-rails', '~> 3.2.1' gem 'uglifier', '>= 1.0.3' end gem 'jquery-rails' gem 'haml-rails' gem 'twitter-bootstrap-rails' gem 'devise' gem 'omniauth' gem 'omniauth-facebook' gem 'omniauth-vkontakte' 

Perform installation:
 bundle install --without production 

Let's make the application a bit prettier, let's use bootstrap:
 rails g bootstrap:layout application fixed 

Do not forget to delete index.html from the public directory,
if the application.html.erb file remains, then delete it too.
Create a user model, where url is the address of the profile page:
 rails g scaffold User username:string nickname:string provider:string url:string 

Configure devise:
 rails generate devise:install rails generate devise User rake db:migrate 

Add the omniauthable module to the User model:
 class User < ActiveRecord::Base # Include default devise modules. Others available are: # :token_authenticatable, :encryptable, :confirmable, :lockable, :timeoutable and :omniauthable devise :database_authenticatable, :registerable, :recoverable, :rememberable, :trackable, :validatable, :omniauthable # Setup accessible (or protected) attributes for your model attr_accessible :email, :password, :password_confirmation, :remember_me attr_accessible :nickname, :provider, :url, :username end 


Create a callback controller with actions for each provider
 rails g controller Users::OmniauthCallbacks facebook vkontakte 

We inherit it from Devise :: OmniauthCallbacksController
 class Users::OmniauthCallbacksControllerController < Devise::OmniauthCallbacksController def facebook end def vkontakte end end 

We prescribe routing in routes.rb:
 Authproviders::Application.routes.draw do devise_for :users, :controllers => { :omniauth_callbacks => "users/omniauth_callbacks" } resources :users, :only => [:index, :destroy] root :to => 'users#index' end 

')
Add to the application.html.haml template links to the social network (user_omniauth_authorize_path (: facebook) and user_omniauth_authorize_path (: vkontakte)), the full template code:
 !!! 5 %html(lang="en") %head %meta(charset="utf-8") %meta(name="viewport" content="width=device-width, initial-scale=1.0") %title= content_for?(:title) ? yield(:title) : "Authproviders" = csrf_meta_tags / Le HTML5 shim, for IE6-8 support of HTML elements /[if lt IE 9] = javascript_include_tag "http://html5shim.googlecode.com/svn/trunk/html5.js" = stylesheet_link_tag "application", :media => "all" %link(href="images/favicon.ico" rel="shortcut icon") %link(href="images/apple-touch-icon.png" rel="apple-touch-icon") %link(href="images/apple-touch-icon-72x72.png" rel="apple-touch-icon" sizes="72x72") %link(href="images/apple-touch-icon-114x114.png" rel="apple-touch-icon" sizes="114x114") %body .navbar.navbar-fixed-top .navbar-inner .container %a.btn.btn-navbar(data-target=".nav-collapse" data-toggle="collapse") %span.icon-bar %span.icon-bar %span.icon-bar %a.brand(href="#") Authproviders .container.nav-collapse %ul.nav - if user_signed_in? %li= link_to "#{current_user.username} (#{current_user.provider})", current_user.url %li= link_to "Sign out", destroy_user_session_path, :method => :delete .container .content .row .span9 = yield .span3 .well.sidebar-nav %h3 Providers %ul.nav.nav-list - if !user_signed_in? %li= link_to "Sign in with Facebook", user_omniauth_authorize_path(:facebook) %li= link_to "Sign in with Vkontakte", user_omniauth_authorize_path(:vkontakte) %footer %p © Company 2012 = javascript_include_tag "application" 

Let's fix the users / index.html.haml template for displaying registered users.
 - model_class = User.new.class %h1=t '.title', :default => model_class.model_name.human.pluralize %table.table.table-striped %thead %tr %th= model_class.human_attribute_name(:username) %th= model_class.human_attribute_name(:nickname) %th= model_class.human_attribute_name(:provider) %th= model_class.human_attribute_name(:sign_in_count) %th= model_class.human_attribute_name(:created_at) %th=t '.actions', :default => t("helpers.actions") %tbody - @users.each do |user| %tr %td= link_to user.username, user.url %td= user.nickname %td= user.provider %td= user.sign_in_count %td= user.created_at %td = link_to t('.destroy', :default => t("helpers.links.destroy")), user_path(user), :method => :delete, :confirm => t('.confirm', :default => t("helpers.links.confirm", :default => 'Are you sure?')), :class => 'btn btn-mini btn-danger' 

Make sure everything works and get down to the most interesting:
 rails s 


Facebook


Go to the page https://developers.facebook.com/apps and create a new application.

(for debugging on localhost, you can write localhost : 3000 in the site url)
In the initialization file devise.rb we add the line:
 config.omniauth :facebook, 'APP_ID', 'APP_SECRET' 

'APP_ID', 'APP_SECRET' change to the values ​​given when creating a new application.

In the User model, we will add the facebook method, which will search for the user at the address of his page, if there is none, then create a new one (not the best solution from a security point of view, but this is just an example):
 def self.find_for_facebook_oauth access_token if user = User.where(:url => access_token.info.urls.Facebook).first user else User.create!(:provider => access_token.provider, :url => access_token.info.urls.Facebook, :username => access_token.extra.raw_info.name, :nickname => access_token.extra.raw_info.username, :email => access_token.extra.raw_info.email, :password => Devise.friendly_token[0,20]) end end 


Add the facebook method to the Users :: OmniauthCallbacksController controller:
 def facebook @user = User.find_for_facebook_oauth request.env["omniauth.auth"] if @user.persisted? flash[:notice] = I18n.t "devise.omniauth_callbacks.success", :kind => "Facebook" sign_in_and_redirect @user, :event => :authentication else flash[:notice] = "authentication error" redirect_to root_path end 


Vkontakte


For contact, the procedure is similar: http://vk.com/developers.php
create an application:

In the initialization file devise.rb, do not forget to append a line:
 config.omniauth :vkontakte, 'APP_ID', 'APP_SECRET' 

It was more difficult to test work with a contact on a local host; you can use such a thing: https://github.com/progrium/localtunnel
After installing through the bundle, when you first start, load the key:
 localtunnel -k ~/.ssh/id_rsa.pub 3000 

Then we start the tunnel, it will give us the address at which the application will be available (we will hammer it into contact), followed by the rails
 localtunnel 3000 This localtunnel service is brought to you by Twilio. Port 3000 is now publicly accessible from http://4v9p.localtunnel.com ... rails s 

We add using the vkontakte method to the model and controller, similar to facebook (I note that according to the information security policy of the contact, email addresses are not given, so in order to not conflict with validation, I create surrogate addresses like this when creating a user: domain + @ vk.com) :
 class User < ActiveRecord::Base devise :database_authenticatable, :registerable, :recoverable, :rememberable, :trackable, :validatable, :omniauthable attr_accessible :email, :password, :password_confirmation, :remember_me attr_accessible :nickname, :provider, :url, :username def self.find_for_facebook_oauth access_token if user = User.where(:url => access_token.info.urls.Facebook).first user else User.create!(:provider => access_token.provider, :url => access_token.info.urls.Facebook, :username => access_token.extra.raw_info.name, :nickname => access_token.extra.raw_info.username, :email => access_token.extra.raw_info.email, :password => Devise.friendly_token[0,20]) end end def self.find_for_vkontakte_oauth access_token if user = User.where(:url => access_token.info.urls.Vkontakte).first user else User.create!(:provider => access_token.provider, :url => access_token.info.urls.Vkontakte, :username => access_token.info.name, :nickname => access_token.extra.raw_info.domain, :email => access_token.extra.raw_info.domain+'@vk.com', :password => Devise.friendly_token[0,20]) end end end 

Controller Code:
 class Users::OmniauthCallbacksController < ApplicationController def facebook @user = User.find_for_facebook_oauth request.env["omniauth.auth"] if @user.persisted? flash[:notice] = I18n.t "devise.omniauth_callbacks.success", :kind => "Facebook" sign_in_and_redirect @user, :event => :authentication else flash[:notice] = "authentication error" redirect_to root_path end end def vkontakte @user = User.find_for_vkontakte_oauth request.env["omniauth.auth"] if @user.persisted? flash[:notice] = I18n.t "devise.omniauth_callbacks.success", :kind => "Vkontakte" sign_in_and_redirect @user, :event => :authentication else flash[:notice] = "authentication error" redirect_to root_path end end end 

Of course, using DRY, you can and should summarize this code.
Application itself:




Conclusion


As we have seen, creating an application on rails with facebook and contact authentication is very simple.
A working demo is here: http://authproviders.herokuapp.com/
Example code: https://github.com/mystdeim/Authproviders
I will give some useful links:

PS This is my first article, first attempt at writing.

UP: Kei's secret and application IDs are best kept apart so they don't accidentally get into the repository http://habrahabr.ru/post/142128/#comment_4757653

Source: https://habr.com/ru/post/142128/


All Articles