【Rails】Sorceryで外部認証 〜LINE編〜

# はじめに🧎🏻
現在個人開発しているサービスにて
SorceryのExternalモジュールを利用して外部認証を実装する際に
LINE認証を実装している例がなかったため、備忘録も兼ねて記します。

※下記の記事にてGoogle認証についても解説しています！
内容に一部重複している部分があるため、実装済みの部分に関しては都度読み飛ばしていただければと思います。

https://qiita.com/tarakish/items/8e61106fa2473a9d97e8

### 環境💻
- Ruby `3.0.1`
- Ruby on Rails `6.1.4.4`
- 【gem】 sorcery `0.16.2`

### 想定読者👥
- sorceryのexternalモジュールでLINE認証を実装したい人

### 前提🎬
- [Simple Password Authentication](https://github.com/Sorcery/sorcery/wiki/Simple-Password-Authentication)でのメールアドレス認証機能は実装済みであること。

# 実装の流れ✔️
少し長い道のりなので全体像を把握しておきましょう。
1. authenticationsテーブルを作成
2. Authenticationモデルの設定
3. OauthsControllerの作成
4. ルーティングの追加
5. LINE Developersに登録
6. sorcery.rbの設定
7. OauthsControllerのストロングパラメータを編集
8. mkcertでローカルホストをSSL化して動作確認
9. リンクの設置

この内1~5に関しては、偉大な先人達が記事に残してくれているので割愛します。
自分が主に参考にさせていただいたのは以下の記事です。

- 【1~4】 Externalモジュールの導入部分
https://blog.aiandrox.com/posts/tech/2020/03/29/

- 【5】 Line APIの登録部分(記事の`3.LINE Developersに登録`を参照)
  https://qiita.com/s10aim_tana/items/2d174d4e31e4041700ee



5まで終わりましたか？おかえりなさい🍵

それでは`6.sorcery.rbの設定`からやっていきましょう！

### 6. sorcery.rbの設定📝
まず完成形がこちら↓
```ruby:sorcery.rb
Rails.application.config.sorcery.submodules = [:external]

# Here you can configure each submodule's features.
Rails.application.config.sorcery.configure do |config|
    (中略)
    # -- external -- 78行目辺り
    # What providers are supported by this app
    # i.e. [:twitter, :facebook, :github, :linkedin, :xing, :google, :liveid, :salesforce, :slack, :line].
    # Default: `[]`
    config.external_providers = %i[line]
    (中略) # 223行目辺り
    config.line.key = Rails.application.credentials.dig(:line, :channel_id)
    config.line.secret = Rails.application.credentials.dig(:line, :channel_secret)
    config.line.callback_url = Settings.sorcery[:line_callback_url]
    config.line.scope = 'profile'
    # config.line.bot_prompt = "normal"
    # config.line.user_info_mapping = {name: 'displayName'}
    (以下略)
end
```
細かく見ていきましょう。
##### ①モジュールの読み込み
```ruby
Rails.application.config.sorcery.submodules = [:external]
```
externalモジュールを読み込みます。

##### ②providersの指定
```ruby
config.external_providers = %i[line]
```
利用する外部認証のプロバイダーを指定します。

##### ③ キー、`callback_url`、取得情報の設定
```ruby
config.line.key = Rails.application.credentials.dig(:line, :channel_id)
config.line.secret = Rails.application.credentials.dig(:line, :channel_secret)
config.line.callback_url = Settings.sorcery[:line_callback_url]
config.line.scope = 'profile'
# config.line.bot_prompt = "normal"
# config.line.user_info_mapping = {name: 'displayName'}
```

###### a.キーの設定
- `config.line.key`…作成したプロバイダー（チャネル）のチャネルIDを設定
- `config.line.secret`…作成したプロバイダー（チャネル）のチャネルシークレットを設定

上記２つは秘匿情報にあたるため、`credentials.yml`などで管理してください。
```yaml:credentials.yml
line:
  channel_id: チャネルidの値
  channel_secret: チャネルシークレットの値
```

###### b.`callback_url`の設定
- `config.line.callback_url`…コールバックURLを設定。

開発・本番環境で違うものを設定するため、[Config](https://github.com/rubyconfig/config)ジェムなどで管理すると楽です。
GoogleやTwitterの認証と違い、LINEのチャネルにはコールバックURLを一つしか登録できないため、
確認したい環境に合わせて都度変更する必要があります。
```yaml
# development.yml ※開発環境もhttpsにしておく。設定は後述。
sorcery:
    line_callback_url: 'https://localhost:3000/oauth/callback?provider=line'

# production.yml
sorcery:
    line_callback_url: 'https://本番環境のドメイン/oauth/callback?provider=line'
```

###### c.取得情報の設定
- `config.line.scope`…取得する情報のスコープを定める。5種類あります。規定は[公式ドキュメント](https://developers.line.biz/ja/docs/line-login/integrate-line-login/#scopes)を参照。
    Google認証の場合はscopeを指定しなくても認証機能は動きましたが、LINEでは必須のようです。
    ※また、emailを取得する場合はチャネル側でメールアドレス取得申請をする必要があります。

- `config.line.bot_prompt`…ログイン時に公式アカウントを友だち追加するオプションを表示する設定。`normal`or`aggressive`を指定。詳細は[公式ドキュメント](https://developers.line.biz/ja/docs/line-login/link-a-bot/)。

- `config.line.user_info_mapping`…名前通り、line認証を通して取得する情報(user_info)を、アプリ側でどのパラメータとして扱うか(mapping)の設定。

今回自分の個人開発では、authenticationsテーブルのuidを発行するために外部認証を使用しておりemailや名前は取得しないため、下の2つはコメントアウトしています。
※そのため、ユーザーの情報を取得する場合もしかすると今回の記述では動かない箇所が出てくるかもしれません。ご了承ください🙏🏻

### 7.OauthsControllerのストロングパラメータを編集🖋
```ruby:oauths_controller.rb
class OauthsController < ApplicationController
    (中略)
    private

    def auth_params
        params.permit(:code, :provider, :error, :state)
    end
end
```
`:error`と`:state`を追加します。
これらを許可しないとLINE API側で弾かれてしまいます。
`:state`に関しては、[公式](https://developers.line.biz/ja/docs/line-login/integrate-line-login/#making-an-authorization-request:~:text=%E4%BB%98%E4%B8%8E%E3%81%A7%E3%81%8D%E3%81%BE%E3%81%99%E3%80%82-,state,-String)にもある通り必須のパラメータなので理解できますが、`:error`をわざわざ追加しないと弾かれるのはよくわかってないので深堀りの余地あり。

### 8.mkcertでローカルホストをSSL化して動作確認🔑

LINE認証の場合は、https通信でないと動作してくれないため
動作確認のためにlocalhostをSSL化する必要があります。
自分の場合は下記の記事を参考に、`mkcert`と`puma`を使う方法で行いました。
わざわざ書くこともないので、この記事を参考にしていただければと思います🙌🏻

https://qiita.com/t12u/items/52721d3ea2ce1e605ae9#%E3%82%A2%E3%83%97%E3%83%AA%E3%82%B1%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%B3%E3%81%ABhttps%E3%81%A7%E3%82%A2%E3%82%AF%E3%82%BB%E3%82%B9%E3%81%A7%E3%81%8D%E3%82%8B%E3%82%88%E3%81%86%E3%81%AB%E8%A8%AD%E5%AE%9A%E3%81%99%E3%82%8B:~:text=%E3%81%A3%E3%81%A6%E3%81%84%E3%81%8D%E3%81%BE%E3%81%99%E3%80%82-,%E3%82%A2%E3%83%97%E3%83%AA%E3%82%B1%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%B3%E3%81%ABhttps%E3%81%A7%E3%82%A2%E3%82%AF%E3%82%BB%E3%82%B9%E3%81%A7%E3%81%8D%E3%82%8B%E3%82%88%E3%81%86%E3%81%AB%E8%A8%AD%E5%AE%9A%E3%81%99%E3%82%8B,-https%E3%81%A7%E3%82%A2%E3%82%AF%E3%82%BB%E3%82%B9


### 9.リンクの設置📌
```erb:user_sessions/new.html.erb
<%= link_to 'Login with Line', auth_at_provider_path(provider: :line) %>
```

# おわりに🙇🏻‍♂️

おそらく機能実装までの過程で間違いなく何かしらのエラーと戦うことになると思います。（自分もそうでした）
本記事でフォローできればよかったのですが、実装から執筆まで期間が空き詳細は忘れてしまったので、
気休め程度に「エラーが出た際はこの辺チェックするといいかもリスト」を置いておきます😶‍🌫️
- チャネルのコールバックURLが正しく設定されているか？（稀に反映に少し時間がかかることもあるみたい）
- パラメータは正しく渡っているか？
- Oauthsコントローラーのストロングパラメータは適切か？
- 本番環境はSSL化されているか？

まだまだ学習中の身のため間違っている点や補足、タイポなどありましたら
お気軽にコメント、編集リクエストいただけますと幸いです！！

# 参考記事📄

https://github.com/Sorcery/sorcery

https://developers.line.biz/ja/docs/line-login/integrate-line-login

https://blog.aiandrox.com/posts/tech/2020/03/29

https://qiita.com/s10aim_tana/items/2d174d4e31e4041700ee

https://qiita.com/t12u/items/52721d3ea2ce1e605ae9