gem 'omniauth', '0.1.6'
Rails.application.config.middleware.use OmniAuth::Builder do provider :facebook, 'APP_ID', 'APP_SECRET' end
rails generate model service user_id:integer provider:string uid:string uname:string uemail:string rails generate controller services
class User < ActiveRecord::Base devise :database_authenticatable, :oauthable, :registerable, :recoverable, :rememberable, :trackable, :validatable, :confirmable, :lockable has_many :services, :dependent => :destroy has_many :articles, :dependent => :destroy has_many :comments, :dependent => :destroy has_many :ratings, :dependent => :destroy belongs_to :country attr_accessible :email, :password, :password_confirmation, :remember_me, :fullname, :shortbio, :weburl validates :weburl, :url => {:allow_blank => true}, :length => { :maximum => 50 } validates :fullname, :length => { :maximum => 40 } validates :shortbio, :length => { :maximum => 500 } end
class Service < ActiveRecord::Base belongs_to :user attr_accessible :provider, :uid, :uname, :uemail end
... match '/auth/facebook/callback' => 'services#create' resources :services, :only => [:index, :create] ...
class ServicesController < ApplicationController def index end def create render :text => request.env["omniauth.auth"].to_yaml end end
--- user_info: name: Markus Proske urls: Facebook: http://www.facebook.com/profile.php?id=.... Website: nickname: profile.php?id=.... last_name: Proske first_name: Markus uid: "..." credentials: token: ........... extra: user_hash: name: Markus Proske timezone: 1 gender: male id: "...." last_name: Proske updated_time: 2010-11-18T13:43:01+0000 verified: true locale: en_US link: http://www.facebook.com/profile.php?id=........ email: markus.proske@gmail.com first_name: Markus provider: facebook
... def create omniauth = request.env['omniauth.auth'] if omniauth omniauth['extra']['user_hash']['email'] ? email = omniauth['extra']['user_hash']['email'] : email = '' omniauth['extra']['user_hash']['name'] ? name = omniauth['extra']['user_hash']['name'] : name = '' omniauth['extra']['user_hash']['id'] ? uid = omniauth['extra']['user_hash']['id'] : uid = '' omniauth['provider'] ? provider = omniauth['provider'] : provider = '' render :text => uid.to_s + " - " + name + " - " + email + " - " + provider else render :text => 'Error: Omniauth is empty' end end ...
... match '/auth/:service/callback' => 'services#create' resources :services, :only => [:index, :create, :destroy] ...
# Do not forget to restart your server after changing this file Rails.application.config.middleware.use OmniAuth::Builder do provider :facebook, 'APP_ID', 'APP_SECRET' provider :twitter, 'CONSUMER_KEY', 'CONSUMER_SECRET' provider :github, 'CLIENT ID', 'SECRET' end
class ServicesController < ApplicationController before_filter :authenticate_user!, :except => [:create] def index # get all authentication services assigned to the current user @services = current_user.services.all end def destroy # remove an authentication service linked to the current user @service = current_user.services.find(params[:id]) @service.destroy redirect_to services_path end def create # get the service parameter from the Rails router params[:service] ? service_route = params[:service] : service_route = 'no service (invalid callback)' # get the full hash from omniauth omniauth = request.env['omniauth.auth'] # continue only if hash and parameter exist if omniauth and params[:service] # map the returned hashes to our variables first - the hashes differ for every service if service_route == 'facebook' omniauth['extra']['user_hash']['email'] ? email = omniauth['extra']['user_hash']['email'] : email = '' omniauth['extra']['user_hash']['name'] ? name = omniauth['extra']['user_hash']['name'] : name = '' omniauth['extra']['user_hash']['id'] ? uid = omniauth['extra']['user_hash']['id'] : uid = '' omniauth['provider'] ? provider = omniauth['provider'] : provider = '' elsif service_route == 'github' omniauth['user_info']['email'] ? email = omniauth['user_info']['email'] : email = '' omniauth['user_info']['name'] ? name = omniauth['user_info']['name'] : name = '' omniauth['extra']['user_hash']['id'] ? uid = omniauth['extra']['user_hash']['id'] : uid = '' omniauth['provider'] ? provider = omniauth['provider'] : provider = '' elsif service_route == 'twitter' email = '' # Twitter API never returns the email address omniauth['user_info']['name'] ? name = omniauth['user_info']['name'] : name = '' omniauth['uid'] ? uid = omniauth['uid'] : uid = '' omniauth['provider'] ? provider = omniauth['provider'] : provider = '' else # we have an unrecognized service, just output the hash that has been returned render :text => omniauth.to_yaml #render :text => uid.to_s + " - " + name + " - " + email + " - " + provider return end # continue only if provider and uid exist if uid != '' and provider != '' # nobody can sign in twice, nobody can sign up while being signed in (this saves a lot of trouble) if !user_signed_in? # check if user has already signed in using this service provider and continue with sign in process if yes auth = Service.find_by_provider_and_uid(provider, uid) if auth flash[:notice] = 'Signed in successfully via ' + provider.capitalize + '.' sign_in_and_redirect(:user, auth.user) else # check if this user is already registered with this email address; get out if no email has been provided if email != '' # search for a user with this email address existinguser = User.find_by_email(email) if existinguser # map this new login method via a service provider to an existing account if the email address is the same existinguser.services.create(:provider => provider, :uid => uid, :uname => name, :uemail => email) flash[:notice] = 'Sign in via ' + provider.capitalize + ' has been added to your account ' + existinguser.email + '. Signed in successfully!' sign_in_and_redirect(:user, existinguser) else # let's create a new user: register this user and add this authentication method for this user name = name[0, 39] if name.length > 39 # otherwise our user validation will hit us # new user, set email, a random password and take the name from the authentication service user = User.new :email => email, :password => SecureRandom.hex(10), :fullname => name # add this authentication service to our new user user.services.build(:provider => provider, :uid => uid, :uname => name, :uemail => email) # do not send confirmation email, we directly save and confirm the new record user.skip_confirmation! user.save! user.confirm! # flash and sign in flash[:myinfo] = 'Your account on CommunityGuides has been created via ' + provider.capitalize + '. In your profile you can change your personal information and add a local password.' sign_in_and_redirect(:user, user) end else flash[:error] = service_route.capitalize + ' can not be used to sign-up on CommunityGuides as no valid email address has been provided. Please use another authentication provider or use local sign-up. If you already have an account, please sign-in and add ' + service_route.capitalize + ' from your profile.' redirect_to new_user_session_path end end else # the user is currently signed in # check if this service is already linked to his/her account, if not, add it auth = Service.find_by_provider_and_uid(provider, uid) if !auth current_user.services.create(:provider => provider, :uid => uid, :uname => name, :uemail => email) flash[:notice] = 'Sign in via ' + provider.capitalize + ' has been added to your account.' redirect_to services_path else flash[:notice] = service_route.capitalize + ' is already linked to your account.' redirect_to services_path end end else flash[:error] = service_route.capitalize + ' returned invalid data for the user id.' redirect_to new_user_session_path end else flash[:error] = 'Error while authenticating via ' + service_route.capitalize + '.' redirect_to new_user_session_path end end
<section id="deviseauth"> <h2>Sign in</h2> <h3>Sign in with your CommunityGuides account -- OR -- use an authentication service</h3> <div id="local" class="box"> <%= form_for(resource, :as => resource_name, :url => session_path(resource_name)) do |f| %> <p><%= f.label :email %><br /> <%= f.text_field :email %></p> <p><%= f.label :password %><br /> <%= f.password_field :password %></p> <% if devise_mapping.rememberable? %> <p><%= f.check_box :remember_me %> <%= f.label :remember_me %></p> <% end %> <p><%= f.submit "Sign in" %></p> <% end %> </div> <div id="remote"> <div id="terms" class="box"> <%= link_to "Terms of Service", "#" %> </div> <div id="services" class="box"> <a href="/auth/facebook" class="services"><%= image_tag "facebook_64.png", :size => "64x64", :alt => "Facebook" %>Facebook</a> <a href="/auth/google" class="services"><%= image_tag "google_64.png", :size => "64x64", :alt => "Google" %>Google</a> <a href="/auth/github" class="services"><%= image_tag "github_64.png", :size => "64x64", :alt => "Github" %>Github</a> <a href="/auth/twitter" class="services"><%= image_tag "twitter_64.png", :size => "64x64", :alt => "Twitter" %>Twitter</a> </div> </div> <div id="devise_links"> <%= render :partial => "devise/shared/links" %> </div> </section>
<section id="deviseauth"> <h2>Sign up</h2> <h3>Sign up on CommunityGuides manually -- OR -- or use one of your existing accounts</h3> <div id="local2" class="box"> <%= form_for(resource, :as => resource_name, :url => registration_path(resource_name)) do |f| %> <%= devise_error_messages! %> <p><%= f.label :email %><br /> <%= f.text_field :email %></p> <p><%= f.label :password %><br /> <%= f.password_field :password %></p> <p><%= f.label :password_confirmation %><br /> <%= f.password_field :password_confirmation %></p> <p><%= recaptcha_tags %></p> <p><%= f.submit "Sign up" %></p> <% end %> </div> <div id="remote2"> <div id="terms" class="box"> <%= link_to "Terms of Service", "#" %> </div> <div id="services" class="box"> <a href="/auth/facebook" class="services2"><%= image_tag "facebook_64.png", :size => "64x64", :alt => "Facebook" %>Facebook</a> <a href="/auth/google" class="services2"><%= image_tag "google_64.png", :size => "64x64", :alt => "Google" %>Google</a> <a href="/auth/github" class="services2"><%= image_tag "github_64.png", :size => "64x64", :alt => "Github" %>Github*</a> <div id="footnote_signup">* You can use Github only if you set a public email address</div> </div> </div> <div id="devise_links"> <%= render :partial => "devise/shared/links" %> </div> </section>
<section id="deviseauth"> <h2>Authentication Services - Setting</h2> <div id="currservices"> <h3>The following <%= @services.count == 1 ? 'account is' : 'accounts are' %> connected with your local account at CommunityGuides:</h3> <% @services.each do |service| %> <div class="services_used"> <%= image_tag "#{service.provider}_64.png", :size => "64x64" %> <div class = "user"> <div class="line1">Name: <%= service.uname %> (ID: <%= service.uid %>)</div> <div class="line2">Email: <%= service.uemail != '' ? service.uemail : 'not set' %></div> <div class="line3"> <% @services.count == 1 ? @msg = 'Removing the last account linked might lock you out of your account if you do not know the email/password sign-in of your local account!' : @msg = '' %> <%= link_to "Remove this service", service, :confirm => 'Are you sure you want to remove this authentication service? ' + @msg, :method => :delete, :class => "remove" %> </div> </div> </div> <% end %> </div> <div id="availableservices"> <h3>You can connect more services to your account:</h3> <div id="services"> <a href="/auth/facebook" class="services"><%= image_tag "facebook_64.png", :size => "64x64", :alt => "Facebook" %>Facebook</a> <a href="/auth/google" class="services"><%= image_tag "google_64.png", :size => "64x64", :alt => "Google" %>Google</a> <a href="/auth/github" class="services"><%= image_tag "github_64.png", :size => "64x64", :alt => "Github" %>Github</a> <a href="/auth/twitter" class="services"><%= image_tag "twitter_64.png", :size => "64x64", :alt => "Twitter" %>Twitter</a> </div> <h4>If you signed-up for CommunityGuides via an authentication service a random password has been set for the local password. You can request a new password using the "Forgot your Password?" link on the sign-in page.</h4> </div> </section>
... def update # no mass assignment for country_id, we do it manually # check for existence of the country in case a malicious user manipulates the params (fails silently) if params[resource_name][:country_id] resource.country_id = params[resource_name][:country_id] if Country.find_by_id(params[resource_name][:country_id]) end if current_user.haslocalpw super else # this account has been created with a random pw / the user is signed in via an omniauth service # if the user does not want to set a password we remove the params to prevent a validation error if params[resource_name][:password].blank? params[resource_name].delete(:password) params[resource_name].delete(:password_confirmation) if params[resource_name][:password_confirmation].blank? else # if the user wants to set a password we set haslocalpw for the future params[resource_name][:haslocalpw] = true end # this is copied over from the original devise controller, instead of update_with_password we use update_attributes if resource.update_attributes(params[resource_name]) set_flash_message :notice, :updated sign_in resource_name, resource redirect_to after_update_path_for(resource) else clean_up_passwords(resource) render_with_scope :edit end end end ...
... user = User.new :email => email, :password => SecureRandom.hex(10), :fullname => name, :haslocalpw => false ...
Source: https://habr.com/ru/post/166325/
All Articles