I'm going to implement facebook and vkontakte login(registering) for my rails app, and with facebook all is ok (login and regitering is ok), done as in manual
here
But with vkontakte something wrong, i'll still get "empty" page
here is my code:
class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController
def facebook
# You need to implement the method below in your model (e.g. app/models/user.rb)
#user = User.find_for_facebook_oauth(request.env["omniauth.auth"], current_user)
if #user.persisted?
flash[:notice] = I18n.t "devise.omniauth_callbacks.success", :kind => "Facebook"
sign_in_and_redirect #user, :event => :authentication
else
session["devise.facebook_data"] = request.env["omniauth.auth"]
redirect_to new_user_registration_url
end
end
def vkontakte
#user = User.find_for_vkontakte_oauth(request.env["omniauth.auth"], current_user)
if #user.persisted?
flash[:notice] = I18n.t "devise.omniauth_callbacks.success", :kind => "Vkontakte"
sign_in_and_redirect #user, :event => :authentication
else
session["devise.vkontakte_data"] = request.env["omniauth.auth"]
redirect_to new_user_registration_url
end
end
end
And model:
class User < ActiveRecord::Base
# Include default devise modules. Others available are:
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable,
:token_authenticatable, :lockable, :timeoutable, :omniauthable #:confirmable,
# Setup accessible (or protected) attributes for your model
attr_accessible :email, :password, :password_confirmation, :remember_me, :provider, :uid, :name
def self.find_for_facebook_oauth(auth, signed_in_resource=nil)
user = User.where(:provider => auth.provider, :uid => auth.uid).first
unless user
user = User.create(name:auth.extra.raw_info.name,
provider:auth.provider,
uid:auth.uid,
email:auth.info.email,
password:Devise.friendly_token[0,20]
)
end
user
end
def self.new_with_session(params, session)
super.tap do |user|
if data = session["devise.facebook_data"] && session["devise.facebook_data"]["extra"]["raw_info"]
user.email = data["email"] if user.email.blank?
end
if data = session["devise.vkontakte_data"] && session["devise.vkontakte_data"]["extra"]["raw_info"]
user.email = data["email"] if user.email.blank?
end
end
end
def self.find_for_vkontakte_oauth(auth, signed_in_resource=nil)
user = User.where(:provider => auth.provider, :uid => auth.uid).first
unless user
user = User.create(name:auth.info.name,
provider:auth.provider,
uid:auth.uid,
email:auth.extra.raw_info.domain+'#vk.com',
password:Devise.friendly_token[0,20]
)
end
user
end
What's wrong?
Have you tried omniauth railscasts made by Ryan Bates ?
http://railscasts.com/episodes/360-facebook-authentication - here is for facebook
http://railscasts.com/episodes/235-omniauth-part-1
http://railscasts.com/episodes/236-omniauth-part-2
these are for authentication from different social networks. I'm started working with VK api, so if you will have problems with implmentation of these railscasts - tell me.
check if your server host as like registered in vk. For development, you may edit /etc/hosts and access to your site via this host.
Example: site is inmyhouse.su
/etc/hosts: 127.0.1.1 local.inmyhouse.su
Related
I can't get devise omniauth-facebook to work. I've followed the devise guide to no avail. I think my problem is the model is not retrieving info.
Model
class User < ActiveRecord::Base
has_attached_file :image, styles: {large: "1920x1080#", medium: "800x500#", thumb: "100x100"}, :default_url => "/images/:style/missing.png"
validates_attachment_content_type :image, :content_type => ["image/jpg", "image/jpeg", "image/png", "image/gif"]
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable,
:omniauthable, :omniauth_providers => [:facebook]
def admin?
admin
end
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]
user.name = auth.info.name # assuming the user model has a name
user.image = auth.info.image # assuming the user model has an image
puts request.env["omniauth.auth"]
end
end
def self.new_with_session(params, session)
super.tap do |user|
if data = session["devise.facebook_data"] && session["devise.facebook_data"]["extra"]["raw_info"]
user.email = data["email"] if user.email.blank?
end
end
end
end
Routes.rb
Rails.application.routes.draw do
mount RailsAdmin::Engine => '/admin', as: 'rails_admin'
devise_for :users do
delete 'logout' => 'sessions#destroy', :as => :destroy_user_session,
:controllers => { :omniauth_callbacks => "user/omniauth_callbacks" }
end
Controller
class User::OmniauthCallbacksController < Devise::OmniauthCallbacksController
# You should configure your model like this:
# devise :omniauthable, omniauth_providers: [:twitter]
# You should also create an action method in this controller like this:
# def twitter
# end
def facebook
# You need to implement the method below in your model (e.g. app/models/user.rb)
#user = User.from_omniauth(request.env["omniauth.auth"])
if #user.persisted?
sign_in_and_redirect #user, :event => :authentication #this will throw if #user is not activated
set_flash_message(:notice, :success, :kind => "Facebook") if is_navigational_format?
else
session["devise.facebook_data"] = request.env["omniauth.auth"]
redirect_to new_user_registration_url
end
end
def self.new_with_session(params, session)
if session["devise.user_attributes"]
new(session[devise.user_attributes], without_protection: true) do |user|
user.attributes = params
user.valid?
end
else
super
end
After I click "Login with facebook", it directs to a facebook url, I input a password, press enter, get redirected to the same page with a long url. (http://localhost:3000/demographics?code=AQDK1z40APoLiWykomxDDUljBUNHotenM4lzj_bZMhH8iQ74J_Nu_EUnPqBqkbNAeWQEPZwQs7YghqkB4eD7AoQLkN_RuYIlmotMtrJc4UyGRSe3CJIHcxp6kcB9BuYHA_Ldz0NMJvvGzOuvC-uDpFn6TyrzvV5v9LvivORXVduSsCy7_r6PcW8jxAkWqZzKyASXf26h8h3f_kha2d0KX6Ygft8ozN1HT9Xr-1y7ZtIKgTXEGMrqK950kASv2oTE0tQ5CYt6mfEZsVyLpykYIApOls8NLhjOaOIJewzV9EnLdSq0FbrvtedhhDmy-hg6IkRAbRVgwEkfUFsi9DXoxKyX&state=bdbf498f33f67ef57f3f54b846f870f21bb80c039c099f1a#=)
EDIT:
Now I am getting
Could not authenticate you from Facebook because "Invalid credentials".
So the problem was I didn't config the routes properly.
In fact, I had to remove all existing devise routes and add this line.
devise_for :users, :controllers => { :omniauth_callbacks => "users/omniauth_callbacks" }
This is what worked for me:
user model
devise :omniauthable, :omniauth_providers => [:facebook]
def self.from_omniauth(auth)
where(provider: auth.provider, uid: auth.uid).first_or_create do |user|
user.provider = auth.provider
user.uid = auth.uid
user.email = auth.info.email
user.username = auth.info.name #gives full user name
user.password = Devise.friendly_token[0,20]
user.skip_confirmation!
user.save
end
end
devise.rb
config.omniauth :facebook, ENV['facebook_key'], ENV['facebook_secret'],
scope: 'email,public_profile', info_fields: 'email, first_name, last_name'
callbacks_controller.rb
class CallbacksController < ApplicationController
def facebook
#user = User.from_omniauth(request.env["omniauth.auth"])
if #user.persisted?
sign_in_and_redirect #user, :event => :authentication
flash[:notice] = "Logged in as #{#user.username}"
else
session["devise.facebook_data"] = request.env["omniauth.auth"]
redirect_to new_user_registration_url
end
end
def failure
redirect_to root_path
end
end
routes.rb
devise_for :users, controllers: { omniauth_callbacks: "callbacks" }
The problem is Facebook is not always returning email for user
from facebook developers https://developers.facebook.com/bugs/298946933534016
Some possible reasons:
No Email address on account
No confirmed email address on account
No verified email address on account
User entered a security checkpoint which required them to reconfirm
their email address and they have not yet done so
Users's email address is unreachable
Set an condition in your controller if request.env["omniauth.auth"].info.email.present? see the script below.
class User::OmniauthCallbacksController < Devise::OmniauthCallbacksController
def facebook
puts request.env["omniauth.auth"] # check if request.env["omniauth.auth"] is provided an email
if request.env["omniauth.auth"].info.email.present?
#user = User.from_omniauth(request.env["omniauth.auth"])
if #user.persisted?
sign_in_and_redirect #user, :event => :authentication #this will throw if #user is not activated
set_flash_message(:notice, :success, :kind => "Facebook") if is_navigational_format?
else
session["devise.facebook_data"] = request.env["omniauth.auth"]
redirect_to new_user_registration_url
end
else
redirect_to new_user_registration_url, notice: "Couldn't connect to your #{request.env["omniauth.auth"].provider} account. Try to sign up."
end
end
end
So I've found similar questions on this site with not a really direct solution to this problem that I'm seeing.
I'm using omniauth-instagram and omniauth-facebook with devise and getting the error below (with both providers).
URL:
http://localhost:3000/users/auth/callbacks/facebook
Error: Not found. Authentication passthru.
Below are examples of my callbacks_controller.rb, user.rb and routes.rb -- Any helpful tips in the right direction are much appreciated. Let me know if I should provide any other information!
callbacks_controller.rb
class Users::Auth::CallbacksController < Devise::OmniauthCallbacksController
def instagram
# You need to implement the method below in your model (e.g. app/models/user.rb)
#user = User.find_for_oauth(request.env["omniauth.auth"], current_users)
if #user.persisted?
sign_in_and_redirect #user, :event => :authentication #this will throw if #user is not activated
set_flash_message(:notice, :success, :kind => "Instagram") if is_navigational_format?
else
session["devise.instagram_data"] = request.env["omniauth.auth"]
redirect_to new_user_registration_url
end
end
def facebook
# You need to implement the method below in your model (e.g. app/models/user.rb)
#user = User.find_for_oauth(request.env["omniauth.auth"], current_users)
if #user.persisted?
sign_in_and_redirect #user, :event => :authentication #this will throw if #user is not activated
set_flash_message(:notice, :success, :kind => "Facebook") if is_navigational_format?
else
session["devise.facebook_data"] = request.env["omniauth.auth"]
redirect_to new_user_registration_url
end
end
end
user.rb
class User < ActiveRecord::Base
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
devise :omniauthable, :omniauth_providers => [:instagram, :facebook]
def self.find_for_oauth(auth, signed_in_resource = nil)
# Get the identity and user if they exist
identity = Identity.find_for_oauth(auth)
user = identity.user
if user.nil?
# Get the existing user from email if the OAuth provider gives us an email
user = User.where(:email => auth.info.email).first if auth.info.email
# Create the user if it is a new registration
if user.nil?
user = User.new(
name: auth.extra.raw_info.name,
#username: auth.info.nickname || auth.uid,
email: auth.info.email.blank? ? TEMP_EMAIL : auth.info.email,
password: Devise.friendly_token[0,20]
)
user.skip_confirmation!
user.save!
end
# Associate the identity with the user if not already
if identity.user != user
identity.user = user
identity.save!
end
end
user
end
end
routes.rb
devise_for :users, :controllers => { :omniauth_callbacks => "users/auth/callbacks" }
Did you add both in devise.rb?
# devise.rb
# ...
# ==> OmniAuth
# Add a new OmniAuth provider. Check the wiki for more information on setting
# up on your models and hooks.
# config.omniauth :github, 'APP_ID', 'APP_SECRET', :scope => 'user,public_repo'
config.omniauth :facebook, "KEY", "SECRET"
config.omniauth :instagram, "KEY", "SECRET"
After configuring your strategy, you need to make your model (e.g. app/models/user.rb) omniauthable:
devise :omniauthable, :omniauth_providers => [:facebook, :instagram]
I used devise to allow users register and login within the site. I also used omniauth-facebook to allow users login with their facebook accounts. However, people who registered first with the same email as their facebook-email cannot login with their facebook account.I want to use both facebook login and site registration.
This is my user.rb
class User < ActiveRecord::Base
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable, :omniauthable
def self.find_for_facebook_oauth(auth, signed_in_resource=nil)
user = User.where(:provider => auth.provider, :uid => auth.uid).first
unless user
user = User.create(name:auth.extra.raw_info.name,
provider:auth.provider,
uid:auth.uid,
email:auth.info.email,
password:Devise.friendly_token[0,20]
)
end
user
end
end
And omniauth_callbacks_controller.rb
class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController
def facebook
#user = User.find_for_facebook_oauth(request.env["omniauth.auth"], current_user)
if #user.persisted?
sign_in_and_redirect #user, :event => :authentication #this will throw if #user is not activated
set_flash_message(:notice, :success, :kind => "Facebook") if is_navigational_format?
else
session["devise.facebook_data"] = request.env["omniauth.auth"]
redirect_to new_user_registration_url
end
end
end
You can easily do this the following way:
Create another model called Authorization with the attributes:
provider:string, uid:string, user_id:integer
Now User would be associated with Authorization as follows:
class User
has_many :authorizations
...
end
Whenever a facebook login occurs, check with the authorizations model to see if a record is present. If not create it and link it to the user model (by searching by email id).
Get the proper user object and login with it.
Source: http://net.tutsplus.com/tutorials/ruby/how-to-use-omniauth-to-authenticate-your-users/
I asked the question and just found ways out based on matrixtheone's anwser (really great help) and this blog.
http://www.orhancanceylan.com/rails-twitter-and-facebook-authentications-with-omniauth-and-devise/
Here is the code. If there is something wrong, please figure it out
def facebook
omni = request.env["omniauth.auth"]
authentication = Authentication.find_by_provider_and_uid(omni['provider'],omni['uid'])
if authentication
flash[:notice] = "Logged in Successfully"
sign_in_and_redirect User.find(authentication.user_id)
elsif user = User.find_by(email: omni['extra']['raw_info'].email)
user.authentications.create!(provider:omni['provider'],
uid:omni['uid'])
flash[:notice] = "Authentication for registered user is successful"
sign_in_and_redirect user
else
user = User.new
user.password = Devise.friendly_token[0,20]
user.email = omni['extra']['raw_info'].email
user.authentications.build(provider:omni['provider'],
uid:omni['uid'])
if user.save
flash[:notice] = "Logged in."
sign_in_and_redirect User.find(user.id)
else
session[:omniauth] = omni.except('extra')
redirect_to new_user_registration_path
end
end
end
You can add a custom validation for the email field in user model like
email_regex = /\A[\w+\-.]+#[a-z\d\-.]+\.[a-z]+\z/i
validates :email, :presence =>{:message => "An email address must be entered."},
:format => { :with => email_regex , :message => "The email address is not valid." },
:uniqueness => { :case_sensitive => false, :message => "The email address is already used."}
So I've got Omniauth with Twitter validation functioning at about 95%, in other-words it's almost fully functional. When clicking the | Sign-In with Twitter | button on my app, it re-directs me to twitter where I am then prompted to enter my Twitter credentials, and then re-directed back to the app.
However, instead of logging me in after the Twitter auth process I get the following error on the Sign-in page:
1 error prohibited this user from being saved:
Email can't be blank
How can I get Omniauth to re-direct me to the logged in page in my case the /posts page? And why is it producing such an error, when Omniauth is supposed to authorize me via Twitter validation?
User model:
class User < ActiveRecord::Base
has_many :authentications
# Include default devise modules. Others available are:
# :token_authenticatable, :lockable, :timeoutable and :activatable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
# Setup accessible (or protected) attributes for your model
attr_accessible :email, :password, :password_confirmation
# attr_accessible :title, :body
def apply_omniauth(omniauth)
self.email = omniauth['info']['email'] if email.blank?
authentications.build(:provider => omniauth['provider'], :uid => omniauth['uid'])
end
def password_required?
(authentications.empty? || !password.blank?) && super
end
end
Authentications Controller:
class AuthenticationsController < ApplicationController
def create
omniauth = request.env["omniauth.auth"]
authentication = Authentication.find_by_provider_and_uid(omniauth['provider'], omniauth['uid'])
if authentication
flash[:notice] = "Signed in successfully."
sign_in_and_redirect(:user, authentication.user)
elsif current_user
current_user.authentications.create!(:provider => omniauth['provider'], :uid => omniauth['uid'])
flash[:notice] = "Authentication successful."
redirect_to authentications_url
else
user = User.new
user.apply_omniauth(omniauth)
if user.save
flash[:notice] = "Signed in successfully."
sign_in_and_redirect(:user, user)
else
session[:omniauth] = omniauth.except('extra')
redirect_to new_user_registration_url
end
end
end
end
Registrations Controller:
class RegistrationsController < Devise::RegistrationsController
def create
super
session[:omniauth] = nil unless #user.new_record?
end
private
def build_resource(*args)
super
if session[:omniauth]
#user.apply_omniauth(session[:omniauth])
#user.valid?
end
end
end
I just remembered, the reason is actually simple, twitter doesn't return email in the omniauth request so it redirects you to the new user registration page and you have to fill in your email.
https://github.com/intridea/omniauth/wiki/Auth-Hash-Schema
furthermore this could be of help
Skip email validation for omniauth-twitter on devise 2.0
I am using a GEM device to authenticate user from Facebook oauth. I use the following to create a user
def self.find_for_facebook_oauth(auth, signed_in_resource=nil)
user = User.where(:provider => auth.provider, :uid => auth.uid).first
unless user
user = User.create(name:auth.extra.raw_info.name,
provider:auth.provider,
uid:auth.uid,
email:auth.info.email,
password:Devise.friendly_token[0,20]
)
end
user
end
and then to login:
def facebook
#user = User.find_for_facebook_oauth(request.env["omniauth.auth"], current_user)
def after_sign_in_path_for(resource)
return '/mypage'
end
if #user.persisted?
flash[:notice] = I18n.t "devise.omniauth_callbacks.success", :kind => "Facebook"
sign_in_and_redirect #user, :event => :authentication
else
session["devise.facebook_data"] = request.env["omniauth.auth"]
redirect_to new_user_registration_url
end
end
However when the user visits the app after about 15 minutes of inactivity they will no longer be logged in. How can I increase this time? I don't seem to be getting anywhere currently.
If anyone could provide details of setting a cookie or something so that this will persist even when the browser is closed that would be good to.
EDIT
Full user.rb
class User < ActiveRecord::Base
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable,
:omniauthable
# Setup accessible (or protected) attributes for your model
attr_accessible :name, :provider, :uid, :email, :password, :password_confirmation, :remember_me
has_many :ratings
has_many :rated_recipes, :through => :ratings, :source => :recipes
def self.find_for_facebook_oauth(auth, signed_in_resource=nil)
user = User.where(:provider => auth.provider, :uid => auth.uid).first
unless user
user = User.create(name:auth.extra.raw_info.name,
provider:auth.provider,
uid:auth.uid,
email:auth.info.email,
password:Devise.friendly_token[0,20]
)
end
user
end
end
EDIT:
So some of the advice below has got me somewhere. It all seems to work fine when you access the rails via the url it is hosted at. However as soon as you hit the facebook app page the user no longer persists in any of the tabs open. Can't see why this would be