Search code examples
ruby-on-railsvue.jsnuxt.jsomniauth

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'))
  }

Solution

  • A couple of things...

    1. 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.
    2. 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.
    3. Run rake routes to see if the routes you have defined are what you're expecting.
    4. If you can't figure it out, I also created a project using Omniauth and devise. You can view my code here.