-
Notifications
You must be signed in to change notification settings - Fork 55
[#325] Authentication via Social Networks #360
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Closed
ruslankhaertdinov
wants to merge
10
commits into
fs:examples
from
ruslankhaertdinov:authentication-via-social-networks
Closed
Changes from all commits
Commits
Show all changes
10 commits
Select commit
Hold shift + click to select a range
caa4d0d
[#325] Authentication via Social Networks
fd23292
update .ruby-verison
8d6d669
apply review comments: move check_omniauth content into policy object…
e4e8549
try to add services
cf69813
apply review comments: extract omniauth logic into separate services
1407201
rework oauth_organizer and fetch_oauth_user classes
2690d11
extend user_spec
95fa19c
apply review comments: revise code
11a7531
apply review comments: refactor feature specs
5552594
apply review comments: extract user#connect_social_profile into servi…
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1 +1 @@ | ||
| 2.2.2 | ||
| 2.2.3 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,42 @@ | ||
| class OmniauthCallbacksController < Devise::OmniauthCallbacksController | ||
| include OmniauthHelper | ||
|
|
||
| expose(:user) { OauthOrganizer.new(current_user, auth_hash).call } | ||
|
|
||
| SocialProfile::PROVIDERS.each do |provider| | ||
| define_method(provider.to_s) do | ||
| begin | ||
| handle_user | ||
| rescue OauthOrganizer::OauthError | ||
| handle_error | ||
| end | ||
| end | ||
| end | ||
|
|
||
| def after_sign_in_path_for(_resource) | ||
| edit_user_registration_path | ||
| end | ||
|
|
||
| private | ||
|
|
||
| def auth_hash | ||
| request.env["omniauth.auth"] | ||
| end | ||
|
|
||
| # rubocop:disable Metrics/AbcSize | ||
| def handle_user | ||
| if user.persisted? | ||
| sign_in_and_redirect user, event: :authentication | ||
| set_flash_message(:notice, :success, kind: "#{provider_name(auth_hash.provider)}") if is_navigational_format? | ||
| else | ||
| session[:omniauth] = auth_hash.except("extra") | ||
| redirect_to new_user_registration_url | ||
| end | ||
| end | ||
| # rubocop:enable Metrics/AbcSize | ||
|
|
||
| def handle_error | ||
| redirect_to new_user_session_path, | ||
| notice: t("omniauth.verification.failure", kind: provider_name(auth_hash.provider)) | ||
| end | ||
| end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,13 @@ | ||
| class RegistrationsController < Devise::RegistrationsController | ||
| def create | ||
| super | ||
| session[:omniauth] = nil unless @user.new_record? | ||
| end | ||
|
|
||
| private | ||
|
|
||
| def build_resource(*args) | ||
| super | ||
| @user.apply_omniauth(session[:omniauth]) if session[:omniauth] | ||
| end | ||
| end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,21 @@ | ||
| class SocialProfilesController < ApplicationController | ||
| before_action :authenticate_user! | ||
|
|
||
| expose(:social_profiles) { current_user.social_profiles } | ||
| expose(:social_profile) | ||
|
|
||
| def destroy | ||
| if social_profile.destroy | ||
| flash[:notice] = t "flash.actions.destroy.notice", resource_name: resource_name | ||
| else | ||
| flash[:alert] = t "flash.actions.destroy.alert", resource_name: resource_name | ||
| end | ||
| redirect_to edit_user_registration_url | ||
| end | ||
|
|
||
| private | ||
|
|
||
| def resource_name | ||
| SocialProfile.model_name.human | ||
| end | ||
| end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| module OmniauthHelper | ||
| def provider_name(provider) | ||
| t "active_record.attributes.social_profile.provider_name.#{provider}" | ||
| end | ||
| end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,23 @@ | ||
| class ConnectSocialProfile | ||
| attr_reader :user, :auth | ||
| private :user, :auth | ||
|
|
||
| def initialize(user, auth) | ||
| @user = user | ||
| @auth = auth | ||
| end | ||
|
|
||
| def call | ||
| if social_profile | ||
| social_profile.update_attribute(:user, user) | ||
| else | ||
| user.apply_omniauth(auth) && user.save | ||
| end | ||
| end | ||
|
|
||
| private | ||
|
|
||
| def social_profile | ||
| @social_profile ||= SocialProfile.from_omniauth(auth) | ||
| end | ||
| end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,22 @@ | ||
| class FetchOauthUser | ||
| attr_reader :auth | ||
| private :auth | ||
|
|
||
| def initialize(auth) | ||
| @auth = auth | ||
| end | ||
|
|
||
| def call | ||
| find_social_profile_user || find_user_by_email | ||
| end | ||
|
|
||
| private | ||
|
|
||
| def find_social_profile_user | ||
| SocialProfile.from_omniauth(auth).try(:user) | ||
| end | ||
|
|
||
| def find_user_by_email | ||
| User.find_by(email: auth["info"]["email"]) | ||
| end | ||
| end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,44 @@ | ||
| class OauthOrganizer | ||
| class OauthError < StandardError | ||
| end | ||
|
|
||
| attr_reader :auth, :current_user | ||
| private :auth, :current_user | ||
|
|
||
| def initialize(current_user, auth) | ||
| @current_user = current_user | ||
| @auth = auth | ||
| end | ||
|
|
||
| def call | ||
| user.present? ? connect_social_profile : fail_oauth | ||
| user | ||
| end | ||
|
|
||
| private | ||
|
|
||
| def user | ||
| @user ||= current_user || fetch_oauth_user || build_user | ||
| end | ||
|
|
||
| def fetch_oauth_user | ||
| FetchOauthUser.new(auth).call if auth_verified? | ||
| end | ||
|
|
||
| def auth_verified? | ||
| AuthVerificationPolicy.new(auth).verified? | ||
| end | ||
|
|
||
| def build_user | ||
| User.build_from_omniauth(auth) if auth_verified? | ||
| end | ||
|
|
||
| def connect_social_profile | ||
| ConnectSocialProfile.new(user, auth).call | ||
| end | ||
|
|
||
| def fail_oauth | ||
| fail OauthError, "Sorry, but yours #{auth.provider.titleize} failed verification. | ||
| Seems like yours #{auth.provider.titleize} account hasn't been verified." | ||
| end | ||
| end | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,12 @@ | ||
| class SocialProfile < ActiveRecord::Base | ||
| PROVIDERS = %i(facebook google_oauth2) | ||
|
|
||
| belongs_to :user | ||
|
|
||
| validates :user, :provider, :uid, presence: true | ||
| validates :uid, uniqueness: { scope: :provider } | ||
|
|
||
| def self.from_omniauth(auth) | ||
| find_by(provider: auth.provider, uid: auth.uid) | ||
| end | ||
| end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,14 +1,27 @@ | ||
| class User < ActiveRecord::Base | ||
| devise :database_authenticatable, :registerable, :confirmable, | ||
| :recoverable, :rememberable, :trackable, :validatable | ||
| :recoverable, :rememberable, :trackable, :validatable, | ||
| :omniauthable, omniauth_providers: SocialProfile::PROVIDERS | ||
|
|
||
| validates :full_name, presence: true | ||
|
|
||
| has_many :social_profiles, dependent: :destroy | ||
|
|
||
| def to_s | ||
| full_name | ||
| end | ||
|
|
||
| def full_name_with_email | ||
| "#{self[:full_name]} (#{email})" | ||
| end | ||
|
|
||
| def self.build_from_omniauth(auth) | ||
| new(email: auth["info"]["email"], full_name: auth["info"]["name"]) | ||
| end | ||
|
|
||
| def apply_omniauth(auth) | ||
| self.email = auth["info"]["email"] if email.blank? | ||
| self.full_name = auth["info"]["name"] if full_name.blank? | ||
| social_profiles.build(provider: auth["provider"], uid: auth["uid"]) | ||
| end | ||
| end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,32 @@ | ||
| class AuthVerificationPolicy | ||
| attr_reader :auth | ||
| private :auth | ||
|
|
||
| def initialize(auth) | ||
| @auth = auth | ||
| end | ||
|
|
||
| def verified? | ||
| request_verification_for | ||
| rescue NoMethodError | ||
| fail_with_error | ||
| end | ||
|
|
||
| private | ||
|
|
||
| def request_verification_for | ||
| send(auth.provider) | ||
| end | ||
|
|
||
| def fail_with_error | ||
| fail ArgumentError, I18n.t("omniauth.verification.not_implemented", kind: auth.provider) | ||
| end | ||
|
|
||
| def facebook | ||
| auth.info.verified? || auth.extra.raw_info.verified? | ||
| end | ||
|
|
||
| def google_oauth2 | ||
| auth.extra.raw_info.email_verified? | ||
| end | ||
| end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,14 @@ | ||
| - if social_profiles.any? | ||
| b Successfully authorized via: | ||
| ul.js-social-profiles | ||
| - social_profiles.each do |social_profile| | ||
| li = link_to "#{provider_name(social_profile.provider)} (#{social_profile.uid.truncate(9)}). Unauthorize?", | ||
| social_profile, | ||
| data: { confirm: "Are you sure you want to remove this social profile?" }, | ||
| method: :delete, | ||
| class: "js-unauthorize" | ||
|
|
||
| b Add service to sign in with: | ||
| ul | ||
| - SocialProfile::PROVIDERS.each do |provider| | ||
| li = link_to provider_name(provider), user_omniauth_authorize_path(provider) |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@MaximLarionov what do you think, should I leave it as is, or change logic and replace
if auth_verified?withif !user_found_by_email??There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hm, I doubt that we should build something from unverified auth, so auth verification should be kept