はじめに
とある理由により Sign in with Apple (以下、SIWA
) の実装をしなければならなくなったのですが、Appleの公式ドキュメント を読んでいても、 iOSアプリ + Railsバックエンド という構成のサービスが、一体どのようにして SIWA 機能を実装するかなどの手順や情報が乏しすぎて非常に理解に苦しんだので、OpenID Connect などの知識と組み合わせながら、実装した結果とSIWAで調べたことなどをここに残します。
前提
こちらの記事は、OAuth2認証
、OpenID Connect
の知識を前提に書いております。
僕は、以下の記事が参考になりました。
https://qiita.com/TakahikoKawasaki/items/200951e5b5929f840a1f
https://qiita.com/TakahikoKawasaki/items/498ca08bbfcc341691fe
アプリの実装に関しては一切触れていません。あくまでサーバ側のみです。
各種Appleでの設定に関してはこことかが参考になりそうです。
https://medium.com/identity-beyond-borders/how-to-configure-sign-in-with-apple-77c61e336003
実装例
認証処理の流れと全体像
- ユーザが アプリから AppleID での認証を開始する
- アプリが Appleから
authorization_code
とid_token
を入手する - アプリが 2で手に入れた情報を サーバ側に渡す
- サーバが
authorization_code
を Appleの認可サーバに渡す - Appleの認可サーバが
authorization_code
と引き換えにid_token
などの情報を サーバに返す - サーバが 3で手に入れた
id_token
を 5で手に入れたid_token
で 検証する - サーバが リソースへのアクセスを ユーザに付与する (※こちらは各種サービスの実装依存なので割愛)
SIWAの細かい話
認証処理の実装について
上記の 1と2 の処理において Apple Server は [authorization_code
および id_token
] を アプリに返却しているため、OpenID Connect Section 3. を根拠に Sign in with Apple は "Hybrid Flow" を採用していることが導き出せる。
Hybrid Flow の場合、
https://openid.net/specs/openid-connect-core-1_0.html#HybridFlowSteps
に記載されている方法でユーザの情報を取得しなければならないことが明記されている。
(以下では、SIWA仕様に読み替えて、手順を簡略化する)
- 「クライアント」が、Appleの認可エンドポイントにエンドユーザの認証を行うためのリクエストを送る。
-
Apple
の認可エンドポイント から「クライアント」にauthorization_code
とレスポンスタイプによって1つ以上の付加パラメータを送る。SIWA場合、付加パラメータがid_token
やname
である。 - 「クライアント」が、
authorization_code
を利用してApple
の トークンエンドポイントにリクエストを送り、id_token
やaccess_token
が含まれるレスポンスをもらう。 - 「クライアント」は トークンエンドポイントから返ってきた
id_token
の検証を行い エンドユーザの識別情報を得る。
ここで、「クライアント」(client)と言っているのは 「アプリ + サーバ」 であることに留意したい。
そのため、「誰が」認証エンドポイントにリクエストを投げ、「誰が」トークンエンドポイントにリクエストを投げて検証するかは、完全に実装する側に委ねられている。
OAuth 2.0 RFC 6749 の clientの定義より
client
An application making protected resource requests on behalf of the
resource owner and with its authorization. The term "client" does
not imply any particular implementation characteristics (e.g.,
whether the application executes on a server, a desktop, or other
devices).
1と2 (ユーザ認証処理) をアプリが行い、3と4 (認証情報の検証)に関しては サーバが行うような実装とするのが適切であろう。
サーバ側で行わなければならない検証項目は、OpenID Connect Section 3.3.3.5. を根拠に特に以下の2つである。
A. authorization_code
の検証
B. id_token
の 検証
以下に、それぞれについて詳しく書く。
A. authorization_code
の検証について
検証方法
検証内容
- アプリからサーバに送られてきた
authorization_code
がid_token
とペアであることを検証する。- この検証に失敗する場合、第三者が不正に取得した
authorization code
あるいは、id_token
を忍び込ませている可能性がある
- この検証に失敗する場合、第三者が不正に取得した
B. ID Token の検証について
検証方法
検証内容について
- 発行者(
iss
)情報が、両方のid_token
で一致していること - ユーザの一意な識別子(
sub
)が、両方のid_token
で一致していること -
nonce
値が、両方のid_token
で一致していること- All Claims about the Authentication event present in either SHOULD be present in both. の解釈より
-
email
などのユーザ情報に関する値が 両方のid_token
で存在する場合に一致していること- If either ID Token contains Claims about the End-User, any that are present in both SHOULD have the same values in both. の解釈より
-
id_token
が失効していないこと- 失効しているトークンを受けないようにするため
-
id_token
の jwt の署名を確認すること- Appleの秘密鍵によって、署名が行われていることを確かめることで発行者情報を強固にする
- 逆に署名が誤っている場合は、元のトークン情報が改ざんされている可能性がある
SIWA と 一般的な OpenID Connect 準拠の認証方法との違いについて
丁寧にも、OpenID Connect財団の方たちが、「SIWA が 一般的なOpenID Connectの仕様と、どういう点で異なるのか」を Peculiarities の欄で全て列挙してくれている。
https://bitbucket.org/openid/connect/src/default/How-Sign-in-with-Apple-differs-from-OpenID-Connect.md
以下、Peculiarities 欄を一部訳すると以下のような事が書かれている。
-
Discovery document 用のエンドポイントが提供されていないので、開発者がエンドポイントやスコープ、署名アルゴリズムなどの情報を得たいと思ったときに、毎度Appleのドキュメントを精読する必要があること。本来は Discovery document を用意するのが推奨されている。
-
UserInfo エンドポイントが提供されていないので、ユーザに関する情報は全て(失効する・潜在的に大きなデータになり得る)
id_token
に含まれる事になっていること。 -
アプリケーションからの
scope
の指定は、「最も最初のリクエスト(すなわち連携時)」のときのみ有効になっている。もし、連携時のときに 氏名(name
)だけのscope
を指定してユーザが許可した場合、そのユーザのemail
は二度とアプリケーション側から取得しに行くことは出来ない。
(アプリケーションからユーザ情報の取得を行う処理の不自由さを指している)
... など、かなり独自の実装が行われている事が伺える。
公式ドキュメント
Apple
- sign in with apple を利用したユーザの認証について
- ユーザの検証方法について
OpenID Connect
OAuth 2.0
- トップページ