[Rails6系] auth0を使用したSNSログイン機能をHerokuにデプロイするまで

### 前提
記事を書いている私は現役のエンジニアではなくエンジニアを目指して学習中の者です。
よって、気をつけてはいますが記述内容には誤った内容が含まれている危険性があります。
その為この記事は参考程度に読んで頂き詳しい内容は公式の記述を参考にすることをおすすめします。

### この記事で実装すること
auth0とdeviseを使用してRailsアプリで簡単にSNSログインができる機能を実装し、Herokuにデプロイするまでの手順を説明します。
deviseを使用してユーザーの新規作成ができるRailsアプリがあることを前提に進めていきます。

### 使用技術
Ruby 3.1.2
Rails 6.1.6
Heroku
Deviseでユーザー新規作成機能実装済み


### 実装手順
auth0への登録からRailsアプリへの連携までについても説明しますがこの辺については以下のQiita記事や公式のチュートリアルがとても分かりやすいので参考にして下さい。
[公式のチュートリアル](https://auth0.com/docs/quickstart/webapp/rails/interactive)
[RailsアプリにAuth0をサクッと導入する](https://qiita.com/wonder_boooy/items/57d30faf940f9831bf1b)

# auth0を使ったSNSログインを実装

##### 1, auth0にフリープランでアカウントを作成する
公式サイトから無料トライアルをクリックしてアカウントを作成します。
[公式サイト](https://auth0.com/jp?utm_source=google&utm_medium=cpc&utm_term=-g-auth0&pm=true&utm_campaign=japan-jp-brand-auth0&matchtype=e&device=c&utm_source=google&utm_medium=cpc&utm_campaign=&utm_term=auth0&gclid=CjwKCAjwi8iXBhBeEiwAKbUofUau5yh_6TDFtKPaJMan2qeJQc9ueS17FDT2gVEyTD3dOMgTEl4vbxoCz4IQAvD_BwE)
有料プランもありますがauth0は無料プランで最大7000人までのユーザー登録と無制限ログインを提供してくれているので個人サービスレベルでは無料プランで十分使えます！

##### 2, create applicationから新しくアプリを作成
create applicationをクリックすると以下の画面が表示されるので**任意のアプリ名**と**Regular Web Applications**を選択してcreateします。
また、アプリに使用しているフレームワークではRuby on Railsを選択してあげましょう。
![スクリーンショット 2022-08-09 21.40.05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2055656/3cf7cde7-9ce8-345d-ba00-9f10583ffd46.png)

##### 3, 作成したアプリに**Application Callback URLs**と**Allowed LOgout URLs**を追加する
Application Callback URLsは承認後にリダイレクトされる先のURLらしいです。
ローカルの場合だと以下のURLを追加してあげます。
```
http://localhost:3000/auth/auth0/callback
```
Allowed LOgout URLsはログアウト後の遷移先となるURLらしいです。
同じくローカルの場合は以下のURLを追加します。
```
http://localhost:3000
```
これで一旦auth0での設定は終了です。
次の工程からはRailsのアプリ上で行います。

##### 4, gemを追加する
gemを追加してbundle installを実行します。
```ruby
#gemfile
gem 'omniauth-auth0', '~> 3.0'
gem 'omniauth-rails_csrf_protection', '~> 1.0'
```

##### 5, 設定に使用するauth0_domain, auth0_client_id, auth0_client_secretを環境変数にします。
auth0上で作成したアプリと紐づけるために3つの情報をRailsアプリにも記述する必要があります。しかし、それを直接書いてGithubにアップロードするのはセキュリティ的に良くないので環境変数にします。
Railsではdotenv-railsというgemがあるので今回はこれを使用します。実装方法は以下の記事に詳しく載っています。
[[Ruby on Rails]環境変数の設定方法(.bash_profile、Dotenv-rails)](https://qiita.com/yuichir43705457/items/7cfcae6546876086b849)
このgemのお陰で.envファイルに情報を書けば他のファイルで直接的に値を書かなくても呼び出せるようになります。
今回であればauth0で作成したアプリのsettingsタブのBasic Informationに書かれている3つの情報を記述します。
記述が終了したら.envファイルを.gitignoreに追加するようにしましょう。
```
#.env
AUTH0_DOMAIN = '自分のドメイン名'
AUTH0_CLIENT_ID = '自分のクライアントid'
AUTH0_CLIENT_SECRET = '自分のシークレットクライアント'
```

##### 6, config/initializersにauth0.rbファイルを作成して設定を記述する
```ruby
#config/initializers/auth0.rb
Rails.application.config.middleware.use OmniAuth::Builder do
  provider(
    :auth0,
    ENV['AUTH0_CLIENT_ID'],
    ENV['AUTH0_CLIENT_SECRET'],
    ENV['AUTH0_DOMAIN'],
    callback_path: '/auth/auth0/callback',
    authorize_params: {
      scope: 'openid profile'
    }
  )
end
```

##### 7, auth0用のコントローラーを作成する
ジェネレーターを使用してコントローラーを作成します。

```
$ rails generate controller auth0 callback failure logout --skip-template-engine
```
コントローラーを編集します。
```ruby
# app/controllers/auth0_controller.rb
class Auth0Controller < ApplicationController
  def callback
    auth_info = request.env['omniauth.auth']
    session[:userinfo] = auth_info['extra']['raw_info']

    redirect_to '/auth0-test'
  end

  def failure
    @error_msg = request.params['message']
  end

  def logout
    reset_session
    redirect_to logout_url
  end

  private


  def logout_url
    request_params = {
      returnTo: root_url,
      client_id: ENV['AUTH0_CLIENT_ID']
    }

    URI::HTTPS.build(host: ENV['AUTH0_CLIENT_ID'], path: '/v2/logout', query: to_query(request_params)).to_s
  end

  def to_query(hash)
    hash.map { |k, v| "#{k}=#{CGI.escape(v)}" unless v.nil? }.compact.join('&')
  end
end
```
ついでにルーティングも設定します。
```ruby
# config/route.rb
Rails.application.routes.draw do
  # ..
  get '/auth/auth0/callback' => 'auth0#callback'
  get '/auth/failure' => 'auth0#failure'
  get '/auth/logout' => 'auth0#logout'
end
```

##### 8, ログイン・ログアウトボタンを作成する。
任意の場所にボタンを表示してあげましょう。
```erb
<%= button_to 'Login', auth_auth0_callback_path, method: :post %>
```
```erb
<%= button_to 'Logout', auth_logout_path, method: :get %>
```
ここまででauth0によるSNSログインが使用できるようになっていると思います！
デフォルトであればGoogleが設定されているのでローカル上で試してみて下さい！


# auth0で取得した情報をRailsアプリのデータベースと紐づける
今の状態でもSNSログインは可能なのですがRailsのデータベースとは紐づいていません。
具体的にいうとログインはできてもdeviseのcurrent_userメソッドなどを使用するとundefind methodエラーになってしまいます。
これはauth0ではsession[:userinfo]に連携先のSNSから取得した情報を保存してその情報を表示しているだけなのでcurrent_userやuserに紐づいた投稿などを行うためには新規でユーザーを作成してあげる必要があります。

##### 1, SNSでのログインが行われたら裏側でユーザーを新規作成する
今回はsession[:userinfo]に含まれるユニークな値を取得してそれをユーザーに持たせるという形で実装しました。
しかし、後でいろいろ調べるとsession[:userinfo]の中にメールアドレスを持たせることも可能かもなのでその辺は詳しく調べてみて下さい！
session[:userinfo]['sub']で取得できる情報がユニークな値ぽかったのでそれを使用していきます。
まずはUserモデルにユニークな値を保持するためのカラムを追加して**rails db:migrate**を実行します。
```
$ rails generate migration AddSocialUniqueIdToUsers social_unique_id:string
```

##### 2, 自動でユーザーを新規作成するコードを追加
次にApplicationControllerでauth0経由でのログインがあった場合に自動でユーザーを新規作成するコードを追加します。
```ruby
class ApplicationController < ActionController::Base
  #全てのアクションの前にチェックを行う
  before_action :check_social_signed_in?

  private

  def check_social_signed_in?
    #auth0経由でログインが行われいるかを確認
    return unless session[:userinfo].present?
    #既にログインを行なっていた場合はそれ以降の処理を実行しない
    return if @current_user
    #ユーザーが既に登録されていなかった場合にだけ新規作成を実行
    #nameやユニーク値などはsession[:userinfo]から取得しパスワードはランダムな値を生成する
    @current_user = User.find_by(social_unique_id: session[:userinfo]['sub']) || User.create!(name: session[:userinfo]['name'],                                             password: SecureRandom.alphanumeric(10), avatar: session[:userinfo]['picture'], social_unique_id: session[:userinfo]['sub'])
    #ユーザーの作成が完了したらログインを行う
    sign_in @current_user
  end
end
```
これでauth0を使ってログインしたユーザーもcurrent_userによるプロフィール画像の表示やuserに紐づけた投稿を行えるようになります！

# Herokuにデプロイする
最後に本番環境でもauth0を使えるようにHerokuでの設定を行います。
既にアプリをHerokuでデプロイしている前提で進めます。

##### 1, Herokuの対象アプリにAdd-onsを追加する
Herokuのマイページにログインしてauth0でのSNSを実装したいアプリを選択します。
アプリページでResourcesタブを選択してAdd-onsからauth０を検索して追加します。(freeを選ぶように注意して下さい)
これでauth0とHerokuを紐づけることができます。

##### 2, 環境変数を設定する
今度はHerokuのSettingタブを開きます。
settingの中にあるReveal Config Varsをクリックします。
auth0とherokuを結びつけた時点でAUTH0_CLIENT_ID、AUTH0_DOMAIN、AUTH0_CLIENT_SECRETが自動で登録されています。
この値をRailsアプリの.envファイルに書いたものに修正してあげましょう。

##### 3, auth0に本番環境ようにURLを追加する。
最後にauth0のページに移動して最初に設定した**Application Callback URLs**と**Allowed LOgout URLs**に本番用のURLを追加します。URLは複数登録できます。
```
#Application Callback URLs
http://localhost:3000/auth/auth0/callback,
https://auth0-test.herokuapp.com/auth/auth0/callback
```
```
#Allowed LOgout URLs
http://localhost:3000,
https://auth0-test.herokuapp.com
```

以上で本番環境でもauth0を使った簡単SNSログインができるようになっているかと思います！
拙い文章でしたがお役に立てれば幸いです。