【Appleログイン】Sign in with Appleの実装手順（Ruby on Rails編）

## 対象読者

- Ruby on Railsで作成したWebアプリケーションにAppleログイン（Sign in wth Apple）を実装したい人

最近色々なサービスで頻繁に見かけるようになったAppleログインですが、その実装手順に関して意外と日本語の情報が少ないように感じたのでメモ書きとして残しておきたいと思います。

Dockerfileを書き始めるところから始めるので、ちゃんと手順に沿っていればとりあえず同じように再現できるはずです。

## 下準備

まず最初に環境構築のため各種フォルダ・ファイルを作成します。

### ディレクトリを作成

```
$ mkdir sign-in-with-apple-sample
$ cd sign-in-with-apple-sample
```

### 各種ファイルを作成

```
$ touch Dockerfile
$ touch docker-compose.yml
$ touch Gemfile
$ touch Gemfile.lock
```

```dockerfile:./Dockerfile
FROM ruby:2.6.6

RUN apt-get update -qq && apt-get install -y build-essential libpq-dev nodejs

ENV APP_PATH /myapp

RUN mkdir $APP_PATH
WORKDIR $APP_PATH

COPY Gemfile $APP_PATH/Gemfile
COPY Gemfile.lock $APP_PATH/Gemfile.lock
RUN bundle install

COPY . $APP_PATH
```

```yml
version: '3'
services:
  db:
    image: mysql:5.7
    environment:
      MYSQL_DATABASE: root
      MYSQL_ROOT_PASSWORD: password
    volumes:
      - mysql-data:/var/lib/mysql
    ports:
      - "3306:3306"

  web:
    tty: true
    stdin_open: true
    build: .
    command: bash -c "rm -f tmp/pids/server.pid && bundle exec rails s -p 3000 -b '0.0.0.0'"
    volumes:
      - .:/myapp
      - ./vendor/bundle:/myapp/vendor/bundle
    ports:
      - "3000:3000"
    links:
      - db

volumes:
  mysql-data:
```

```rb:./Gemfile
source 'https://rubygems.org'
gem 'rails', '~> 5.2.4'
```

```rb:./Gemfile.lock
# 空でOK
```

### Railsプロジェクトを作成

```
$ docker-compose run web rails new . --force --no-deps -d mysql --skip-bundle
$ docker-compose build
```

### database.ymlを書き換え

```yml:./config/database.yml
default: &default
  adapter: mysql2
  encoding: utf8
  pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
  username: root
  password: password # デフォルトだと空欄になっているはずなので追記 
  host: db # デフォルトだと「localhost」になっているので書き換え

development:
  <<: *default
  database: myapp_development
```

### コンテナを起動 & データベースを作成

```
$ docker-compose up -d
$ docker-compose run web rails db:create
```

### localhost:3000へアクセス

![スクリーンショット 2020-12-25 12.08.56.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/688854/dc824873-e272-faa3-c612-00badcdb2c0e.png)

「[localhost:3000](http://localhost:3000/)」へアクセスし、いつもの画面が表示されれれば成功です。

## 実装

準備ができたのでいよいよ実装に入ります。

### 各種gemの用意

```rb:./Gemfile
...

gem 'devise'
gem 'omniauth'
gem 'omniauth-apple'
```

今回使用する各種gemをインストールします。

```
$ docker-compose build
```

Gemfileを更新したので再度ビルド。

### Deviseをインストール

```
$ docker-compose run web rails g devise:install

...

create  config/initializers/devise.rb
create  config/locales/devise.en.yml
```

### Userモデルを作成

```
$ docker-compose run web rails g devise user

...

invoke  active_record
create  db/migrate/20201224203750_devise_create_users.rb
create  app/models/user.rb
invoke  test_unit
create  test/models/user_test.rb
create  test/fixtures/users.yml
insert  app/models/user.rb
route   devise_for :users
```

### OmniAuth用のカラムを追加

```
$ docker-compose run web rails g migration AddColumnsToUsers uid:string provider:string
$ docker-compose run web rails db:migrate
```

### OmniAuth用のモジュールとメソッドを追加

```rb:app/models/user.rb
class User < ApplicationRecord
  devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :validatable, :omniauthable, omniauth_providers: [:apple] # ←追加

  def self.from_omniauth(auth)
    where(provider: auth.provider, uid: auth.uid).first_or_create do |user|
      user.provider = auth.provider
      user.uid = auth.uid
      user.email = auth.info.email
      user.password = Devise.friendly_token[0, 20]
    end
  end
end
```

デフォルトの状態だと「omniauthable」「omniauth_providers」のモジュールが無いので追加し、「apple」を指定します。

### 各種コントローラーを作成

```
$ docker-compose run web rails g devise:controllers users

...

create  app/controllers/users/confirmations_controller.rb
create  app/controllers/users/passwords_controller.rb
create  app/controllers/users/registrations_controller.rb
create  app/controllers/users/sessions_controller.rb
create  app/controllers/users/unlocks_controller.rb
create  app/controllers/users/omniauth_callbacks_controller.rb
```

色々なコントローラーが一気に作成されますが、今回必要なのは「omniauth_callbacks_controller.rb」のみです。

```rb:./app/controllers/users/omniauth_callbacks_controller.rb
class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController
  skip_before_action :verify_authenticity_token, only: %i[apple]

  def apple
    @user = User.from_omniauth(request.env['omniauth.auth'])

    if @user.persisted?
      sign_in_and_redirect @user, event: :authentication
      set_flash_message(:notice, :success, kind: 'Apple') if is_navigational_format?
    else
      session['devise.apple_data'] = request.env['omniauth.auth'].except('extra')
      redirect_to new_user_registration_url
    end
  end
end
```

### Rackミドルウェアを作成

参照記事: [RailsとRack](https://railsguides.jp/rails_on_rack.html)

```rb:./config/initializers/omniauth.rb
Rails.application.config.middleware.use OmniAuth::Builder do
  provider :apple, ENV['CLIENT_ID'], '',
           {
             scope: 'email name',
             team_id: ENV['TEAM_ID'],
             key_id: ENV['KEY_ID'],
             pem: ENV['APPLE_PRIATE_KEY'],
           }
end
```

config/initializers/配下に「omniauth.rb」というファイルを作成。

Sign in with Appleを実装するためには、Apple Developerに登録して

- CLIENT_ID
- TEAM_ID
- KEY_ID
- PRIVATE_KEY

といった4つの値を取得する必要があります。

参照記事: 
[Apple Developerのアカウント登録方法](https://www.hatarakumama-pj.com/posts/ginzaitlab6889998/)
[Sign in with Apple を実装するために必要な証明書の作り方](https://qiita.com/natsumo/items/4f209a0efa0d488f1de0)

この辺についてはすでに色々な記事が世の中に存在するので各自取得しておいてください。

### Return URLsの設定

ここから一瞬だけApple Developer側の操作になります。

最後に、認証が許可された後のリダイレクト先を指定。

Apple Developerのダッシュボードから「Certifications, Identifers & Profiles」へ飛ぶ。

![80911786-8d52f100-8d73-11ea-8bbd-39af246f5aee.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/688854/a2f83418-c3d9-11e6-1492-43e537523d9a.png)

「Identifers」タブを開き、右のプルダウンメニューから「Services IDs」を選択。

![80911889-25e97100-8d74-11ea-8220-b0fe6ba46875.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/688854/ba42a234-43bf-9baa-d3bd-f7e3fdb9226d.png)

「Sign in with Apple」の右にある「configure」をクリックすると「Domains and Sub domains」「Return URLs」を登録する画面が出てくるので、それぞれ記述していきます。

![スクリーンショット 2020-12-25 13.02.54_censored.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/688854/35a4cfff-eaf8-4a41-26bb-b692f0bda8f7.jpeg)

```
Domains and Sub domains → 「http://」「.com」などを除外した部分
Return URLs → 後ろに「users/auth/apple/callback」を追加したもの
```
一つ注意点として、Sign in with Appleでは「localhost」が使用できません。

したがって、

```
localhost:3000
http://localhost:3000/users/auth/apple/callback
```

などと記述しても、不正な値として弾かれてしまいます。

そこで、「ngrok」などをいローカルサーバーに適当なドメインを割り当ててもらう方法で今回は試します。

参照記事：[ngrokが便利すぎる](https://qiita.com/mininobu/items/b45dbc70faedf30f484e)

![80912200-4e726a80-8d76-11ea-9ca3-a87b3deb3854.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/688854/6faea949-73bf-1ee6-61ac-4ae180349e78.png)

今回の場合はターミナルで

```
$ ngrok http 3000
```

と叩き、生成されたURLを先ほどのページに記述すればOKです。

### ビューを作成

大体の準備は完了しましたが、ビューをまだ作成していないので適当に作ります。

```
$ docker-compose run web rails g controller home index
```

```erb:app/views/home/index.erb
<h1>Sign in with Apple</h1>

<% if user_signed_in? %>
  <%= link_to 'logout', destroy_user_session_path, method: :delete %>
  <h1>Hello、<%=  current_user.email %>！</h1>
<% else %>
  <%= link_to 'login', user_apple_omniauth_authorize_path %>
<% end %>
```

### ルーティングを設定

ここまでに実装した機能が正常に動くようにルーティングを設定します。

```rb:./config/routes.rb
Rails.application.routes.draw do
  root to: 'home#index'
  devise_for :users, controllers: { omniauth_callbacks: 'users/omniauth_callbacks' }
end
```

## 動作確認

![スクリーンショット 2020-12-25 11.31.35.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/688854/a1fe8bfc-2b75-bf78-04ee-7a0ab8b0f82d.png)

ngrokで立ち上がったURLへアクセスし、↑のような画面になる事を確認。

![80911472-8dea8800-8d71-11ea-8b4d-0fd215100e15_censored.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/688854/b40d2a18-8c58-0b81-704f-0a1add7801d1.jpeg)

「login」ボタンを押すとSign in with Appleのページへ飛ぶので、自身のAppleIDとパスワードをそれぞれ入力。

![スクリーンショット 2020-12-25 11.42.46_censored.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/688854/5150f59b-5dac-fc2b-fcdc-ea062d0a016d.jpeg)

認証に成功すると、自身のメールアドレスが表示された画面に切り替わります。（「privaterelay.appleid.com」はSign in with Apple用に付与される独自のドメイン）

以上でSign in with Appleの実装は完了です。お疲れ様でした！

## あとがき

Apple Developerへの登録だったり各種証明書の取得は面倒そうな印象を受けましたが、コードの実装自体は案外簡単だった気がします。

もしかするとUIが変わったりするかもしれませんが、基本的な考え自体は変わらないと思うので是非とも参考にしていただけると幸いです。