How am I supposed to use Devise and Omniaauth in Database Mongodb? - ruby-on-rails

I did look Ryan Bates episodes to use devise with omniauth. Problem is that I am able to sign up with linkdin. My code
In my user.rb
field :provider, :type => String
field :uid, :type => String
field :name, :type => String
#has_many :authentications
def self.from_omniauth(auth)
where(auth.slice("provider", "uid")).first || create_from_omniauth(auth)
end
def self.create_from_omniauth(auth)
create! do |user|
user.provider = auth["provider"]
user.uid = auth["uid"]
user.name = auth["info"]["nickname"]
end
end
I add this and in my create controller for authentication I did
user = User.from_omniauth(env["omniauth.auth"])
session[:user_id] = user.id
redirect_to root_url, notice: "Signed in!"
I am succeded to put the value of linkdin in my user database as
{ "_id" : ObjectId("50b2f4e66d7ab88ac7000003"), "email" : "", "encrypted_password" : "", "sign_in_count" : 0, "provider" : "linkedin", "uid" : "wuBFLcbDyB", "name" : null, "updated_at" : ISODate("2012-11-26T04:49:42.549Z"), "created_at" : ISODate("2012-11-26T04:49:42.549Z") }
But as I login from linkdin it does not signup through linkdin else it redirects to
http://localhost:3000/users/sign_in
How can I login through that linkdin?

If you have something like this in your user model
validates :username, presence: true
Then you must know that linked in does not provide you any username. Since that, to complete your authentication / registration, your user has to add explicitly his username.
Make sure that your registrations_contreoller.rb looks like this
class RegistrationsController < Devise::RegistrationsController
def create
super
end
private
def build_resource(*args)
super
if session[:omniauth]
#user.apply_omniauth(session[:omniauth])
#user.valid?
end
end
end

Related

Rails API Omniauth

I am trying to implement Omniauth with Devise in Rails API with NuxtJS framework.
I did auth module connexion and user account creation with Omniauth method but i would like understand how redirect the user afer signin/signup, i am Rails developer and beginner with NuxtJS.
BACKEND
User model oauth registration method:
def self.from_facebook(auth)
where(uid: auth.uid, provider: auth.provider).first_or_create do |user|
user.email = auth.info.email
user.first_name = auth.info.first_name
user.last_name = auth.info.last_name
user.password = Devise.friendly_token[0, 20]
user.provider = auth.provider
user.uid = auth.uid
Client.create(user: user)
end
end
Registration controller:
# frozen_string_literal: true
module Overrides
class RegistrationsController < DeviseTokenAuth::ApplicationController
before_action :set_user_by_token, only: [:destroy, :update]
before_action :validate_sign_up_params, only: :create
before_action :validate_account_update_params, only: :update
skip_after_action :update_auth_header, only: [:create, :destroy]
def create
build_resource
unless #resource.present?
raise DeviseTokenAuth::Errors::NoResourceDefinedError,
"#{self.class.name} #build_resource does not define #resource,"\
' execution stopped.'
end
# give redirect value from params priority
#redirect_url = params.fetch(
:confirm_success_url,
DeviseTokenAuth.default_confirm_success_url
)
# success redirect url is required
if confirmable_enabled? && !#redirect_url
return render_create_error_missing_confirm_success_url
end
# if whitelist is set, validate redirect_url against whitelist
return render_create_error_redirect_url_not_allowed if blacklisted_redirect_url?
# override email confirmation, must be sent manually from ctrl
resource_class.set_callback('create', :after, :send_on_create_confirmation_instructions)
resource_class.skip_callback('create', :after, :send_on_create_confirmation_instructions)
if #resource.respond_to? :skip_confirmation_notification!
# Fix duplicate e-mails by disabling Devise confirmation e-mail
#resource.skip_confirmation_notification!
end
if #resource.save
if params[:farmer]
Farmer.create(
user: #resource
)
else
Client.create(
user: #resource
)
end
yield #resource if block_given?
unless #resource.confirmed?
# user will require email authentication
#resource.send_confirmation_instructions({
client_config: params[:config_name],
redirect_url: #redirect_url
})
end
if active_for_authentication?
# email auth has been bypassed, authenticate user
#client_id, #token = #resource.create_token
#resource.save!
update_auth_header
end
render_create_success
else
clean_up_passwords #resource
render_create_error
end
end
def update
if #resource
if #resource.send(resource_update_method, account_update_params)
yield #resource if block_given?
render_update_success
else
render_update_error
end
else
render_update_error_user_not_found
end
end
def destroy
if #resource
#resource.destroy
yield #resource if block_given?
render_destroy_success
else
render_destroy_error
end
end
def sign_up_params
params.permit(
:first_name,
:last_name,
:email,
:cellphone,
:phone,
:password,
:password_confirmation,
:birthdate
)
end
def account_update_params
params.permit(*params_for_resource(:account_update))
end
protected
def build_resource
#resource = resource_class.new(sign_up_params)
#resource.provider = provider
# honor devise configuration for case_insensitive_keys
if resource_class.case_insensitive_keys.include?(:email)
#resource.email = sign_up_params[:email].try(:downcase)
else
#resource.email = sign_up_params[:email]
end
end
def render_create_error_missing_confirm_success_url
response = {
status: 'error',
data: resource_data
}
message = I18n.t('devise_token_auth.registrations.missing_confirm_success_url')
render_error(422, message, response)
end
def render_create_error_redirect_url_not_allowed
response = {
status: 'error',
data: resource_data
}
message = I18n.t('devise_token_auth.registrations.redirect_url_not_allowed', redirect_url: #redirect_url)
render_error(422, message, response)
end
def render_create_success
render json: {
status: 'success',
data: resource_data
}
end
def render_create_error
render json: {
status: 'error',
data: resource_data,
errors: resource_errors
}, status: 422
end
def render_update_success
render json: {
status: 'success',
data: resource_data
}
end
def render_update_error
render json: {
status: 'error',
errors: resource_errors
}, status: 422
end
def render_update_error_user_not_found
render_error(404, I18n.t('devise_token_auth.registrations.user_not_found'), status: 'error')
end
def render_destroy_success
render json: {
status: 'success',
message: I18n.t('devise_token_auth.registrations.account_with_uid_destroyed', uid: #resource.uid)
}
end
def render_destroy_error
render_error(404, I18n.t('devise_token_auth.registrations.account_to_destroy_not_found'), status: 'error')
end
private
def resource_update_method
if DeviseTokenAuth.check_current_password_before_update == :attributes
'update_with_password'
elsif DeviseTokenAuth.check_current_password_before_update == :password && account_update_params.key?(:password)
'update_with_password'
elsif account_update_params.key?(:current_password)
'update_with_password'
else
'update_attributes'
end
end
def validate_sign_up_params
validate_post_data sign_up_params, I18n.t('errors.messages.validate_sign_up_params')
end
def validate_account_update_params
validate_post_data account_update_params, I18n.t('errors.messages.validate_account_update_params')
end
def validate_post_data which, message
render_error(:unprocessable_entity, message, status: 'error') if which.empty?
end
def active_for_authentication?
!#resource.respond_to?(:active_for_authentication?) || #resource.active_for_authentication?
end
end
end
Omniauth callbacks controller:
def facebook
#user = User.from_facebook(request.env["omniauth.auth"])
# NOTE: redirection here
end
FRONTEND
Stategie:
facebook: {
client_id: 'CLIENT_ID',
userinfo_endpoint: 'https://graph.facebook.com/v2.12/me?fields=about,name,picture{url},email,birthday',
redirect_uri:'http://localhost:3000/omniauth/facebook',
scope: ['public_profile', 'email', 'user_birthday']
}
Login method:
facebookLogin () {
this.$auth.loginWith('facebook')
.then((response) => {
this.$toast.success({
title: 'Connexion réussie',
message: 'Vous vous êtes bien connecté.',
position: 'bottom center',
timeOut: 3000
})
})
.catch(() => {
this.$toast.error({
title: 'Erreur',
message: 'L\'email ou le mot de passe ne sont pas valides. Vérifiez votre saisie.',
position: 'bottom center',
timeOut: 8000
})
})
.finally(() => this.$wait.end('signing in'))
}
A couple of things...
Omniauth callbacks controller is missing the redirect information (is that why that note is there?). If you're using Devise, it should say something like sign_in_and_redirect #user underneath the #user = ... line.
Devise comes with built in routes. To use them, you must include something like, devise for :users in your routes.rb file. Check out the "Devise_for magic" section on this page to see an example of these built in routes. Note that you have to have some Devise models configured for this to work.
Run rake routes to see if the routes you have defined are what you're expecting.
If you can't figure it out, I also created a project using Omniauth and devise. You can view my code here.

Create different users in Ruby with iteration

I'm new in Ruby. I want to create different users in ruby using iteration.
def createuser(*args)
obj = H['userClass']
obj.login = H['login']
obj.password = a.password = #default_passwd
obj.email = 'test#example.com'
obj.role = MasterUser::ROLE_MASTER_USER
end
For example I want to call this method and send these arguments:
H = Hash["userClass" => MasterUser.new, "login" => admin]
createuser(H)
What is the proper way to implement this?
Here's a modified version. It should bring you closer to your goal, while still being recognizable :
def create_user(parameters)
klass = parameters['user_class']
user = klass.new
user.login = parameters['login']
user.password = #default_passwd
user.email = 'test#example.com'
user.role = klass::ROLE_MASTER_USER
user
end
user_params = {"user_class" => MasterUser, "login" => 'admin'}
new_user = create_user(user_params)
I'd probably do something like this:
class UserFactory
attr_accessor :user
def initialize(klass)
#user = klass.new
end
def create(params = {})
user.login = params.fetch :login
user.password = params.fetch :password, 'default_password'
user.email = params.fetch :email
# user.role should just be initialised on the klass.new call, no need to do that here
# etc...
end
end
class MasterUser
ROLE = 'master_role'
attr_accessor :login, :password, :email, :role
def initialize
self.role = ROLE
end
end
which you would call like:
UserFactory.new(MasterUser).create(login: 'george', password: 'secret', email: 'me#george.com')
The reason I'd use params.fetch :login, instead of just reading it, is that in Ruby accessing a hash by a key that it doesn't have returns nil, while trying to fetch it will throw an error.
For example:
a = {}
a[:foo] #=> nil
a.fetch :foo #=> throw a KeyError
So that is a way of enforcing that the argument hash has the right keys.

Rails 4 user model will not save with hash data from oauth

I am trying to add oauth to an existing login for my project, but when I retrieve the hash data and try to save the user params with user.save!, the validation rules fail.
user.rb
has_secure_password
validates :email, :format => { :with => /\A[^# ]+#[^# ]+\.[^# ]+\Z/ },
:uniqueness => { :case_sensitive => false }
validates :password, :presence => true, :on => :create
validates :username,
:format => { :with => /\A[A-Za-z0-9][A-Za-z0-9_-]{0,24}\Z/ },
:uniqueness => { :case_sensitive => false }
...
class << self
def from_omniauth(auth_hash)
user = find_or_create_by(uid: auth_hash['uid'], provider: auth_hash['provider'])
user.name = auth_hash['info']['name']
user.email = auth_hash['info']['email']
user.username = auth_hash['info']['email']
user.save!
user
end
end
The output of user.errors.full_messages gives me ["Password can't be blank", "Password can't be blank", "Email is invalid", "Username is invalid"].
What I don't understand is why the validations are failing if the data parameters have been defined (i.e. user.email) and hold the correct values.
What am I missing?
your problem is the find_or_create_by method.
this will looking for the user with uid and provider otherwise try to create it.
but without vaild username, and so on, it will always fail if there is no user with uid and provider
update:
you try to find a user with an uid and a provider. if
find_or_create_by find a valid user, it will return it. with this
you can update the data.
BUT if find_or_create_by did not find a valid user, it will create a user with the given parameter uid and provider. but to
create a valid user, your model needs a valid username, a valid
password, and a valid email
you could do something like this
def from_omniauth(auth_hash)
user = User.find_by(uid: auth_hash['uid'], provider:auth_hash['provider']) || User.new(uid: auth_hash['uid'], provider:auth_hash['provider'], password: 'your password methods')
user.name = auth_hash['info']['name']
user.email = auth_hash['info']['email']
user.username = auth_hash['info']['email']
user.save!
user
end

Undefined Wrapper Method

I'm having a trouble understanding why I cannot successfully call my wrapper method in my User model. My problem lies with this line self.password_hash = hash_check(password, password_salt) in the encrypt_password method shown below. My hash_check method works correctly in the authenticate method so I'm a bit stumped. When I run my test I get the error undefined method 'hash_check' for #<User:0x007f94851a5f88>
class User < ActiveRecord::Base
attr_accessor :password
before_save :encrypt_password
validates_confirmation_of :password
validates_presence_of :password, :on => :create
validates_presence_of :email
validates_uniqueness_of :email
def encrypt_password
if password.present?
self.password_salt = generate_salt
self.password_hash = hash_check(password, password_salt)
end
end
def self.authenticate(email, password)
user = find_user(email)
user && user.password_hash == hash_check(password, user.password_salt) ? user : nil
end
def self.find_user(email)
user = find_by_email(email)
end
private
def self.hash_check(password, password_salt)
BCrypt::Engine.hash_secret(password, password_salt)
end
def generate_salt
BCrypt::Engine.generate_salt
end
end
require 'spec_helper'
describe 'User' do
let(:user) { User.create(email: 'user#gmail.com', password: "secure_password") }
it 'creates a user object' do
user.should be_kind_of User
end
describe '.find_user' do
let(:valid_user_search) { User.find_user('user#gmail.com') }
let(:invalid_user_search) { User.find_user('notregistered#gmail.com') }
it 'returns a user by their email' do
user.should == valid_user_search
end
it 'return nil if no user if found' do
invalid_user_search.should == nil
end
end
describe '.authenticate' do
let(:auth_user) { User.authenticate(user.email, user.password) }
let(:non_auth_user) { User.authenticate('notregistered#gmail.com', 'invalidpass') }
it 'returns an valid user' do
auth_user.should == user
end
it 'returns nil on an invalid user' do
non_auth_user.should == nil
end
end
describe '.encrypt_password' do
it 'returns a password salt'
it 'return a password hash'
end
end
self.hash_check is a class method (because you put self). It works in self.authenticate because it is also a class method, (as it doesn't rely on an instance). HOWEVER, it won't work on an instance method like encrypt_password because you are not invoking the class method at all.
So you are going to need to replace hash_check(password, password_salt) in your instance method with self.class.hash_check(password, password_salt) or User.hash_check(password, password_salt) to be able to use a class method
Read more about the nuances here

Create embedded document on object creation in Mongoid

Let's say I have these:
class User
include Mongoid::Document
field :name, type: String
field :email, type: String
embeds_many :auths
attr_protected :name, :email
end
class Auth
include Mongoid::Document
field :provider
field :uid
embedded_in :user
attr_protected :provider, :uid
end
I create a new user using this method:
def self.create_with_omniauth(auth)
create! do |user|
user.auths.build(provider: auth['provider'], uid: auth['uid'])
if auth['info']
user.name = auth['info']['name'] || ''
user.email = auth['info']['email'] || ''
end
end
end
However, when I look into my database, the result is this:
{
"_id" : ObjectId("517f5f425aca0fbf3a000007"),
"name" : "User",
"email" : "mail#example.com",
"auths" : [
{
"_id" : ObjectId("517f5f425aca0fbf3a000008")
}
]
}
What do I have to do in order to actually save the data provided? The uid and the provider are always properly in the auth array, I checked.
Currently attributes are just skipped since that's what you tell Rails
Either change:
attr_protected :provider, :uid
to:
attr_accessible :provider, :uid
or proceed as follows:
user.auths.build.tap do |user_auth|
user_auth.provider = auth['provider']
user_auth.uid = auth['uid']
end
Can you try this?
def self.create_with_omniauth(auth)
create! do |user|
auth = Auth.build(provider: auth['provider'], uid: auth['uid'])
if auth['info']
user.name = auth['info']['name'] || ''
user.email = auth['info']['email'] || ''
end
user.auths << auth
end
end

Resources