【Rails】Sorceryを使用したGitHubログイン
はじめに
こんにちは!大ちゃんの駆け出し技術ブログです。
sorceryでGitHubログインを実装しましたので本記事で紹介します。GitHubログインをSorceryで実装している記事はほとんどなかったので誰かの役に立てば幸いです。
公式の流れに基本的には沿っていますがGitHubの例ではないです。
GitHubにアプリ登録
外部認証を使用するためにGitHub側で必要な設定を行います。
① 右上のアイコン → 「Settings」をクリック
② 左ペインから「Developer settings」をクリック
③ 左ペインから「OAuth Apps」を選択し、「New GitHub App」をクリックします。
④ 以下のように各項目を入力し、「Register application」をクリック
- Application name(任意のアプリ名) → 「sample-app」
- Homepage URL(アプリのホームページのURL) → 「http://localhost:3000」
- Authorization callback URL(認証後のコールバックURL) → 「http://localhost:3000/oauth/callback?provider=github」
※ 開発環境ではlocalhost:3000ですが、デプロイ をした後はドメイン名に変更します。
⑤ ④の後にアプリの設定画面に遷移します。「You need a client secret to authenticate as the application to the API」(アプリケーション側でAPIを認証するためにはクライアントシークレットが必要)とあるように、client secretがないとGitHubログインが実装できません。「Generate a new client secret」をクリックします。
⑥ Client IDとClient Secretを控える(後ほどアプリ内で記述します。)
補足
※ SNSログイン等の外部認証機能はこのように基本的に「新規アプリ作成」というフローが必要です。そこで使われる用語の意味はほとんど共通ですので下記に概要を書いておきます。
- Authorization callback URL・・・認証後にアクセスするURLのこと。例えば、GitHub認証画面で認証を許可した後のURLに該当する。sorceryの場合は「ドメイン/oauth/callback?provider=(facebook or github or etc...)」で指定します。
- Client ID & Client Secret・・・外部サイトのアプリ固有の鍵みたいなもの。APIを使用するためにアプリ内で環境変数として指定し、APIとの連携を許可する役割を持っています。
Sorcery側の実装
エディタでGitHub認証するための設定をしていきます。前提として、Sorceryでユーザの通常ログインは実装済みで話を進めていきます。
① authenticationsテーブルを作成
下記コマンドを実行することで外部認証用のテーブルを作成します。
$ rails g sorcery:install external --only-submodules gsub config/initializers/sorcery.rb insert app/models/user.rb create db/migrate/2021xxxxxxxxxx_sorcery_external.rb
下記ファイルが作成されます。
class SorceryExternal < ActiveRecord::Migration[6.1] def change create_table :authentications do |t| t.integer :user_id, null: false t.string :provider, :uid, null: false t.timestamps null: false end add_index :authentications, [:provider, :uid] end end
そのままマイグレーション
$ rails db:migrate
② Authenticationモデルの作成
①で実行したコマンドでテーブルは作成されるのですが、それに対応したモデルは作成されていません。そのため手動で作成する必要があります。
$ rails g model Authentication --migration=false
マイグレーションは実行済みなのでオプションで--migration=false
としておきます。①でモデルも一緒に作成しておけばオプションの考慮はいらないはずなのですが、、、
AuthenticationモデルとUserモデルにアソシエーションを追加します。
Userモデル
class User < ApplicationRecord authenticates_with_sorcery! has_many :authentications, dependent: :destroy accepts_nested_attributes_for :authentications end
Authenticationモデル
class Authentication < ApplicationRecord belongs_to :user end
ちなみにaccepts_nested_attributes_for
ですが、これはそれに属するインスタンスが作成された時に、子モデルも同時に作成する設定です。Userモデルが作成されたら、自動でAuthenticationモデルも作成してくれるということになります。
※ ちなみにaccepts_nested_attributes_for
は非推奨となっており、Railsの開発者たちから嫌われています。
③ credentials.yml.encを編集
先ほど控えたClient IDとClient Secretを環境変数としてcredentials.yml.encに記述しアプリ内に登録します。credentials.yml.encは暗号化された文字列が表示されているファイルです。
p2cImUehvb1o9HfMqvKxj0PImkV8nJO12hwcUwC9xuZg6WhKIfkt+yzKIMOs61DflHTnft5abRrDvVDHxwTEe5YQHG+/TdvWF3VlFVxaiG3AhwoiOZqpVmGBVeIk+Bk4cAdNzD6hKwCzoZLC3R80aMtmSDSgGplAp+M3gWvWKPRUUCWnDZD9K/+ayHeuVjf/jmQMwBvHH2aEzDeoFjbaEp5cZgIsVRUnA4HpmAiTHkBmQlN16FWVXB1BmKXfJkCFG01iFUkLMfnUG5sKGHTzb33XRQ0y05pa5RDUQCEZLIuwLL5mL6xLsIs28+P64qLsqqiMfAcVUfQdZMDBSlzH1a+R3cBowF2Zpe9b5gKOcIheM00Xv04imOCxfN6j5Z4waB1u1psOhU/ucUdaN2UrglnVAklHTRC9BjKS6//tfzUsJarpjGT4v7o30MFgkvj825Yjysb0mYQ7FcgcRdZTqDVecz6JQTCAif7+QTbXilPtD5ZbSB7G7Y4GzHP8XaEfXmWPWv15PxotjXVWL+2qI7/2Qy9lJK6WZZU5WfP/a9fX6C5wxy0ZCZlZENamIl6hIlwIcbS5FUurXDZB8WqEGh4v0TYCdzOT9Gskj2Kv+1hQFVsxNdtPig==--6dulaUT3AgkwOLTZ--DD7AmvexctmdANexHgISBw==
しかし、master.keyがconfig配下にある状態で下記コマンドを実行すると中身を編集できます。
$ EDITOR="vi" bin/rails credentials:edit
以下のように編集してください。
github: key: 控えたClient ID secret: 控えたClient Secret callback_url: 'http://localhost:3000/oauth/callback?provider=github
編集後保存します。上述した中身を編集できるコマンドを実行して保存されたか確認しておきましょう。
$ EDITOR="vi" bin/rails credentials:edit
④ sorcery.rbを編集
sorcery.rbをGitHub認証用に対応するように編集します。
Rails.application.config.sorcery.submodules = [:external] # コメントアウト ・ ・ ・ # Here you can configure each submodule's features. Rails.application.config.sorcery.configure do |config| ・ ・ ・# 外部認証にgithubを指定 config.external_providers = [:github] ・ ・ ・ # ③で設定したcredentials.yml.encの値に合わせる config.github.key = Rails.application.credentials.dig(:github, :key) config.github.secret = Rails.application.credentials.dig(:github, :secret) config.github.callback_url = Rails.application.credentials.dig(:github, :callback_url) config.github.user_info_mapping = {email: "email", name: "login", remote_avatar_url: "avatar_url"} config.github.scope = "user:email" # --- user config --- config.user_config do |user| # -- external -- # Class which holds the various external provider data for this user. # Default: `nil` # user.authentications_class = Authentication # コメントアウトしてAuthenticationモデルを指定 end end
⑤ OauthsControllerを作成して下記のように記述
class OauthsController < ApplicationController skip_before_action :require_login, raise: false def oauth return redirect_to root_path, info: t('defaults.info') if logged_in? login_at(params[:provider]) end def callback provider = params[:provider] if @user = login_from(provider) redirect_to root_path, success: "#{provider.titleize}アカウントでログインしました。" else begin @user = create_from(provider) reset_session auto_login(@user) redirect_to root_path, success: "#{provider.titleize}アカウントでログインしました。" rescue redirect_to root_path, error: "#{provider.titleize}アカウントでのログインに失敗しました。" end end end end
補足
login_from
・・・既にユーザが外部認証済みでDBに登録されているか確認し、登録されていればログインをしtrue
を返却するメソッド。登録されていなければfalse
を返す。create_from
・・・login_from
でfalse
の場合にユーザ登録をするメソッド。
⑥ ルーティングを設定
sorceryではどの外部認証を使っても下記のルーティングを記載します。
# routes.rb post "oauth/callback", to: "oauths#callback" get "oauth/callback", to: "oauths#callback" get "oauth/:provider", to: "oauths#oauth", as: :auth_at_provider
⑦ Userテーブルにremote_avatar_url
カラムを追加
GitHub認証の返却値として、name, email, remote_avatar_urlの値が返却されます。sorceryのGitHub認証では、Userテーブルにこれらのカラムがあることを期待して動作するため、もしremote_avatar_url
がないとエラーを起こします。
undefined method 'remote_avatar_url' for < #class User ・・・>
既にsorceryでUserテーブルを作成していますが、そのカラムにremote_avatar_url
がないため、カラムを追加します。
class AddAvatarsToUsers < ActiveRecord::Migration[6.1] def change add_column :users, :remote_avatar_url, :string end end
$ rails db:migrate
⑧ リンクをviewに追加
<%= link_to auth_at_provider_path(provider: :github), class: "btn btn-light" do %> <i class="fab fa-github mr-2"></i> <span class="has-text-weight-medium">Sign in with Github</span> <% end %>
終わりに
Slackログインには1ヶ月もかかったのでGitHubもそれなりに時間がかかるのかなと思っていましたが、たったの2時間で実装できてしまいました、、、、全然難易度違う、、、、、。しかし、Slackログインを経験したからこそClient IDの流れやCallback URLの設定の理解があったので楽に実装できたんだと思います!
他の外部認証もどんどん試していきたいです!