简体   繁体   中英

How do I integrate facebook login for my signin and signup

I already have a user model and controller and a sessions controller. User can sign up and sign in from my app without facebook . However, I want to also integrate facebook login and make it work with the sign in and sign up, the user model, controller and the already programmed sessions. I can integrate most of the Facebook API but the only thing I have never done and I don t know how to do is making the facebook work side by side with the app. This is the first time I am integrating facebook and I am not familiar with their API. Just learning. Thanks a lot for the help. Here is my logic or pseudo code:

On Facebook Login {
Get the Facebook user_id first_name email
if user email already exists set the current user = corresponding user in the database
if user_email does not exist in my database 
sign up the user{
user_name = facebook.user_name
user.email= facebook.user_email
user.Password = facebook_user_id + facebook.user_name
}
}

My app has some facebook API already, the login facebook button works but it is not synchronised with my app's login, sessions and database.

Here is my users_controller:

class UsersController < ApplicationController
before_action :logged_in_user, only: [:index, :edit, :update, :destroy,
                                    :following, :followers]
before_action :correct_user,   only: [:edit, :update]
before_action :admin_user,     only: :destroy
def index
@users = User.all.paginate(page: params[:page])
end
def show
@user = User.find(params[:id])
@posts = @user.posts.paginate(page: params[:page])
end
def new
@user = User.new
end
def create
@user = User.new(user_params)
if @user.save
log_in @user
  flash[:success] = "Welcome to Unstarv!"
  redirect_to @user
else
  render 'new'
end
end
def edit
@user = User.find(params[:id])
end
def update
@user = User.find(params[:id])
if @user.update_attributes(user_params)
  flash[:success] = "Profile updated"
  redirect_to @user
else
  render 'edit'
end
end
def destroy
    User.find(params[:id]).destroy
    flash[:success] = "User deleted"
    redirect_to users_url
end 
def following
@title = "Following"
@user  = User.find(params[:id])
@users = @user.following.paginate(page: params[:page])
render 'show_follow'
end
def followers
@title = "Followers"
@user  = User.find(params[:id])
@users = @user.followers.paginate(page: params[:page])
render 'show_follow'
end
private
def user_params
  params.require(:user).permit(:username, :email, :password,
                               :password_confirmation)
end
 # Confirms a logged-in user.
def logged_in_user
  unless logged_in?
    flash[:danger] = "Please log in."
    redirect_to login_url
  end
end
 # Confirms the correct user.
def correct_user
  @user = User.find(params[:id])
  redirect_to(root_url) unless @user == current_user
end
# Before filters
# Confirms a logged-in user.
def logged_in_user
  unless logged_in?
    store_location
    flash[:danger] = "Please log in."
    redirect_to login_url
  end
end
def correct_user
  @user = User.find(params[:id])
  redirect_to(root_url) unless current_user?(@user)
end
# Confirms an admin user.
def admin_user
  redirect_to(root_url) unless current_user.admin?
end
end

My user model:

class User < ActiveRecord::Base
attr_accessor :remember_member
groupify :group_member
groupify :named_group_member
before_save { self.email = email.downcase }
has_many :posts, dependent: :destroy
has_many :comments, dependent: :destroy
has_many :active_relationships, class_name:  "Relationship",
                              foreign_key: "follower_id",
                              dependent:   :destroy
has_many :passive_relationships, class_name:  "Relationship",
                               foreign_key: "followed_id",
                               dependent:   :destroy
 has_many :following, through: :active_relationships, source: :followed  
 has_many :followers, through: :passive_relationships, source: :follower
 validates :username,  presence: true, length: { maximum: 50 }
 VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i
 validates :email, presence: true, format: { with: VALID_EMAIL_REGEX },
                uniqueness: { case_sensitive: false }
                has_secure_password
                validates :password, length: { minimum: 6 }, allow_blank: true
def feed
following_ids = "SELECT followed_id FROM relationships
                 WHERE  follower_id = :user_id"
Post.where("user_id IN (#{following_ids})
                 OR user_id = :user_id", user_id: id)
end
# Follows a user.
def follow(other_user)
active_relationships.create(followed_id: other_user.id)
end
 # Unfollows a user.
 def unfollow(other_user)
active_relationships.find_by(followed_id: other_user.id).destroy
 end
# Returns true if the current user is following the other user.
def following?(other_user)
following.include?(other_user)
end
# 
def like(other_user)
 end
# Returns the hash digest of the given string.
def User.digest(string)
cost = ActiveModel::SecurePassword.min_cost ? BCrypt::Engine::MIN_COST :
                                              BCrypt::Engine.cost
BCrypt::Password.create(string, cost: cost)
end
# Returns a random token.
def User.new_token
SecureRandom.urlsafe_base64
end
# Remembers a user in the database for use in persistent sessions.
def remember
self.remember_token = User.new_token
update_attribute(:remember_digest, User.digest(remember_token))
end
# Returns true if the given token matches the digest.
def authenticated?(remember_token)
return false if remember_digest.nil?
BCrypt::Password.new(remember_digest).is_password?(remember_token)
end
# Forgets a user.
def forget
update_attribute(:remember_digest, nil)
end
end

My sessions controller:

class SessionsController < ApplicationController
def new
end
def create
user = User.find_by(email: params[:session][:email].downcase)
if user && user.authenticate(params[:session][:password])
log_in user
  params[:session][:remember_me] == '1' ? remember(user) : forget(user)
  redirect_back_or user 
else
 flash.now[:danger] = 'Invalid email/password combination'
 render 'new'
end
end
def destroy
log_out if logged_in?
redirect_to root_url
end
end

My sessions helper:

module SessionsHelper  
# Logs in the given user.
def log_in(user)
session[:user_id] = user.id
end
# Remembers a user in a persistent session.
def remember(user)
user.remember
cookies.permanent.signed[:user_id] = user.id
cookies.permanent[:remember_token] = user.remember_token
end
# Returns true if the given user is the current user.
def current_user?(user)
user == current_user
end
# Returns true if the given user is the current user.
def current_user?(user)
user == current_user
end
# Returns the current logged-in user (if any).
# Returns the user corresponding to the remember token cookie.
def current_user
if (user_id = session[:user_id])
  @current_user ||= User.find_by(id: user_id)
elsif (user_id = cookies.signed[:user_id])
  user = User.find_by(id: user_id)
  if user && user.authenticated?(cookies[:remember_token])
    log_in user
    @current_user = user
  end
end
end
# Returns true if the user is logged in, false otherwise.
def logged_in?
!current_user.nil?
end
# Forgets a persistent session.
def forget(user)
user.forget
cookies.delete(:user_id)
cookies.delete(:remember_token)
end
# Logs out the current user.
# Logs out the current user.
def log_out
forget(current_user)
session.delete(:user_id)
@current_user = nil
end 
# Redirects to stored location (or to the default).
def redirect_back_or(default)
redirect_to(session[:forwarding_url] || default)
session.delete(:forwarding_url)
end
# Stores the URL trying to be accessed.
def store_location
session[:forwarding_url] = request.url if request.get?
end  
end

My application controller:

class ApplicationController < ActionController::Base
# Prevent CSRF attacks by raising an exception.
# For APIs, you may want to use :null_session instead.
protect_from_forgery with: :exception
include SessionsHelper
# Confirms a logged-in user.
def logged_in_user
  unless logged_in?
    store_location
    flash[:danger] = "Please log in."
    redirect_to login_url
  end
end
end

And here is my application layout with some basic facebook api integrated:

<!DOCTYPE html>
<html>
<head>
<title> Unstarv</title>
<%= stylesheet_link_tag 'application', media: 'all',
                                       'data-turbolinks-track' => true %>
<%= javascript_include_tag 'application', 'data-turbolinks-track' => true %>
<%= csrf_meta_tags %>
<%= render 'layouts/shim' %>
</head>
<body>
<div id="fb-root"></div>
<h1 id="fb-welcome"></h1>
<script>
// This is called with the results from from FB.getLoginStatus().
function statusChangeCallback(response) {
console.log('statusChangeCallback');
console.log(response);
// The response object is returned with a status field that lets the
// app know the current login status of the person.
// Full docs on the response object can be found in the documentation
// for FB.getLoginStatus().
if (response.status === 'connected') {
  // Logged into your app and Facebook.
  testAPI();
} else if (response.status === 'not_authorized') {
  // The person is logged into Facebook, but not your app.
  document.getElementById('status').innerHTML = 'Please log ' +
    'into this app.';
} else {
  // The person is not logged into Facebook, so we're not sure if
  // they are logged into this app or not.
  document.getElementById('status').innerHTML = 'Please log ' +
    'into Facebook.';
  }
  }

 // This function is called when someone finishes with the Login
 // Button.  See the onlogin handler attached to it in the sample
 // code below.
 function checkLoginState() {
 FB.getLoginStatus(function(response) {
  statusChangeCallback(response);
  });
 }

 window.fbAsyncInit = function() {
 FB.init({
 appId      : '1427074967594599',
 cookie     : true,  // enable cookies to allow the server to access 
                    // the session
 xfbml      : true,  // parse social plugins on this page
 version    : 'v2.3' // use version 2.3
 });

 // Now that we've initialized the JavaScript SDK, we call 
 // FB.getLoginStatus().  This function gets the state of the
 // person visiting this page and can return one of three states to
 // the callback you provide.  They can be:
 //
 // 1. Logged into your app ('connected')
 // 2. Logged into Facebook, but not your app ('not_authorized')
 // 3. Not logged into Facebook and can't tell if they are logged into
 //    your app or not.
 //
 // These three cases are handled in the callback function.

 FB.getLoginStatus(function(response) {
 statusChangeCallback(response);
 });

  };

 // Load the SDK asynchronously
  (function(d, s, id) {
  var js, fjs = d.getElementsByTagName(s)[0];
  if (d.getElementById(id)) return;
  js = d.createElement(s); js.id = id;
  js.src = "//connect.facebook.net/en_US/sdk.js";
  fjs.parentNode.insertBefore(js, fjs);
  }(document, 'script', 'facebook-jssdk'));

  // Here we run a very simple test of the Graph API after login is
  // successful.  See statusChangeCallback() for when this call is made.
  function testAPI() {
  console.log('Welcome!  Fetching your information.... ');
  FB.api('/me', function(response) {
  console.log('Successful login for: ' + response.name);
  document.getElementById('status').innerHTML =
    'Thanks for logging in, ' + response.name + '!';
   });
   }

  // Place following code after FB.init call.

  function onLogin(response) {
  if (response.status == 'connected') {
  FB.api('/me', function(data) {
  var welcomeBlock = document.getElementById('fb-welcome');
  welcomeBlock.innerHTML = 'Hello, ' + data.first_name + '!';
  });
  }
  }

  FB.getLoginStatus(function(response) {
  // Check login status on load, and if the user is
  // already logged in, go directly to the welcome message.
  if (response.status == 'connected') {
  onLogin(response);
  } else {
   // Otherwise, show Login dialog first.
FB.login(function(response) {
  onLogin(response);
}, {scope: 'user_friends, email, user_info'});
 }
});
</script>

<%= render 'layouts/header' %>



<div class="container">
<% flash.each do |message_type, message| %>
    <div class="alert alert-<%= message_type %>"><%= message %></div>
  <% end %>
  <%= yield %>
  <%= render 'layouts/footer' %>
</div>
</body>
</html>

If answer for your question

How do I integrate facebook login for my signin and signup

I recommend you to use omniauth-facebook gem. It handles base work with facebook. Also but not necessarily you can use devise gem.

Those gems handle basic staff. Yes you can also write you own solution, wraps it to gem, but I think thats not question for this site.

Ruby on Rails 4 - Authentication with Facebook and OmniAuth.

I recommend use this way.


Idea of Facebook auth, that:

  1. You can get user data from Facebook and can prefill user form data (like name, photo, etc).
  2. And if user is already connect facebook account to your application, you can after each facebook auth login him to your application checking for example if uid (that you get from facebook) exist in database.

On Facebook Login
Get the Facebook user_id first_name email

For email you need special credential, and sometimes user want use other email for your application. Or he haven't access to his email that he used for facebook.

if user email already exists set the current user = corresponding user in the database

ok

if user_email does not exist in my database sign up the user{ user_name = facebook.user_name user.email= facebook.user_email user.Password = facebook_user_id + facebook.user_name }

I think you should redirect user to form registration and prefill form field with his data. Than user can change any of this field, type his own password and create account. Or if you want use only facebook login, you can create account without any additional steps. And each time user can login through facebook button. Create password and then send it to email, it is bad idea.

But you can use devise instead of your session class, etc :)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM