SorceryでSlackでログインを実装する

この記事を書いたひと: @t4traw 2019年5月15日

ぼくが普段Railsのログイン周りはSorcery/sorceryというgemを使わせてもらっています。そのsorceryにTwitterやFacebookなどのアカウントを利用したログインができるexternalオプションがるのですが、Slackアカウントを使った認証を実装した時のメモを書いておきます。

ちなみにほぼ公式のwikiに沿ったやり方ですので、さらに詳しい事を調べたい方は公式のwikiの方へどうぞ。

今回の記事のコードをGitHubにpushしておきます。

t4traw/sorcery_slack_login

環境&前提

  • ruby: 2.6.3
  • rails 5.2.2
    • sorcery: 0.13

rails newした直後のシンプルな状態で書いています。

sorceryのインストール&init

まずsorceryをインストールし、初期設定などをします。

Gemfile
gem 'sorcery'

bundle installし、sorceryをインストールします。ついでにexternalオプションもインストールします。すでにsorceryを使用している場合は下のコマンドでオプションを追加できます。

$ rails g sorcery:install
$ rails g sorcery:install external --only-submodules

そしたらdbをmigrateします。

$ rails db:migrate

Authenticationモデルを作成します。

$ rails g model Authentication --migration=false

SlackにAppを登録

SlackにAppを登録し、keyやsecretを生成する必要があります。ログインして、Appを作成してください。

Slack API: Applications | Slack

作成したら、下の方にあるClient IDとClient Secretをメモしておいてください。

Slackにcallback用のurlを登録します。

サイドバーにあるOAuth & Permissionsを開き、Redirect URLsを設定します。

外部認証を実装する

configの中にあるsorcery.rbを編集します。config.slack.keyにSlackのClient ID、config.slack.secretにClient Secretを設定します。

config/initializers/sorcery.rb
# externalが追加されていることを確認
Rails.application.config.sorcery.submodules = [:external]

Rails.application.config.sorcery.configure do |config|
  # ...
  config.external_providers = [:slack]
  # ...
  config.slack.callback_url = "http://0.0.0.0:3000/oauth/callback?provider=slack"
  config.slack.key = 'YOUR_CLIENT_ID'
  config.slack.secret = 'YOUR_CLIENT_SECRET'
  config.slack.user_info_mapping = {email: 'email'}
  # ...
  config.user_config do |user|
    # ...
    user.authentications_class = Authentication
    # ...
  end
end

UserモデルとAuthenticationモデルにrelationなどを追加します。

app/models/user.rb
class User < ApplicationRecord
  authenticates_with_sorcery!
  has_many :authentications, :dependent => :destroy
  accepts_nested_attributes_for :authentications
end
app/models/authentication.rb
class Authentication < ApplicationRecord
  belongs_to :user
end

基本的なログインしてないとページを見られなくなるような処理などを追加します。

app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
  before_action :require_login

  private
  def not_authenticated
    redirect_to login_path, alert: "Please login first"
  end
  
  def require_login
    unless current_user
      redirect_to login_url
    end
  end
end
app/controllers/users_controller.rb
class UsersController < ApplicationController
  skip_before_action :require_login, only: [:login]

  def index
    
  end

  def login

  end
end

oauth用のコントローラーを作成します。

$ rails g controller Oauths oauth callback
app/controllers/oauths_controller.rb
class OauthsController < ApplicationController
  skip_before_action :require_login, raise: false

  def oauth
    login_at(params[:provider])
  end
      
  def callback
    provider = params[:provider]
    if @user = login_from(provider)
      redirect_to root_path, :notice => "Logged in from #{provider.titleize}!"
    else
      begin
        @user = create_from(provider)
        reset_session
        auto_login(@user)
        redirect_to root_path, :notice => "Logged in from #{provider.titleize}!"
      rescue
        redirect_to root_path, :alert => "Failed to login from #{provider.titleize}!"
      end
    end
  end
end

これらのroutesを書いて

config/routes.rb
Rails.application.routes.draw do
  post "oauth/callback" => "oauths#callback"
  get "oauth/callback" => "oauths#callback"
  get "oauth/:provider" => "oauths#oauth", as: :auth_at_provider
  get 'login' => 'users#login', as: :login
  root to: 'users#index'
end

最後に適当にviewを作って動作テストしてみます。

app/views/users/login.html.erb
<%= link_to 'Slackでログイン', auth_at_provider_path(provider: :slack) %>
app/views/users/index.html.erb
<h1>ログイン成功</h1>
<p>email: <%= current_user.email %></p>
<p>provider: <%= current_user.authentications.first.provider %></p>
<p>slack_uid: <%= current_user.authentications.first.uid %></p>

無事ログインできました!

名前とかはまた別に取らないといけない

emailなどは簡単にとれるのですが、nameなどはconfig.slack.user_info_mapping = {email: 'email'}をごにょごにょすればいける?と思ったのですが、そう簡単には取れない様子。別でslackのusers_listというAPI叩く必要があるようです。長くなってきたので、これはまた別の記事で書こうかな。


これでSlackアカウントを使った外部認証ができました。

Slackはワークスペースに登録しているアカウントしか認証できないので、社内Appなどを作る時は非常に便利だなと思います。

それでは。