Статьи

Аутентификация Rails с OAuth 2.0 и OmniAuth

Это третья статья в серии Аутентификация с помощью Rails . Мы создали классические системы аутентификации по логину / паролю с такими функциями, как «Запомнить меня», «Сброс пароля», «Подтверждение электронной почты» и т. П.

Сегодня мы поговорим об аутентификации через социальные сети с помощью протокола OAuth 2 . Мы обсудим OmniAuth и четыре его стратегии: Twitter, Facebook, Google+ и LinkedIn, которые позволяют пользователям входить в любую сеть, которая им нравится. Я научу вас, как интегрировать каждую стратегию, настраивать ее и обрабатывать ошибки.

Исходный код этой статьи доступен на GitHub .

Работающую демонстрацию можно найти на сайте pointpoint-oauth2.herokuapp.com .

OAuth 2 и OmniAuth

OAuth 2 — это протокол авторизации, который позволяет сторонним приложениям получать ограниченный доступ к службе HTTP. Одним из основных аспектов этого протокола является токен доступа, который выдается приложению. Этот токен используется приложением для выполнения различных действий от имени пользователя. Однако он не может выполнить то, что не было одобрено (например, пользователь может разрешить приложению только получать информацию о друзьях, но не размещать его на стене пользователя).

Это здорово, потому что ни при каких обстоятельствах стороннее приложение не должно получать доступ к паролю пользователя. Он получает только специальный токен, срок действия которого истекает через некоторое время (обычно пользователи могут также отозвать доступ вручную) и может использоваться только для выполнения списка утвержденных задач, называемого областью действия .

Как идентифицировать стороннее приложение? Ну, если говорить о социальных сетях, приложение должно быть зарегистрировано в первую очередь. Разработчик предоставляет URL-адрес приложения, имя, контактные данные и другую информацию, которая будет видна пользователю. Затем поставщик авторизации (Twitter или Facebook) выдает пару ключей, которая однозначно идентифицирует приложение для социальной сети / провайдера.

Вот упрощенный обзор того, что происходит, когда пользователь посещает приложение и пытается пройти аутентификацию через социальную сеть:

  • Пользователь нажимает ссылку «Войти»
  • Пользователь перенаправлен на сайт социальной сети. Данные приложения ( client_id ) отправляются для идентификации
  • Пользователь видит данные приложения (например, имя, логотип, описание и т. Д.) И какие действия он хочет выполнить от его имени (область действия). Думайте об этом, как будто вы приходите к пользователю и говорите: «Эй, меня зовут Джек, и я хочу получить список всех ваших друзей! Если вы согласны, я покажу вам интересную статистику о них ».
  • Если пользователь не доверяет этому приложению, он просто отменяет авторизацию
  • Если пользователь доверяет приложению, авторизация утверждается, и пользователь перенаправляется обратно в приложение (через URL обратного вызова ). Информация об аутентифицированном пользователе и сгенерированном токене (иногда с секретным ключом) отправляется вместе.

Существует множество сервисов, которые поддерживают аутентификацию OAuth 2, поэтому для стандартизации процесса создания решений для аутентификации Intridea, Inc. был создан OmniAuth. Он поддерживает все, от OAuth 2 до LDAP. В этой статье мы сосредоточимся на omniauth-oauth2 , абстрактной стратегии OAuth 2. По сути, он используется как строительный блок для простого создания стратегий аутентификации. Вот огромный список всех стратегий, доступных для OmniAuth (он также включает в себя те, которые не связаны с OAuth 2).

Стандартный подход к стратегиям — это здорово, потому что вы можете интегрировать столько, сколько хотите, без проблем. Тем не менее, вы должны помнить, что некоторые социальные сети могут возвращать разные данные об аутентифицирующем пользователе, поэтому проверка работы вашего приложения абсолютно необходима (я покажу вам несколько примеров по этому поводу).

Если вы никогда не интегрировали OAuth 2 в приложение, это может показаться сложным, но не волнуйтесь, скоро это станет очень понятным! Приятно учиться на собственном примере, поэтому давайте погрузимся в код и создадим что-то необычное.

Подготовка демо-приложения

Хорошо, создайте простое приложение, позволяющее пользователям проходить аутентификацию через одну из представленных социальных сетей. Я звоню в мой SocialFreak:

 $ rails new SocialFreak -T 

Rails 4 будет использоваться для этой демонстрации, но Rails 3 должен работать так же хорошо.

Подключите Bootstrap к стилю приложения (необязательно):

Gemfile

 [...] gem 'bootstrap-sass' [...] 

и беги

 $ bundle install 

Импортируйте необходимые файлы:

таблицы стилей / application.css.scss

 @import "bootstrap-sprockets"; @import "bootstrap"; @import 'bootstrap/theme'; 

Изменить макет:

макеты / application.html.erb

 [...] <nav class="navbar navbar-inverse"> <div class="container"> <div class="navbar-header"> <%= link_to 'Social Freak', root_path, class: 'navbar-brand' %> </div> <div id="navbar"> <ul class="nav navbar-nav"> </ul> </div> </div> </nav> <div class="container"> <% flash.each do |key, value| %> <div class="alert alert-<%= key %>"> <%= value %> </div> <% end %> <%= yield %> </div> [...] 

Это устанавливает главное меню (без элементов) и область для отображения флеш-сообщений.

Создайте корневую страницу:

конфиг / routes.rb

 [...] root to: 'pages#index' [...] 

pages_controller.rb

 class PagesController < ApplicationController def index end end 

просмотров / страниц / html.erb

 <div class="jumbotron"> <h1>Welcome!</h1> <p>Authenticate via one of the social networks to get started.</p> </div> 

Аутентификация через Twitter

Добавление стратегии

Давайте использовать драгоценный камень omniauth-twitter, созданный Аруном Агравалом. Это одна из многочисленных стратегий для OmniAuth. Бросьте это в Gemfile:

Gemfile

 [...] gem 'omniauth-twitter' [...] 

и беги

 $ bundle install 

Конфигурация для всех стратегий OmniAuth находится в файле инициализатора omniauth.rb , поэтому создайте его сейчас:

конфиг / Инициализаторы / omniauth.rb

 Rails.application.config.middleware.use OmniAuth::Builder do provider :twitter, ENV['TWITTER_KEY'], ENV['TWITTER_SECRET'] end 

Регистрация новой стратегии (провайдера) заключается в предоставлении пары ключей для идентификации приложения провайдеру. Конечно, нам нужно получить эти два ключа, поэтому сделайте следующее:

  • Перейдите к apps.twitter.com
  • Нажмите «Создать новое приложение»
  • Заполнить форму. В качестве URL обратного вызова укажите адрес своего сайта плюс «/ auth / twitter / callback». Если вы находитесь на локальном компьютере, укажите «http: // localhost: 3000 / auth / twitter / callback». Мы обсудим этот URL обратного вызова в ближайшее время.
  • Нажмите «Создать».
  • Вы будете перенаправлены на информационную страницу приложения в Twitter. Перейдите на вкладку «Ключи и жетоны доступа».
  • Скопируйте Consumer Key и Consumer Secret и вставьте их в файл инициализатора.
  • Вы также можете просматривать другие вкладки, но для этой демонстрации мы не будем ничего менять. Например, в разделе «Права» вы можете указать, какие действия приложение сможет выполнять после аутентификации пользователя. Если вам необходимо, например, иметь возможность публиковать твиты от имени пользователя, вам необходимо соответствующим образом изменить разрешения. Это контролирует вышеупомянутый «объем» авторизации.

Обратите внимание, что вы можете дополнительно настроить эту стратегию, как описано здесь . Для этой демонстрации значения по умолчанию будут работать просто отлично.

Теперь вернемся к URL обратного вызова. Это URL, по которому пользователь будет перенаправлен внутрь приложения после успешной аутентификации и утвержденной авторизации (запрос также будет содержать данные пользователя и токен). Все стратегии OmniAuth предполагают, что URL обратного вызова равен « / авт /: поставщик / обратный вызов» , :provider берет название стратегии («twitter», «facebook», «linkedin» и т. д.), как указано в инициализаторе.

С этим знанием давайте настроим маршруты соответственно:

конфиг / routes.rb

 [...] get '/auth/:provider/callback', to: 'sessions#create' [...] 

и добавьте первую ссылку в наше главное меню:

макеты / application.html.erb

 [...] <ul class="nav navbar-nav"> <li><%= link_to 'Twitter', '/auth/twitter' %></li> </ul> [...] 

Обратите внимание, что маршрут /auth/twitter предоставляется стратегией и перенаправляет на страницу входа в Twitter.

Хеш аутентификации

Метод create внутри SessionsController проанализирует пользовательские данные, сохранит их в базе данных и выполнит вход в приложение. Тем не менее, как выглядят пользовательские данные? Это легко проверить:

sessions_controller.rb

 class SessionsController < ApplicationController def create render text: request.env['omniauth.auth'].to_yaml end end 

request.env['omniauth.auth'] содержит хэш аутентификации со всеми данными о пользователе.

Перезагрузите сервер и попробуйте пройти аутентификацию через Twitter. Вы увидите нечто подобное. Обратите внимание, что помимо вашего имени, местоположения, аватара и другой базовой информации, есть также некоторые специальные данные, такие как количество подписчиков и твитов. Конечно, эти данные будут различаться у разных провайдеров (и некоторые социальные сети по умолчанию отправляют только основную информацию).

Сохранение пользовательских данных

Теперь, когда вы знаете, как выглядит хеш аутентификации и какие данные можно получить, пришло время решить, какую информацию хранить. Поскольку мы хотим создать аутентификацию с использованием нескольких провайдеров, сохранение чего-то вроде количества твитов не является главным приоритетом (однако вы можете просто сериализовать и сохранить «лишнюю» часть хеш-кода аутентификации пользователя в отдельном поле).

Для этой демонстрации давайте сохраним следующее:

  • Название провайдера. Это абсолютно необходимо, потому что у нас будет несколько провайдеров.
  • Уникальный идентификатор пользователя. Этот идентификатор генерируется социальной сетью и может содержать различные символы. В некоторых сетях используются только цифры, а в других — буквы. Сочетание имени провайдера и идентификатора пользователя уникальным образом идентифицирует пользователя в нашем приложении.
  • Полное имя пользователя. Некоторые сети также предоставляют имя и фамилию отдельно (а некоторые, например, Twitter, также отдельные имя и псевдоним), но нам не нужна такая сложность.
  • Местоположение пользователя. Не все сети предоставляют это, но давайте исследуем, какие и в каком формате.
  • URL аватара пользователя. Мы будем отображать аватар пользователя в главном меню, поэтому оно должно быть довольно маленьким. К счастью, большинство социальных сетей позволяют выбирать один из нескольких доступных размеров. Используйте опцию image_size для Twitter, чтобы контролировать это (по умолчанию 48x48px, что нам подходит). Также обратите внимание, что по умолчанию для URL-адресов аватаров в качестве протокола используется http . Если вы хотите, чтобы все на вашей странице использовало https , многие социальные сети позволяют это. Например, в Twitter вы должны установить для secure_image_url значение true .
  • URL профиля пользователя. Каждая социальная сеть обеспечивает это. Однако во многих случаях ключ urls имеет вложенный хэш, например:

:urls => { :Website => 'http://example.com', :Twitter => "https://twitter.com/xxx" }

Мы не храним токен пользователя, потому что фактически не будем выполнять никаких действий от его имени. Если вам это нужно, создайте token и secret поля со string типом данных (некоторые социальные сети также предоставляют секрет). Используйте их при отправке запросов API к сервису.

Хорошо, теперь мы можем сгенерировать соответствующую миграцию:

 $ rails g model User provider:string uid:string name:string location:string image_url:string url:string 

Откройте файл миграции и измените его следующим образом:

xxx_create_users.rb

 [...] t.string :provider, null: false t.string :uid, null: false add_index :users, :provider add_index :users, :uid add_index :users, [:provider, :uid], unique: true [...] 

provider и uid не могут быть нулевыми и должны быть проиндексированы. add_index :users, [:provider, :uid], unique: true добавляет кластерный индекс к этим двум полям и обеспечивает уникальность их комбинации.

Применить миграцию:

 $ rake db:migrate 

Теперь переписать действие create

sessions_controller.rb

 [...] def create begin @user = User.from_omniauth(request.env['omniauth.auth']) session[:user_id] = @user.id flash[:success] = "Welcome, #{@user.name}!" rescue flash[:warning] = "There was an error while trying to authenticate you..." end redirect_to root_path end [...] 

from_omniauth — это еще не существующий метод, который будет анализировать хеш аутентификации и возвращать запись пользователя. Далее просто сохраните идентификатор пользователя внутри сессий и перенаправьте на главную страницу.

Настало время позаботиться о from_omniauth класса from_omniauth :

user.rb

 [...] 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.location = auth_hash['info']['location'] user.image_url = auth_hash['info']['image'] user.url = auth_hash['info']['urls']['Twitter'] user.save! user end end [...] 

find_or_create_by гарантирует, что мы не создаем одного и того же пользователя несколько раз. Метод сохраняет все необходимые данные, сохраняет пользователя и возвращает его. Если вам интересно, к токену пользователя обычно можно получить доступ как auth_hash['credentials']['token'] ( auth_hash['credentials']['secret'] для секрета).

Текущий пользователь и выход из системы

Нам нужен способ узнать, вошел ли пользователь в систему или нет. current_user — это метод, который по соглашению либо возвращает запись пользователя, либо nil :

application_controller.rb

 [...] private def current_user @current_user ||= User.find_by(id: session[:user_id]) end helper_method :current_user [...] 

helper_method :current_user гарантирует, что он также может быть вызван из представлений.

Отлично, теперь пользователь может войти, но не может выйти. Добавить ссылку выхода из системы:

макеты / application.html.erb

 [...] <nav class="navbar navbar-inverse"> <div class="container"> <div class="navbar-header"> <%= link_to 'Social Freak', root_path, class: 'navbar-brand' %> </div> <div id="navbar"> <% if current_user %> <ul class="nav navbar-nav pull-right"> <li><%= image_tag current_user.image_url, alt: current_user.name %></li> <li><%= link_to 'Log Out', logout_path, method: :delete %></li> </ul> <% else %> <ul class="nav navbar-nav"> <li><%= link_to 'Twitter', '/auth/twitter' %></li> </ul> <% end %> </div> </div> </nav> [...] 

Обратите внимание, что я также добавил аватар пользователя в главное меню.

Соответствующий маршрут:

конфиг / routes.rb

 [...] delete '/logout', to: 'sessions#destroy' [...] 

и действие контроллера:

sessions_controller.rb

 [...] def destroy if current_user session.delete(:user_id) flash[:success] = 'See you!' end redirect_to root_path end [...] 

Наконец, измените главную страницу, чтобы отобразить информацию о текущем вошедшем в систему пользователе:

просмотров / страниц / index.html.erb

 <% if current_user %> <div class="page-header"> <h1>Here is some info about you...</h1> </div> <div class="panel panel-default"> <div class="panel-body"> <ul> <li><strong>Name:</strong> <%= current_user.name %></li> <li><strong>Provider:</strong> <%= current_user.provider %></li> <li><strong>uID:</strong> <%= current_user.uid %></li> <li><strong>Location:</strong> <%= current_user.location %></li> <li><strong>Avatar URL:</strong> <%= current_user.image_url %></li> <li><strong>URL:</strong> <%= current_user.url %></li> </ul> </div> </div> <% else %> <%= render 'welcome' %> <% end %> 

Я перенес приветствие в отдельную часть:

просмотров / страниц / _welcome.html.erb

 <div class="jumbotron"> <h1>Welcome!</h1> <p>Authenticate via one of the social networks to get started.</p> </div> 

Это было много, чтобы покрыть, но теперь мы создали базу, и добавление новых провайдеров будет действительно легко (в основном)!

Аутентификация через Facebook

Мы собираемся использовать камень omniauth-facebook от Марка Додвелла.

Бросьте это в Gemfile

Gemfile

 [...] gem 'omniauth-facebook' [...] 

и беги

 $ bundle install 

Идея та же: нам нужно настроить нового провайдера и убедиться, что хеш аутентификации анализируется правильно.

конфиг / Инициализаторы / omniauth.rb

 [...] provider :facebook, ENV['FACEBOOK_KEY'], ENV['FACEBOOK_SECRET'] [...] 

Чтобы получить ключ и секрет Facebook:

  • Перейдите к developers.facebook.com .
  • Нажмите «Добавить новое приложение» в пункте «Мои приложения».
  • Нажмите «Веб-сайт» в диалоговом окне.
  • Нажмите «Пропустить и создать идентификатор».
  • Введите имя для своего приложения и выберите категорию, нажмите «Создать».
  • Вы будете перенаправлены на страницу приложения. Нажмите «Показать» рядом с «Секретом приложения» и введите свой пароль, чтобы открыть ключ. Скопируйте и вставьте эти ключи в файл инициализатора.

Не покидайте эту страницу еще. Вы должны сделать некоторые настройки, чтобы сделать приложение активным:

  • Откройте раздел «Настройки».
  • Нажмите «Добавить платформу» и выберите «Веб-сайт».
  • Заполните «URL сайта» («http: // localhost: 3000» для локального компьютера) и «Домены приложения» (должны быть получены из URL сайта или URL мобильного сайта).
  • Заполните «E-mail контакта» (необходимо сделать приложение активным) и нажмите «Сохранить изменения»
  • Перейдите в раздел «Статус и обзор» и установите «Хотите ли вы сделать это приложение и все его живые функции доступными для широкой публики?» И установите «Да».

Это минимальная настройка — с вашим приложением можно сделать гораздо больше, поэтому не стесняйтесь посещать и другие разделы.

Для провайдера Facebook мы также должны сделать некоторые настройки в приложении Rails:

конфиг / Инициализаторы / omniauth.rb

 [...] provider :facebook, ENV['FACEBOOK_KEY'], ENV['FACEBOOK_SECRET'], scope: 'public_profile', info_fields: 'id,name,link' [...] 

scope — это список разрешений, которые мы запрашиваем у пользователя при авторизации. public_profile означает, что для нашего приложения будет доступна только базовая информация, и никакие действия от имени пользователя не могут быть выполнены. У пользователя может быть запрошено множество разрешений , включая Расширенные, которые фактически позволяют вам получать доступ к конфиденциальным данным и выполнять некоторые действия от имени пользователя.

info_fields — это список полей, которые мы хотим включить в хэш аутентификации. Обратите внимание, что некоторые поля будут присутствовать всегда. Вот список других доступных полей.

Как и Twitter, Facebook поддерживает различные размеры аватаров ( image_size , по умолчанию 50 × 50, что нам подходит) и поддерживает https для доступа к аватару ( secure_image_url ). Вот все настройки, которые вы можете использовать.

Отлично, теперь добавьте новую ссылку в главное меню:

макеты / application.html.erb

 [...] <li><%= link_to 'Facebook', '/auth/facebook' %></li> [...] 

Настало время настроить метод from_omniauth ( здесь приведен пример хэша аутентификации). На самом деле, нужно изменить только одну строку:

модели / user.rb

 [...] user.url = auth_hash['info']['urls'][user.provider.capitalize] [...] 

Теперь вы можете перезагрузить сервер и проверить это!

Аутентификация через Google+

Мы будем использовать гем omniauth-google-oauth2 Джоша Эллиторпа.

Бросьте это в Gemfile:

Gemfile

 [...] gem "omniauth-google-oauth2" [...] 

и беги

 $ bundle install 

Вы знаете правила. Добавьте нового провайдера в файл инициализатора:

конфиг / Инициализаторы / omniauth.rb

 [...] provider :google_oauth2, ENV["GOOGLE_CLIENT_ID"], ENV["GOOGLE_SECRET"] [...] 

Чтобы получить ключ и секрет Google:

  • Перейдите на console.developers.google.com .
  • Нажмите «Создать проект» и дайте ему имя.
  • Откройте раздел «APIs» в правом меню и убедитесь, что Google+ API включен.
  • Откройте «Экран согласия» и заполните «Название продукта» (и другие поля, если хотите).
  • Откройте «Учетные данные» и нажмите «Создать новый идентификатор клиента».
  • Выберите «Веб-приложение».
  • Введите URL-адрес вашего приложения в поле «Авторизованные источники JavaScript» («http: // localhost: 3000» для локального компьютера).
  • Введите URL своего приложения плюс «/ auth / google_oauth2 / callback» в «URI авторизованного перенаправления».

Скопируйте идентификатор клиента и секрет клиента и вставьте его в файл инициализатора.

Теперь некоторые настройки в нашем приложении Rails:

конфиг / Инициализаторы / omniauth.rb

 [...] provider :google_oauth2, ENV["GOOGLE_CLIENT_ID"], ENV["GOOGLE_SECRET"], scope: 'profile', image_aspect_ratio: 'square', image_size: 48, access_type: 'online' [...] 

scope: 'profile' означает, что мы хотим получать только основную информацию о пользователе. Google предоставляет целую кучу API, поэтому посетите Playground, чтобы увидеть полный список доступных областей.

image_aspect_ratio: 'square' означает, что мы хотим получить аватар пользователя с одинаковой шириной и высотой ( original является значением по умолчанию).

image_size: 48 означает, что аватар пользователя будет иметь размер image_size: 48 пикселей по ширине и высоте.

access_type: 'online' означает, что мы не хотим получать токен обновления. Это означает, что по истечении срока действия токена, предоставленного нам Google, мы не сможем обновить его (используя этот специальный токен обновления), поэтому пользователю придется снова войти в систему. Автономный тип доступа полезен, когда вы хотите выполнять некоторые периодические действия от имени пользователя без необходимости постоянного входа в систему. Даже если срок действия токена пользователя истечет, ваша программа сможет обновить его автоматически. Это немного выходит за рамки этого поста.

Есть некоторые другие параметры конфигурации, которые вы можете использовать.

Вернитесь к методу from_omniauth и посмотрите на эту строку:

модели / user.rb

 [...] user.url = auth_hash['info']['urls'][user.provider.capitalize] [...] 

Для Google+ имя провайдера будет «google_oauth2», но соответствующий ключ не будет найден во вложенном хэше urls . Это будет что-то вроде:

 :urls => { :Google => "https://plus.google.com/xxx" } 

Мы могли бы добавить базовый условный оператор здесь, но лучше переименовать нашего провайдера, например так:

конфиг / Инициализаторы / omniauth.rb

 [...] provider :google_oauth2, ENV["GOOGLE_CLIENT_ID"], ENV["GOOGLE_SECRET"], scope: 'profile', image_aspect_ratio: 'square', image_size: 48, access_type: 'online', name: 'google' [...] 

Не забудьте изменить URL-адрес обратного вызова в консоли разработчика Google на « / Аутентификации / Google / обратный вызов»!

Теперь добавьте еще одну ссылку в главное меню:

макеты / application.html.erb

 [...] <li><%= link_to 'Google+', '/auth/google' %></li> [...] 

Перезагрузите сервер и попробуйте пройти проверку подлинности!

Аутентификация с помощью LinkedIn

Это будет последняя социальная сеть на сегодня и будет использовать драгоценный камень omniauth-linkedin-oauth2 от Decio Ferreira:

Gemfile

 [...] gem 'omniauth-linkedin-oauth2' [...] 

Не забудь бежать

 $ bundle install 

Зарегистрировать нового провайдера:

 [...] provider :linkedin, ENV['LINKEDIN_KEY'], ENV['LINKEDIN_SECRET'] [...] 

Чтобы получить ключ и секрет LinkedIn:

  • Перейдите на linkedin.com/secure/developer .
  • Нажмите «Добавить новое приложение».
  • Заполните обязательные поля. Эта форма намного больше (и немного ошибочна), чем вы видели при регистрации приложений для других социальных сетей, так что наберитесь терпения.
  • Заполните «URL-адреса перенаправления OAuth 2.0» URL-адресом в формате « / Авт / LinkedIn / Обратный вызов».
  • После отправки формы откройте страницу своего приложения и скопируйте ключ Consumer и Consumer secret в файл инициализатора.

Добавьте некоторые параметры конфигурации:

 [...] provider :linkedin, ENV['LINKEDIN_KEY'], ENV['LINKEDIN_SECRET'], scope: 'r_basicprofile', fields: ['id', 'first-name', 'last-name', 'location', 'picture-url', 'public-profile-url'] [...] 

scope: 'r_basicprofile' означает, что мы хотим получать только основные пользовательские данные.

fields содержит массив всех полей, которые должны быть возвращены в хеше аутентификации. Список полей по умолчанию можно найти здесь .

Добавьте еще одну ссылку в главное меню:

макеты / application.html.erb

 [...] <li><%= link_to 'LinkedIn', '/auth/linkedin' %></li> [...] 

Здесь все становится немного сложнее, потому что хеш аутентификации в LinkedIn немного отличается от тех, что мы видели до сих пор:

  • location — это вложенный хэш с кодом и именем страны.
  • urls также является вложенным хэшем, но ключ LinkedIn отсутствует. Вместо этого есть ключ public_profile и, возможно, куча других (для хранения личного веб-сайта и тому подобного).

Это вносит некоторую сложность в метод синтаксического анализа:

модели / user.rb

 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.location = get_social_location_for user.provider, auth_hash['info']['location'] user.image_url = auth_hash['info']['image'] user.url = get_social_url_for user.provider, auth_hash['info']['urls'] user.save! user end private def get_social_location_for(provider, location_hash) case provider when 'linkedin' location_hash['name'] else location_hash end end def get_social_url_for(provider, urls_hash) case provider when 'linkedin' urls_hash['public_profile'] else urls_hash[provider.capitalize] end end end 

Я использовал location_hash['name'] чтобы получить название страны, в которой находится пользователь, и urls_hash['public_profile'] чтобы получить только URL профиля пользователя. Конечно, мы можем использовать условное условие вместо этого, но когда-нибудь в будущем вы можете добавить больше провайдеров, и кто знает, какие несоответствия это принесет. Не стесняйтесь рефакторинг этого кода дальше.

К сожалению, LinkedIn не позволяет указывать размеры аватара (по крайней мере, я не нашел способа — если вы его знаете, поделитесь им со мной :)), поэтому добавьте этот простой стиль в таблицу стилей:

таблицы стилей / application.scss

 [...] nav { img { max-width: 48px; } } 

Теперь вы можете перезагрузить свой сервер и войти через LinkedIn!

Спасая от ошибок

Последнее, что мы собираемся обсудить сегодня, это спасение от ошибок, возникающих на этапе аутентификации. Вы можете вспомнить, что внутри метода create у нас есть:

sessions_controller.rb

 [...] begin @user = User.from_omniauth(request.env['omniauth.auth']) session[:user_id] = @user.id flash[:success] = "Welcome, #{@user.name}!" rescue flash[:warning] = "There was an error while trying to authenticate you..." end [...] 

Это действительно спасет любую ошибку, которая from_omniauth внутри метода from_omniauth . Однако это не защищает нас от ошибок, которые произошли во время аутентификации. Например, если вы отключите куки и попытаетесь пройти аутентификацию через одну из социальных сетей, вы получите ошибку SessionExpired .

Спасение таких ошибок должно быть сделано в файле инициализатора:

конфиг / Инициализаторы / omniauth.rb

 [...] OmniAuth.config.on_failure = Proc.new do |env| SessionsController.action(:auth_failure).call(env) end 

Мы просто вызываем действие auth_failure внутри SessionsController . Это может выглядеть так:

sessions_controller.rb

 [...] def auth_failure redirect_to root_path end [...] 

Вот еще один (более уродливый) возможный вариант:

конфиг / Инициализаторы / omniauth.rb

 [...] OmniAuth.config.on_failure do |env| error_type = env['omniauth.error.type'] new_path = "#{env['SCRIPT_NAME']}#{OmniAuth.config.path_prefix}/failure?message=#{error_type}" ,[301, {'Location' => new_path, 'Content-Type' => 'text/html'}, []] end 

Это выполнит перенаправление на « / Неудача? Сообщение = ххх». Не забудьте настроить соответствующий маршрут и действие контроллера для обработки запроса.

Вывод

Мы создали систему проверки подлинности с несколькими поставщиками, которая может быть расширена любым другим способом. Надеюсь, вам понравился этот пост! Вы когда-нибудь интегрировали аутентификацию через социальные сети в свои проекты? Поделитесь своим опытом и не стесняйтесь задавать свои вопросы. Если вы хотите, чтобы я затронул конкретную тему, пожалуйста, отправьте электронное письмо, я отвечу как можно скорее.

Удачного кодирования и оставайтесь на связи!