Rails - Generate email address when one doesn't exist with Omniauth integration - ruby-on-rails

I am working with omniauth with Rails and trying to get twitter, facebook and google hooked up for authentication but keep running into this error:
PG::Error: ERROR: duplicate key value violates unique constraint "index_users_on_email"
DETAIL: Key (email)=() already exists.
Here is my Authentication Controller:
class AuthorizationsController < ApplicationController
def create
authentication = Authorization.find_by_provider_and_uid(auth['provider'], auth['uid'])
if authentication
flash[:notice] = "Signed In Successfully"
sign_in authentication.user, event: :authentication
redirect_to root_path
else
athlete = Athlete.new
athlete.apply_omniauth(auth)
debugger
if athlete.save(validate: false)
flash[:notice] = "Account created and signed in successfully"
sign_in athlete, event: :authentication
redirect_to finalize_profile_path
else
flash[:error] = "An error has occurred. Please try again."
redirect_to root_path
end
end
end
def failure
render json: params.to_json
end
private
def auth
request.env["omniauth.auth"]
end
def resource(user_type)
user_type.downcase.to_sym
end
end
I think what is happening is that when the Athlete is created it is creating one with a blank email address and the unique key is failing... how could I get around this? I think I know how to fix this for Google integration but since Twitter doesn't return an email, this issue will not resolve itself

This is how I was able to get it working:
class AuthorizationsController < ApplicationController
def create
authentication = Authorization.find_by_provider_and_uid(auth['provider'], auth['uid'])
if authentication
flash[:notice] = "Signed In Successfully"
sign_in authentication.user, event: :authentication
redirect_to root_path
else
athlete = Athlete.new(email: generate_auth_email(params[:provider]) )
athlete.apply_omniauth(auth)
debugger
if athlete.save(validate: false)
flash[:notice] = "Account created and signed in successfully"
sign_in athlete, event: :authentication
redirect_to finalize_profile_path
else
flash[:error] = "An error has occurred. Please try again."
redirect_to root_path
end
end
end
def failure
render json: params.to_json
end
private
def auth
request.env["omniauth.auth"]
end
def resource(user_type)
user_type.downcase.to_sym
end
def generate_auth_email(provider)
return auth.info.try(:email) unless provider == "twitter"
return "#{auth.uid}#twitter.com" if provider == "twitter"
end
end
I create an email using the twitter uid with twitter.com being the domain since twitter does not return an email address
Hope this helps someone in the future

Related

Rails: Heroku with Omniauth and Facebook

I am working on an app that allows you to authenticate with Facebook to signup and log into the app. For some reason when working with multiple dynos, it appears that session management isn't carried over.
I can watch my logs log the user in, but when the user is redirected, the app logs the user out for some reason
Here is my AuthorizationsController:
class AuthorizationsController < ApplicationController
skip_before_filter :redirect_to_signed_in_path, :prepare_for_mobile, :redirect_to_https, :force_www!
def create
authentication = Authorization.find_by_provider_and_uid(auth['provider'], auth['uid'])
if authentication
flash[:notice] = "Signed In Successfully"
sign_in authentication.user, event: :authentication
redirect_to root_path
elsif user_signed_in?
current_user.apply_omniauth(auth)
if current_user.save
current_user.update_attribute(:"allow_#{auth['provider']}_sync", true)
PullSocialActionsWorker.perform_async(current_user.id, auth["provider"])
redirect_to edit_social_profile_path, flash: { error: "#{auth["provider"]} Integration is processing" }
else
redirect_to edit_social_profile_path, flash: { error: "An error has occurred. Please try again." }
end
else
password = Devise.friendly_token[0,20]
athlete = Athlete.new(email: generate_auth_email(params[:provider]), password: password, password_confirmation: password )
athlete.apply_omniauth(auth)
begin
athlete.subscriptions.build(trial_expiry: DateTime.now + 30, active: true, account_type_id: AccountType.free.id)
if athlete.save(validate: false)
sign_in athlete, event: :authentication
redirect_to root_path, notice: "Account created and signed in successfully"
else
redirect_to root_path, flash: { error: "An error has occurred. Please try again." }
end
rescue ActiveRecord::RecordNotUnique
redirect_to root_path, flash: { error: "The email address you are trying to connect already exists. Perhaps you #{ActionController::Base.helpers.link_to "Forgot Your Password?", new_user_password_path}".html_safe }
end
end
end
def failure
redirect_to root_url, notice: "An Error has occurred. Please try again!"
end
private
def auth
request.env["omniauth.auth"]
end
def generate_auth_email(provider)
if provider == "twitter"
"#{auth.uid}#twitter.com"
else
auth.info.try(:email)
end
end
end
Gemfile:
gem 'omniauth'
gem 'omniauth-facebook', '1.4.0'
I am also using Memcache/Dalli for caching...
gem 'memcachier'
gem 'dalli'
Anyone else run into this issue before?

Devise and Omniauth

I'm trying to integrate Devise with Omniauth and have some troubles with them both. The main problem is to bind Devise User model with Omniauth authentications. I want a simple way to associate my user, with external providers like facebook, twitter, g+ and so on.
One of the most annoying issues that arise with my application is:
If an user registered on my site with devise (I call it local user), that means, provided an email and a password, when user tries to login with twitter, the system asks for mail confirmation. If that mail already exists, then user have to provide another mail. I want instead that he can confirm with a password that it is actually his email. How to do that? How can I override that template?
This is my authentications controller:
class AuthenticationsController < ApplicationController
def index
#authentications = Authentication.all
end
def create
#authentication = Authentication.new(params[:authentication])
if #authentication.save
redirect_to authentications_url, :notice => "Successfully created authentication."
else
render :action => 'new'
end
end
def destroy
#authentication = Authentication.find(params[:id])
#authentication.destroy
redirect_to authentications_url, :notice => "Successfully destroyed authentication."
end
def twitter
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 current_user
token = omni['credentials'].token
token_secret = omni['credentials'].secret
current_user.authentications.create!(:provider => omni['provider'], :uid => omni['uid'], :token => token, :token_secret => token_secret)
flash[:notice] = "Authentication successful."
sign_in_and_redirect current_user
else
user = User.new
user.apply_omniauth(omni)
if user.save
flash[:notice] = "Logged in."
sign_in_and_redirect User.find(user.id)
else
session[:omniauth] = omni.except('extra')
p session
redirect_to new_user_registration_path
end
end
end
end
I also have no idea where new_users_registration_path is.

Linked In authentication and aggregate data

I'm building a web app with Ruby on Rails and I want my users to authenticate and aggregate data from Linked In (and others like Github, Twitter, etc...).
I am using these gems:
Devise for registration
omniauth-linkedin for authentication
pengwynn/linkedin for data aggregation
Though, Linked In has a not-so-nice pin thing.
Is there a way to avoid it and get the data I want from my users account without having them to go to linked in, fetch a pin and submit it to me?
Thanks in advance.
authentications_controller.rb
class AuthenticationsController < ApplicationController
def index
#authentications = current_user.authentications if current_user
end
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'])
current_user.apply_omniauth(omniauth)
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(:user, user)
redirect_to user_path(user.username)
else
session[:omniauth] = omniauth.except('extra')
redirect_to new_user_registration_url
end
end
end

Authlogic with OAuth and OpenID - DoubleRenderError

I have authlogic and openid working correctly, and am trying to integrate oauth for twitter authentication. I do not want both a register and sign on button, so I have followed this example: Implicit user creation with Authlogic and Authlogic OAuth plugin
I get the DoubleRenderError on initial registration and subsequent log in, but refreshing the page lets the action complete successfully.
My users_controller create:
def create
#user = User.new(params[:user])
#user.save do |result| # LINE A
if result
flash[:notice] = "Account registered!"
redirect_to account_url
else
unless #user.oauth_token.nil?
#user = User.find_by_oauth_token(#user.oauth_token)
unless #user.nil?
UserSession.create(#user)
flash.now[:message] = "Welcome back!"
redirect_to account_url
else
redirect_back_or_default root_path
end
else
redirect_back_or_default root_path
end
end
end
end
And my user_sessions_controller create:
def create
#user_session = UserSession.new(params[:user_session])
#user_session.save do |result|
if result
flash[:notice] = "Login successful!"
redirect_back_or_default account_url
else
render :action => :new
end
end
end
Is there a way to resolve this? Thanks
After repeated failures, the following appears to work for normal authlogic username/password, OAuth with Twitter, and OpenID for at least google and yahoo, which is all I was interested in
def create
#user = User.new(params[:user])
#user.save do |result| # LINE A
if result
flash[:notice] = "Account registered!"
redirect_to account_url and return
else
if #user.oauth_token
#user = User.find_by_oauth_token(#user.oauth_token)
UserSession.create(#user)
flash.now[:message] = "Welcome back!"
redirect_to account_url and return
else
flash[:notice] = "Something went awry. Perhaps the name or email is already in use."
redirect_to register_path and return
end
end
end
end
Additionally, i added 'and return' into the update block in my users controller after both success and failure redirects/renders

Authentication Problem - not recognizing 'else' - Ruby on rails

I can't seem to figure out what I am doing wrong here. I have implemented the Super Simple Authentication from Ryan Bates tutorial and while the login portion is functioning correctly, I can't get an error message and redirect to happen correctly for a bad login.
Ryan Bates admits in his comments he left this out but can't seem to implement his recommendation. Basically what is happening is that when someone logs in correctly it works. When a bad password is entered it does the same redirect and flashes 'successfully logged in' thought they are not. The admin links do not show (which is correct and are the links protected by the <% if admin? %>) but I need it to say 'failed login' and redirect to login path. Here is my code:
SessionsController
class SessionsController < ApplicationController
def create
if
session[:password] = params[:password]
flash[:notice] = 'Successfully logged in'
redirect_to posts_path
else
flash[:notice] = "whoops"
redirect_to login_path
end
end
def destroy
reset_session
flash[:notice] = 'Successfully logged out'
redirect_to posts_path
end
end
ApplicationController
class ApplicationController < ActionController::Base
helper_method :admin?
protected
def authorize
unless admin?
flash[:error] = "unauthorized request"
redirect_to posts_path
false
end
end
def admin?
session[:password] == "123456"
end
helper :all # include all helpers, all the time
protect_from_forgery # See ActionController::RequestForgeryProtection for details
#
end
You need to use Ruby's comparison operator == rather than the assignment operator =. Your create action should be:
def create
if session[:password] == params[:password]
flash[:notice] = 'Successfully logged in'
redirect_to posts_path
else
flash[:notice] = "whoops"
redirect_to login_path
end
end
Edit: The problem is that nowhere in your SessionsController are you actually checking the entered password against the correct password. Change your create method to this:
def create
if params[:password] == '123456'
session[:password] = params[:password]
flash[:notice] = 'Successfully logged in'
redirect_to posts_path
else
flash[:notice] = "whoops"
redirect_to login_path
end
end
It's not ideal having the password hard-coded like this and storing it in the session for use by the admin? helper method, but this is supposed to be super simple authentication.
if #YOU MISSING SOMETHING HERE WHICH Returns TRUE IF USER IS VALID
session[:password] = session[:password]
flash[:notice] = 'Successfully logged in'
redirect_to posts_path
else
flash[:notice] = "invalid login" #CHange if messaage for invalid login
redirect_to login_path
end
it must be
if session[:password] == params[:password]
You never have a fail condition due to:
if session[:password] = session[:password]
This will always be true. You probably want something like:
if session[:password] == 'canihazpasswrd' then
do_something_here
Edit: Refer #john's answer. :)
Try this:
def create
if session[:password] == '123456'
flash[:notice] = 'Succesfully logged in'
redirect_to home_path
else
flash[:notice] = "Incorrect Password!"
redirect_to login_path
end
end
The thing is that the tutorial you used does no user's authentication. It only checks if the login belongs to an admin, so some content will be showed.
This way you'll never have wrong login/password, just admin/non-admin.

Resources