56
44

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

【Sign in with Apple】既存サービスに追加で実装するときに役立つ資料まとめ(2019年9月版)

Last updated at Posted at 2019-09-30

概要

2019年9月12日から、新規でApp Storeに提出されるアプリかつ、ソーシャル(サードパーティ)ログインを実装しているアプリにはSign in with Appleの実装が必須になっているそうです。
https://developer.apple.com/news/?id=09122019b

Starting today, new apps submitted to the App Store must follow these guidelines.

Apps that exclusively use a third-party or social login service (such as Facebook Login, Google Sign-In, Sign in with Twitter, Sign In with LinkedIn, Login with Amazon, or WeChat Login) to set up or authenticate the user’s primary account with the app must also offer Sign in with Apple as an equivalent option.

唐突すぎて信じがたいですが英文を何度読んでもそういう状況だということで、いろいろ調べてみましたのでその情報をまとめます。

背景としては、私自身が自社でiOSアプリを開発中で、既存Webサービスのアプリ化のためTwitterログインやメールログインが既に存在し、そのあたりとの連携も実装不可避になっています。
無事にリリースでき安定稼働した暁にはまた記事にまとめようと思いますが、速報として一旦現在分かっていることを記事にまとめさせていただきました。

既存サービスへの追加のため、既存ユーザーがSign in with Appleに乗り換えたときのアカウントのIDを連携するようなユースケースに置かれた方々に参考になれば、と思って書きました。

想定読者

  • 現在すでに運用しているWebサービスのアプリ版をリリースしようとしているアプリエンジニア・サーバーサイドエンジニア
  • およびそれと近しい状況に置かれている方
  • Sign in with Appleの仕組みなんもわからん、という方

役立った資料

一応公式

サーバーサイドでSign in with AppleしたユーザーのID連携をするときは、このあたりが公式ドキュメントになります。

https://developer.apple.com/documentation/Signinwithapplerestapi/verifying_a_user
https://developer.apple.com/documentation/Signinwithapplerestapi/authenticating_users_with_sign_in_with_apple

しかし情報がなさすぎて正直意味がわからないため、常にブラウザの固定タブで開いておきつつ、以下に示していくような非公式のWebサイトを順次巡りながら、公式に書かれていることと照らしていくような進め方を推奨します。

全体の流れ

バックエンドサーバで管理しているIDと、Sign in with Appleで得たIDをどうやって連携するかの全体的なフローがシーケンス図で書かれています。

フローの考え方としては、

1. クライアントアプリでSign in with AppleしてAuthorization CodeおよびID Token(JWT)を取得
2. サーバーサイドでAuthorization CodeまたはID Tokenを検証し、成功すればログイン処理を行う

となります。いくつかやり方が提示されていますので、好みというか要件に沿って選べばいいと思います。

プライベートメールアドレスの仕組みについて

上記のスライドには、
そもそものSign in with Appleの大きな特徴である、**「ユーザーがプライベートなメールアドレスをサービス側に登録し、自身の本当のメールアドレスは隠蔽する」**という仕組みについても書かれています。

これはApple側で、「ユーザーの本当のメールアドレスとランダムに作成した偽のメールアドレスをリレーする」仕組みを用意していて、サービス側に伝えるのは偽のほうにしておくというものです。サービスから通知とかメルマガで偽のメールアドレスに送ると、Appleのメールサーバー側で転送してくれます。

何の意味がある仕組みかというと、普通、ユーザーがサービスを退会してもサービス側でユーザー情報を論理削除するか物理削除するかなんて利用規約次第なところがあって、論理削除の場合はずっとメールアドレスを保持されるためなにかの拍子に流出するリスクなどがあります。それを根本的に防ぐことができる仕組みです。

68枚目のスライドにめっちゃ重要なことが書かれていて、
ユーザーはApple IDの管理画面からサービスとの連携を解除するというのができて、その場合はプライベートメールアドレスは変わってしまうため、再度Sign in with Appleしたときにメールアドレスの更新処理をする必要があります。

要は初回のSign in with Appleなのか、何回目かのSign in with Appleなのか、連携解除後の再度Sign in with Appleなのかで処理が変わってくるわけで、気をつけてテストしないと取り返しのつかないことになりそうです。

必要なシークレットやclient_idの生成方法

英語ですが、このサイトが非常によく書かれています。

実際にサーバーサイドからAuthorization Codeを検証するリクエストを飛ばそうと思ったら、client_idと呼ばれるよくわからないIDに、client_secretというよりよくわからないシークレットを付けて投げることになります。
https://developer.apple.com/documentation/Signinwithapplerestapi/verifying_a_user

そもそもAppleのコンソールからSign in with AppleをONにする必要があり、その中で発行されるKeyを使ってclient_secretをJWTで生成するといった特徴的なフローがあります。
それがこの記事にはスクショ付きで一通りまとめてありますので、これに従って作業を進めればOKです。

特にclient_secret生成が面倒で、秘密鍵やKey ID、client_idといったいくつかの値を使ってJWTを生成しています。上記記事ではRubyで書いてありますが、ちゃんと動きますのでこのまま利用できます。ありがたや〜

client_secretはいくつかの値を使ってJWTを作成したものなのですが、有効期限が半年に設定されるのが一般的なため、実運用にあたっては定期的に上記Rubyコードを実行する体制を整え、かつ生成したJWTをどこかセキュアなところに保存しておいてプロダクションのサーバーからその値を読み取るような仕組みが必要かと思います。私はAWS Parameter Storeを使っているので、LambdaでRubyを書いて定期実行でParameter Storeに突っ込むなどの方法を検討中です。

具体的なリクエストやレスポンス

実際にSign in with Apple時に利用するエンドポイントを利用するときにどんなJWTが返ってきたり、どんなJWTを投げるかという踏み込んだ部分について書いてあります。

あと、私は使いませんでしたが、Auth0についても書いてあるので、Auth0使っている人は参考になるかもです。

ユーザーのメールアドレスや名前を得るタイミング

上記のスライドにはハマりやすいポイントが書いてありまして、
33ページ目の**「認可レスポンスにユーザー自身のメールアドレスや名前が返ってくるのは初回のみ」**ということです。
※Webから実行したときの挙動として確認済み

そのユーザーにとって一度目または連携解除後の初回のSign in with Apple時のみ、認可エンドポイントを叩いた後のレスポンスにユーザー自身のメールアドレスや名前が含まれているということです。

Sign in with Appleしたユーザーを既存ユーザーと紐付けたいとなれば、メールアドレスで紐付けるのが定番かと思いますが、この初回タイミングを逃してしまえば連携を解除してもらわない限りできなくなってしまうので要注意です(自動で連携するのではなく、プロフィール画面などに連携するボタンがあり、押したら連携するようなUXにすれば対応可能かとは思います)。

まとめ

まずはヤフーさんのスライドを読んで、どういったシーケンス図で実装しようかという目立てをします。

次に、紹介した英語の資料を読みながら、Appleのコンソールで秘密鍵の作成やSign in with AppleをONにします。
また、**client_secret**の生成もやってみます。

ここまで終わったら、Sign in with Appleのボタンを実際にアプリやブラウザに配置して押してみましょう。

ブラウザならこちらの記事、
https://developer.apple.com/documentation/Signinwithapplejs/configuring_your_webpage_for_sign_in_with_apple

アプリならこのあたりが参考になります。
https://developer.apple.com/documentation/authenticationservices

ボタンを押してみると、ブラウザの場合はredirect_urlで指定したエンドポイントへのリクエストに、アプリからならコールバックでのイベントハンドラでAuthorization CodeやID Token、また初回リクエストの場合はメールアドレスや名前が返ってきているはずです。

次に、バックエンドでID連携処理をやるためにAuthorization CodeやID Token、およびメールアドレスや名前を自前で構築したバックエンドAPIに投げます。
※CRSF対策も必要ならよしなにやってください。ID Tokenの検証の場合はただのJWT検証になるため特に必要だと思います。

バックエンドではID TokenのJWTを復号して中身のissとかsubが妥当かチェックする、またはAuthorization Code/auth/tokenエンドポイントに投げてCodeの検証をします(補足ですが、前述のclient_secretが必要云々はAuthorization Codeの検証をするパターンの場合のみです)。

Authorization Codeの検証の場合は、リクエストが成功して、かつAppleへのアクセストークンなど含まれた正常なレスポンスになっていることがチェックできたら、ID連携処理を実行して、必要なアクセストークンをアプリ側に返してあげることでログイン処理完了とする、というフローがいいと思います。

最後に

公式ドキュメントの情報が少なすぎて錯乱しましたが、結果、かなりユーザーフレンドリーでセキュアに設計されたいい仕組みだなあと思っています(洗脳されただけかも)。

もし私と同じような状況で実装した方がいらっしゃったらぜひ情報共有しましょう。

Twitter
https://twitter.com/Meijin_garden

また、弊社では中高生が利用する勉強質問サイトを運営しています。週1〜の副業から一緒に開発するメンバーを募集中ですので、Webエンジニア、iOSエンジニアの方はいつでもお問い合わせください!
https://www.wantedly.com/companies/noschool

56
44
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
56
44

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?