対象読者
- 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
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
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:
source 'https://rubygems.org'
gem 'rails', '~> 5.2.4'
# 空でOK
Railsプロジェクトを作成
$ docker-compose run web rails new . --force --no-deps -d mysql --skip-bundle
$ docker-compose build
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へアクセス
「localhost:3000」へアクセスし、いつもの画面が表示されれれば成功です。
実装
準備ができたのでいよいよ実装に入ります。
各種gemの用意
...
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用のモジュールとメソッドを追加
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」のみです。
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
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のアカウント登録方法
Sign in with Apple を実装するために必要な証明書の作り方
この辺についてはすでに色々な記事が世の中に存在するので各自取得しておいてください。
Return URLsの設定
ここから一瞬だけApple Developer側の操作になります。
最後に、認証が許可された後のリダイレクト先を指定。
Apple Developerのダッシュボードから「Certifications, Identifers & Profiles」へ飛ぶ。
「Identifers」タブを開き、右のプルダウンメニューから「Services IDs」を選択。
「Sign in with Apple」の右にある「configure」をクリックすると「Domains and Sub domains」「Return URLs」を登録する画面が出てくるので、それぞれ記述していきます。
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が便利すぎる
今回の場合はターミナルで
$ ngrok http 3000
と叩き、生成されたURLを先ほどのページに記述すればOKです。
ビューを作成
大体の準備は完了しましたが、ビューをまだ作成していないので適当に作ります。
$ docker-compose run web rails g controller home index
<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 %>
ルーティングを設定
ここまでに実装した機能が正常に動くようにルーティングを設定します。
Rails.application.routes.draw do
root to: 'home#index'
devise_for :users, controllers: { omniauth_callbacks: 'users/omniauth_callbacks' }
end
動作確認
ngrokで立ち上がったURLへアクセスし、↑のような画面になる事を確認。
「login」ボタンを押すとSign in with Appleのページへ飛ぶので、自身のAppleIDとパスワードをそれぞれ入力。
認証に成功すると、自身のメールアドレスが表示された画面に切り替わります。(「privaterelay.appleid.com」はSign in with Apple用に付与される独自のドメイン)
以上でSign in with Appleの実装は完了です。お疲れ様でした!
あとがき
Apple Developerへの登録だったり各種証明書の取得は面倒そうな印象を受けましたが、コードの実装自体は案外簡単だった気がします。
もしかするとUIが変わったりするかもしれませんが、基本的な考え自体は変わらないと思うので是非とも参考にしていただけると幸いです。