всех, сейчас я делаю одно приложение и немного запутался, был бы признателен за вашу помощь. Для аутентификации я выбрал Devise gem и oauth2 для входа в социальные сети.
Проблема, которую я не могу решить, заключается в том, что я могу зарегистрироваться либо по почте, либо с помощью какого-либо входа в социальную сеть. Я пытаюсь сделать так, чтобы один пользователь мог войти в систему, используя любой из доступных учетных записей социальных сетей (поставщиков) и электронную почту. А если, например, пользователь уже был зарегистрирован по почте, разрешить ему авторизоваться через социальный логин выбранной системы (провайдера).
Также в будущем, если эта задача будет решена, я планирую дать возможность добавить или удалить социальную сеть при редактировании пользователем информации о себе (если это важно, в текущем контексте).
Мой код на данный момент:
routes.rb
Rails.application.routes.draw do
root 'pages#home'
devise_for :users, controllers: { omniauth_callbacks: 'users/omniauth_callbacks' }
end
user.rb
class User < ApplicationRecord
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable, :trackable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :validatable,
:confirmable, :omniauthable, omniauth_providers: %i[github google_oauth2 facebook twitter linkedin gitlab]
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
user.image = auth.info.image
user.skip_confirmation!
end
end
def self.new_with_session(params, session)
super.tap do |user|
if data = session["devise.github"] && session["devise.github_data"]["extra"]["raw_info"]
user.email = data["email"] if user.email.blank?
end
if data = session["devise.google_oauth2"] && session["devise.google_oauth2_data"]["extra"]["raw_info"]
user.email = data["email"] if user.email.blank?
end
if data = session["devise.facebook"] && session["devise.facebook_data"]["extra"]["raw_info"]
user.email = data["email"] if user.email.blank?
end
if data = session["devise.twitter"] && session["devise.twitter_data"]["extra"]["raw_info"]
user.email = data["email"] if user.email.blank?
end
if data = session["devise.linkedin"] && session["devise.linkedin_data"]["extra"]["raw_info"]
user.email = data["email"] if user.email.blank?
end
if data = session["devise.gitlab"] && session["devise.gitlab_data"]["extra"]["raw_info"]
user.email = data["email"] if user.email.blank?
end
end
end
end
omniauth_callbacks_controller.rb
class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController
def github
@user = User.from_omniauth(request.env["omniauth.auth"])
if @user.persisted?
sign_in_and_redirect @user, event: :authentication
set_flash_message(:notice, :success, kind: "Github") if is_navigational_format?
else
session["devise.github_data"] = request.env["omniauth.auth"]
redirect_to new_user_registration_url, alert: @user.errors.full_messages.join("\n")
end
end
def google_oauth2
@user = User.from_omniauth(request.env['omniauth.auth'])
if @user.persisted?
sign_in_and_redirect @user, event: :authentication
set_flash_message(:notice, :success, kind: "Google") if is_navigational_format?
else
session['devise.google_data'] = request.env['omniauth.auth'].except(:extra)
redirect_to new_user_registration_url, alert: @user.errors.full_messages.join("\n")
end
end
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'].except(:extra)
redirect_to new_user_registration_url, alert: @user.errors.full_messages.join("\n")
end
end
def twitter
@user = User.from_omniauth(request.env['omniauth.auth'])
if @user.persisted?
sign_in_and_redirect @user, event: :authentication
set_flash_message(:notice, :success, kind: "Twitter") if is_navigational_format?
else
session['devise.twitter_data'] = request.env['omniauth.auth'].except(:extra)
redirect_to new_user_registration_url, alert: @user.errors.full_messages.join("\n")
end
end
def linkedin
@user = User.from_omniauth(request.env['omniauth.auth'])
if @user.persisted?
sign_in_and_redirect @user, event: :authentication
set_flash_message(:notice, :success, kind: "Linkedin") if is_navigational_format?
else
session['devise.linkedin_data'] = request.env['omniauth.auth'].except(:extra)
redirect_to new_user_registration_url, alert: @user.errors.full_messages.join("\n")
end
end
def gitlab
@user = User.from_omniauth(request.env["omniauth.auth"])
if @user.persisted?
sign_in_and_redirect @user, event: :authentication
set_flash_message(:notice, :success, kind: "Gitlab") if is_navigational_format?
else
session["devise.gitlab_data"] = request.env["omniauth.auth"]
redirect_to new_user_registration_url, alert: @user.errors.full_messages.join("\n")
end
end
def failure
flash[:error] = 'There was a problem signing you in. Please register or try signing in later.'
redirect_to root_path
end
end
Мой Gemfile
source 'https://rubygems.org'
git_source(:github) { |repo| "https://github.com/#{repo}.git" }
ruby '2.7.1'
gem 'rails', '~> 6.0.3', '>= 6.0.3.1'
gem 'pg', '>= 0.18', '< 2.0'
gem 'puma', '~> 4.1'
gem 'sass-rails', '>= 6'
gem 'webpacker', '~> 4.0'
gem 'turbolinks', '~> 5'
gem 'jbuilder', '~> 2.7'
gem 'bootsnap', '>= 1.4.2', require: false
gem 'devise'
gem 'omniauth-github'
gem 'omniauth-gitlab'
gem 'omniauth-twitter'
gem 'omniauth-facebook'
gem 'omniauth-google-oauth2'
gem 'omniauth-linkedin-oauth2'
gem 'activerecord-session_store'
group :development, :test do
gem 'dotenv-rails'
gem 'letter_opener'
gem 'byebug', platforms: [:mri, :mingw, :x64_mingw]
end
group :development do
# Access an interactive console on exception pages or by calling 'console' anywhere in the code.
gem 'web-console', '>= 3.3.0'
gem 'listen', '~> 3.2'
# Spring speeds up development by keeping your application running in the background. Read more: https://github.com/rails/spring
gem 'spring'
gem 'spring-watcher-listen', '~> 2.0.0'
end
# Windows does not include zoneinfo files, so bundle the tzinfo-data gem
gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby]
schema.rb
ActiveRecord::Schema.define(version: 2020_06_04_090555) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
create_table "sessions", force: :cascade do |t|
t.string "session_id", null: false
t.text "data"
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
t.index ["session_id"], name: "index_sessions_on_session_id", unique: true
t.index ["updated_at"], name: "index_sessions_on_updated_at"
end
create_table "users", force: :cascade do |t|
t.string "email", default: "", null: false
t.string "encrypted_password", default: "", null: false
t.string "reset_password_token"
t.datetime "reset_password_sent_at"
t.datetime "remember_created_at"
t.string "confirmation_token"
t.datetime "confirmed_at"
t.datetime "confirmation_sent_at"
t.string "unconfirmed_email"
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
t.string "name"
t.string "image"
t.string "provider"
t.string "uid"
t.index ["confirmation_token"], name: "index_users_on_confirmation_token", unique: true
t.index ["email"], name: "index_users_on_email", unique: true
t.index ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true
end
end