I'm using omniauth and devise and google to have users login to the website. I need to only allow users to sign in if they have a specific company email. For example, they click on sign-in with google and then unless they have a "#somecompany.com" email address they can successfully login. Otherwise they cannot login with a normal "#gmail.com" email. I cant seem to find where to do that in the documentation.
user model
def self.from_omniauth(access_token)
data = access_token.info
user = User.where(email: data['email']).first_or_initialize
user.given_name = data['first_name']
user.family_name = data['last_name']
user.password = SecureRandom.uuid
user.save!
user
end
omniauth controller
def google_oauth2
#user = User.from_omniauth(request.env["omniauth.auth"])
if #user.persisted?
flash[:notice] = I18n.t "devise.omniauth_callbacks.success", :kind => "Google"
sign_in_and_redirect #user, :event => :authentication
else
session["devise.google_data"] = request.env["omniauth.auth"]
redirect_to new_user_registration_url
end
end
routes
devise_for :users, :controllers => { :omniauth_callbacks => "omniauth_callbacks" }
You can try:
providers:
- { name: 'google_oauth2', app_id: 'APP-ID',
app_secret: 'APP-SECRET',
args: { access_type: 'offline', approval_prompt: 'auto', hd: 'example.com' } }
where example.com is changed to your company domain.
Otherwise you can try these answers on StackOverflow:
In Rails, is it possible to limit who can log in with google using the api?
Restrict Login Email with Google OAuth2.0 to Specific Domain Name
Update your method in model as
def self.from_omniauth(access_token)
data = access_token.info
user = User.where(email: data['email']).first_or_initialize
user.given_name = data['first_name']
user.family_name = data['last_name']
user.password = SecureRandom.uuid
user.save! unless data['email'].split("#").include?('gmail.com')
user
end
update google_oauth2 method as well for already registered user
if #user.persisted? && !#user.email.split("#").include?('gmail.com')
Related
I have configured Facebook via login using this tutorial and it's working fine. I'm able to send a login call at Facebook and after users acknowledgment, it's redirect to my site. However, In the callback URL, only "code" is returning but I required an access token.
Here is my code.
Devise.rb
config.omniauth :facebook, "client_id", "secret", scope: 'public_profile,email,manage_pages,read_insights',:display => 'popup'
User.rb
devise :database_authenticatable,:registerable,:recoverable,
:rememberable, :trackable, :validatable,
:confirmable,:lockable,:timeoutable,
:omniauthable, omniauth_providers: [:facebook]
def self.current
Thread.current[:user]
end
def self.current=(user)
Thread.current[:user] = user
end
def self.create_from_provider_data(provider_data)
where(provider: provider_data.provider, uid: provider_data.uid).first_or_create do | user |
user.email = provider_data.info.email
user.password = Devise.friendly_token[0, 20]
user.skip_confirmation!
end
end
omniauth controller
def facebook
#user = User.create_from_provider_data(request.env['omniauth.auth'])
if #user.persisted?
sign_in_and_redirect #user
set_flash_message(:notice, :success, kind: 'Facebook') if is_navigational_format?
else
user = User.create!(email: auth.info.email,
password: Devise.friendly_token[0,20],
user_name: auth.info.first_name
)
user.authentications.create!(user_id: user.id,
provider: auth.provider,
uid: auth.uid)
flash[:error] = 'There was a problem signing you in through Facebook. Please register or try signing in later.'
redirect_to new_user_registration_url
end
end
def failure
flash[:error] = 'There was a problem signing you in. Please register or try signing in later.'
redirect_to new_user_registration_url
end
CallBack Response:
Parameters: {"code"=>"AQBBtixd3nmIkT_KyKUKmy68hBYt7kdZ0jX1pATNmDCSWmjfoiAC_8C5aOF3P3jKRyhKYX2JfL0gZDWZCTraN_kdZpxIJu8pYWkktr0E9Q2WXk6xjz0Uyz4cS2YeTv0SyqjZbZvQr8roxKCPvZb-6UKbCzMGZsi3-VLXK3suPfs729nR8MilmuatZqQ-TanApTy-8Qh195ntqfD6gIMhbZZMBhAuVSbf6GmyT-anNV0exczDMzKIF6OpZoIFC7Vxez8EH3cR7BUPqc5OldfbDD8j9pY6kDeoc00An9wuGj4hAfne_jeShUZTw_zSQMmxkbMqe2acFPJWE5DLL2QNII54", "state"=>"c231b221dba5bdc33fff6349af5793c0cbb5bdcc69253372"}
As per this documentation, we can get required information from callback response.
How i can update my code to get access token.
You can to send another request with the code to get the access token.
You can fetch email and other details by request.env['omniauth.auth']['info']['email'].
As I can see you are using auth.info.email and auth is not defined due to which error persist.
I used the Devise gem to set up a User model for my app. I'm trying to work in Facebook authentication using Omniauth. I can retrieve the Name and Email data, but I'm having trouble getting any other public_profile data. In this example I'm trying to get Gender, but none of the other data works either. When I visit this path: user_omniauth_authorize_path(:facebook), the "facebook" action in "controllers/registrations_controller.rb" is called. But for the user that is created, user.gender, and all other data other than name and email, comes back nil.
config/initializers/devise.rb
config.omniauth :facebook, "<ID>", "<SECRET>", scope: 'email', display: 'popup', info_fields: 'email,name,gender'
app/models/devise.rb
devise :omniauthable, :omniauth_providers => [:facebook]
def self.from_omniauth(auth)
where(provider: auth.provider, uid: auth.uid).first_or_create do |user|
user.gender = auth.extra.raw_info.gender.to_s
end
end
app/controllers/registrations_controller.rb
def facebook
#user = User.from_omniauth(request.env["omniauth.auth"])
if #user.persisted?
sign_in_and_redirect #user, :event => :authentication
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
gemfile
gem 'devise'
gem 'omniauth-facebook'
What's strange is I can't even get the class type of the data. Name and Email return "String" if I call class.to_s on them, but all the other data returns "NilClass".
What am I doing wrong?
UPDATE:
Okay I literally have no idea what could have possibly changed, but this same exact code now suddenly works....so problem solved I guess?
Try This way
def self.from_omniauth(access_token)
data = access_token.info
email = data.info.email
first_name = data.first_name
last_name = data.last_name
end
You can gender attribute using auth.info.gender
where(provider: auth.provider, uid: auth.uid).first_or_create do |user|
user.gender = auth.info.gender
end
You sure the user has these data as public info?
I would try to debug the permissions in Facebook's open graph explorer
https://developers.facebook.com/tools/explorer/
That will help you understand wether it is permissions, lacking data issue or a problem with the integration.
I am working on Facebook authentication for Devise and I stumbled upon one problem - when will try to sign up someone with Facebook email that is already in the system, I get an error (which is partially correct).
I would need to redirect this user back to the homepage and there to print out this issue, but at the moment I am having this message printed as an error (on localhost).
Here's how I am doing that (user model):
def self.find_for_facebook_oauth(access_token, signed_in_resource=nil)
data = access_token.extra.raw_info
if user = User.where(:provider => 'facebook', :uid => data.id).first
user
else # Create a user with a stub password.
user = User.create!(:first_name => data.first_name,
:last_name => data.last_name,
:email => data.email,
:password => Devise.friendly_token[0,20],
:provider => 'facebook',
:uid => data.id,
:terms_of_use => true)
end
return user if user
end
How to redirect the user on a page where would be printed out the validation messages?
Thanks
Why don't you have something like this in your model and controller
Return just the user object from the method
def self.find_for_facebook_oauth(access_token, signed_in_resource=nil)
data = access_token.extra.raw_info
#Look for the first user with provider: :facebook & uid: data.id,
#If now user is there go ahead and create one with first_or_create.
user = User.where(:provider => 'facebook', :uid => data.id).first_or_create do |user|
user.first_name = data.first_name,
user.last_name = data.last_name,
user.email = data.email,
user.password = Devise.friendly_token[0,20],
user.provider = 'facebook',
user.uid = data.id,
user.terms_od_use = true
end
end
Controller
def sign_in_method #dummy method
user = User.find_for_facebook_oauth(access_token, signed_in_resource=nil)
if user.valid?
redirect success_url #any url where u want to redirect on success
else user.errors
redirect root_url, error: user.errors.full_messages.join(', ')
end
end
I got Devise setup and working for my app. When i add
gem omniauth-google-oauth2
im getting an error in controller sign_in_and_redirect method.
Error:
NoMethodError (undefined method `serialize_into_session' for String:Class):
app/controllers/users/omniauth_callbacks_controller.rb:9:in `google_oauth2'
Code:
omniauth_callbacks_controller.rb
def google_oauth2
#user = User.find_for_google_oauth2(request.env["omniauth.auth"], current_user)
if #user.persisted?
flash[:notice] = I18n.t "devise.omniauth_callbacks.success", :kind => "Google"
sign_in_and_redirect sites_path, :event => :authentication
else
session["devise.google_data"] = request.env["omniauth.auth"]
redirect_to new_user_registration_url
end
end
user.rb
def self.find_for_google_oauth2(access_token, signed_in_resource=nil)
data = access_token.info
user = User.where(:email => data["email"]).first
unless user
user = User.create(email: data["email"],
password: Devise.friendly_token[0, 20]
)
end
user
end
devise.rb
require "omniauth-google-oauth2"
config.omniauth :google_oauth2, 'CLIENT_ID', 'CLIENT_SECRET', {access_type: "offline", approval_prompt: ""}
Please let me know if im missing something.
Your config is good, but I use for this situation instead omniauth-google-oauth2, this gem https://rubygems.org/gems/omniauth-openid.
Add to your /config/initializer/devise.rb the next:
require 'openid/store/filesystem'
config.omniauth :open_id, :store => OpenID::Store::Filesystem.new('/tmp'), :name => 'google', :identifier => 'https://www.google.com/accounts/o8/id', :require => 'omniauth-openid'
and after inside your omniauth_callbacks_controller.rb change the method User.find_for_google_oauth2... by User.find_for_open_id...
on your user model change the self.find_for_google_oauth2 by self.find_for_open_id.
Try with this gem please!
I have the following action:
users.rb:
def omniauth_create
auth = request.env["omniauth.auth"]
user = User.from_omniauth(env["omniauth.auth"])
unless user.email.blank?
if user.id.nil?
# Save the user since he hasn't been created yet
user.save!
end
sign_in user
redirect_back_or user
else
# Send user to a form to fill his email
#session[:omniauth] = request.env['omniauth.auth'].except('extra')
redirect_to(enter_email_path(oprovider: user.provider,
ouid: user.uid,
oname: user.name,
opassword: user.password,
opassword_confirmation: user.password))
end
end
It does the following:
If the user's email is not blank, sign him in, and redirect him to his profile (and save him if his id is nil. In other words, if he hasn't been created yet).
If the user's email is blank, send him to enter_email_path (where the user can enter his email).
Now I want to add another if statement that flashes an error if the email had been already taken, and redirects the user to the root_path
I'm not very sure how to do this, Any suggestions? (and where to put that if statement?)
EDIT:
Strange, got this instead of the redirect to the root path:
Validation failed: Email has already been taken
I don't know if this helps but here is the origin of from_omniauth:
def self.from_omniauth(auth)
find_by_provider_and_uid(auth["provider"], auth["uid"]) || User.create_with_omniauth(auth)
end
def self.create_with_omniauth(auth)
new do |user|
user.provider = auth["provider"]
user.uid = auth["uid"]
user.name = auth["info"]["name"]
user.email = auth["info"]["email"]
user.password = user.password_confirmation = SecureRandom.urlsafe_base64(n=6)
end
end
The code as it is right now:
user.rb:
# if user.email.present?
if user.id.nil?
# User.find_by_email(user.email).present?
if User.exists?(:email => user.email)
redirect_to root_path
end
user.save!
end
sign_in user
redirect_back_or user
else
(the rest didn't change).
It seems like the code is ignoring the if User.exists?(:email => user.email) part?
Rails has a method to check if an object exists based on parameters. You could do this:
if user.email.present?
if user.id.nil?
if User.exists?(:email => user.email)
# return redirect email is already token
end
# save user
end
# sign_in user
else
# redirect to get email
end
By the way, I am not familiar with Omniauth so I am not sure what is right but new_record? is usually used when checking if object is already saved or not. If you have an id, it usually is.
If you are confused you could create functions in your User model for better reading like
class User
def new?
id.nil?
end
def email_taken?
self.class.exists?(:email => email)
end
end
# back to controller
if user.email.present?
if user.new?
if user.email_taken?
# return redirect email is already token
end
# save user
end
# sign_in user
else
# redirect to get email
end
try this
def omniauth_create
auth = request.env["omniauth.auth"]
user = User.from_omniauth(env["omniauth.auth"])
if user.email.present?
if user.id.nil?
if User.find_by_email(user.email).present?
# send error email already be taken
# or login with that user that means define that user for sign in
else
# save user and login with that user
user.save!
end
sign_in user
redirect_back_or user
end
else
# Send user to a form to fill his email
# session[:omniauth] = request.env['omniauth.auth'].except('extra')
redirect_to(enter_email_path(oprovider: user.provider,
ouid: user.uid,
oname: user.name,
opassword: user.password,
opassword_confirmation: user.password))
end
Update
you can also use find_or_create method
def self.find_or_create(attributes)
Model.where(attributes).first || Model.create(attributes)
end
Update 2
In your Modal file
class << self
def create_with_omniauth(auth)
create! do |user|
user.provider = auth['provider']
user.uid = auth['uid']
if auth['info']
user.uid = auth['uid'] || ""
user.name = auth['info']['name'] || ""
user.email = auth['info']['email'] || ""
user.access_token = auth['credentials']['token'] || ""
user.oauth_token_secret = auth['credentials']['secret'] || ""
user.oauth_token = auth['credentials']['token'] || ""
end
end
end
end
In your controller
def create
auth = request.env["omniauth.auth"]
user = User.where(:provider => auth['provider'],:uid => auth['uid']).first || User.create_with_omniauth(auth)
session[:user_id] = user.id
redirect_to root_url
end
Not sure if you want a rails answer or a SQL answer, but you could use the following SQL to find your response:
select id from users where email = :email
If this returns 0 rows, the email does not exist, if it returns 1, it does exist. I guess you could also use
Users#find(:first, :conditions => 'email = #{email}')
but I haven't tested this.