RailsアプリでSpotifyAPIのソーシャルログインを実装

# RailsでSpotifyAPIとOmmniAuthのソーシャルログインを実装

## 環境
* Ruby 3.0.3
* Rails 6.1.4

## 前提
`Authorization Code Flow`というエンドユーザーの情報を取得でき長時間実行アプリケーションに適しているものを利用します。
https://developer.spotify.com/documentation/general/guides/authorization/code-flow/

他にも２種類のトークンがあるので目的に合わせてトークンの発行をしてください。

Spotifyでのログインのみなのでdeviseだったりを使わずに実装します。

## 手順
### 1\. SpotifyAPIを利用するための準備
下記のリンクがSpotifyAPIを利用するときの設定等を行うサイトになります。
https://developer.spotify.com/dashboard/
[![Image from Gyazo](https://i.gyazo.com/7a5ac245300bc15826dc181af66aed86.jpg)](https://gyazo.com/7a5ac245300bc15826dc181af66aed86)
###### まずSpotifyを利用したことがない場合は、Spotifyアカウントを作成する必要があるので公式ホームページから指示に従って無料のアカウントを作成してください。
DASHBOARDにログインし、「CREATE AN APP」からアプリ名と簡単な説明を入力しアプリの作成をします。
作成後はclient IDと隠れているClient Secretを確認し、EDIT SETTINGSからRedirect URIsを設定する。
Redirect URIsは今回OmniAuthを使うので`http://localhost:3000/auth/spotify/callback`を使う
[![Image from Gyazo](https://i.gyazo.com/0eed47356be28f904005610a4f7f4621.jpg)](https://gyazo.com/0eed47356be28f904005610a4f7f4621)

### 2\. Railsアプリに必要なGemの準備
* `dotenv`
* `omniauth-spotify`

#### dotenvの設定
インストール後、`.env`を作成し確認したClient IDとClient Secretを追加する。このファイルは公開しないので.gitignoreに追加する。
```.env
SPOTIFY_CLIENT_ID = "*****"
SPOTIFY_CLIENT_SECRET = "*****"
```

#### omniauth-spotifyの設定
`config/initializer/omniauth.rb`を作成し、.envに設定したkeyを使って設定する。
認可範囲を自分が取得したい範囲のものを設定する。今回はユーザーのプロフィールに関係するものだけを選択。
認可範囲は下記のリンクから
https://developer.spotify.com/documentation/general/guides/authorization/scopes/
```config/initializer/omniauth.rb
Rails.application.config.middleware.use OmniAuth::Builder do
  provider :spotify, ENV[SPOTIFY_CLIENT_ID], ENV[SPOITFY_CLIENT_SECRET]
     scope: 'playlist-read-private user-read-private user-read-email'
end
```

### 3\. ログイン機能の作成
#### ルーティングの設定
```routes.rb
Rails.application.routes.draw do
  get '/new', to: 'sessions#new'
  get '/auth/:provider/callback', to: 'sessions#create'
end
```
### ログインボタンの実装
```sessions/new.slim.html
= link_to 'spotifyでログイン', "/auth/spotify", method: :post
```

### ログイン処理の実装
```sessions_controller.rb
def create
  user = User.find_or_create_from_omniauth!(request.env["omniauth.auth"])
  session[user_id] = user.id
  redirect_to root_path
end
```
**request.env["omniauth.auth"]の中身**
```
{"provider"=>"spotify",
 "uid"=>"******",
 "info"=>
  {"name"=>"***",
   "nickname"=>"*******",
   "email"=>nil,
   "urls"=>{"spotify"=>"https://open.spotify.com/user/******"},
   "image"=>nil,
   "birthdate"=>nil,
   "country_code"=>"JP",
   "product"=>"premium",
   "follower_count"=>0},
 "credentials"=>
  {"token"=>
    "******",
   "refresh_token"=>
    "******",
   "expires_at"=>1662430288,
   "expires"=>true},
 "extra"=>
  {"raw_info"=>
    {"country"=>"JP",
     "display_name"=>"***",
     "explicit_content"=>{"filter_enabled"=>false, "filter_locked"=>false},
     "external_urls"=>{"spotify"=>"https://open.spotify.com/user/*******"},
     "followers"=>{"href"=>nil, "total"=>0},
     "href"=>"https://api.spotify.com/v1/users/******",
     "id"=>"******",
     "images"=>[],
     "product"=>"premium",
     "type"=>"user",
     "uri"=>"spotify:user:******"}}}
```

### 受け取ったユーザーの情報を処理する
```user.rb
def self.find_or_create_from_omniauth!(auth_hash)
  user = User.find_or_initialize_by!(provider: auth_hash[:provider], uid: auth_hash)
  user.update!(
    name: auth_hash[:info][:name],
    image: auth_hash[:info][:image],
    access_token: auth[:credentials][:token],
    refresh_token: auth[:credentials][:refresh_token]
  )
user
end
```

これでログイン機能の完成です。
認証のみに使う場合は`User.find_or_create_by!`を使いますが、今回はユーザーの情報を使ったり画像の更新があった場合にここで更新したかったので`find_or_initialize_by!`でuserを作り、update!する仕様にしました。
また、本来であればaccess_tokenとrefresh_tokenをそのまま登録するのはセキュリティ的に良くないので暗号化するためにGemの`attr_encrypted`をインストール、設定します。