Sign in with Apple 実装のデッドラインは4月 ですね☺️
Web/iOS/Androidなど、複数プラットフォームをサポートするためWebViewでログイン機能を実装しているサービスや、外部サービスAPI利用の際に独自のAPIを噛ませているなど、サーバーサイドでSign in with Appleに対応するケースはあるかと思います。
iOSエンジニアであるわたしがサーバーサイドで実装した際に、わかりにくいな〜🤯と感じたポイントについて記載します。
自分のスキルセットとして、認証・認可、Webの知識はほぼ0でした🤯
実装したソースコードをの抜粋を載せていますが、言語はrubyです。
やること
今回ご説明する Sign in with Apple の全体のざっくりフローは、
- Apple Developer サイトでの設定
- Apple ID サインイン画面表示(Authorizeエンドポイント)、認可コード受取り
- 認可コードを使い、AppleのユーザーIDを取得(Tokenエンドポイント)
です。
取得したいデータがemailとnameのみの場合 → ステップ2まで
ユーザーIDを取得したい場合 → ステップ3まで
実装する必要があります。
参考までに前知識
Auth関連
まず最初に、OAuth周りの知識があると理解が早いと思います。
この方のqiitaが非常に参考になりました。(ありがとうございました。)
Appleガイドライン関連
Apple Developer サイトでのもろもろ
ではさっそくApple Developerサイトを開きます。
Servise ID作成からドメイン認証
- 対象の App ID を作成し、Capabilities として Sign in with Apple を追加する
- Apple Developer の Certificates, Identifiers & Profiles から Service ID を追加する
- Primary App ID を 1 のAppに指定する
- Domainsに認可コードのリダイレクト先のドメインと Return URLs を指定する
- Domainの検証ファイル
apple-developer-domain-association.txt
をダウンロードする - Domainの検証ファイルをWebサイトの
https://.../.well-known/
ディレクトリに置く
Service ID と Return URLs はAuthorize、Tokenエンドポイントで利用します。
ドメイン検証に失敗したら
-
https://{yourdomain}/.well-known/apple-developer-domain-association.txt
にアクセスできるか - 検証ファイルは有効期限内か(7日間のみ有効)
- 検証ファイルのダウンロードは何回でも可能なので、常に最新のファイルを使う
- サーバーがTLSの条件を満たしているか
を確認する。
明文化されていなさそうですが、Appleさんからサポートを受けた開発者がフォーラムに書き込んでいます。
https://forums.developer.apple.com/thread/122124
そのほか、ネットワーク構成などに問題がないか等、公式の Troubleshooting Domain Verification を読んでみるとよいです。
Key をダウンロード
Tokenエンドポイントのリクエストパラメータの JSON Web Token 作成に使います。
当然ですが、センシティブなファイルなので暗号化などよしなに行い、サーバーのどこか安全なところに置いておく。
今回はわたしは、.txt
に変換して使いました。
公式ドキュメントは Create a Sign in with Apple private key
Apple IDサインインページ表示
Appleサインインボタン
公式ドキュメント Incorporating Sign in with Apple into Other Platforms の Add a Custom “Sign in with Apple” Button にダウンロードURLやサイズ、カラー指定のクエリパラメータの説明があります。
Human Interface Guidelines にしたがって正しくつかいましょう。
Authorizeエンドポイント
Appleの認証ページURLのクエリパラメータ、レスポンスについては Incorporating Sign in with Apple into Other Platforms に記載があります。
大事なのは、
client_id = Service IDで指定した文字列
redirect_uri = Service IDに設定したリダイレクトURI
です。
-
response_type
=id_token
にする場合、response_mode
はfragment
またはform_post
である必要がある - 氏名やメールアドレスを取得したい(
scope
を指定する)場合、response_mode
=form_post
にする必要がある
など、諸々の制約があります。
サインインページが表示されると、ユーザーによるApple IDの認証を受け付けます。
認証が完了すると、指定したリダイレクトURIにリダイレクトが行われます。
受け取れる値は認可コードと、scope
に指定がある場合初回1度のみユーザーID以外の個人情報(メールアドレス、氏名など)が受け取れます。
サインインページがうまく表示されない
invalid_uri や invalid_client が表示されたら、焦らず client_id
と redirect_uri
を今一度確認しましょう。
開発・本番など環境別でドメインを分けている場合、 redirect_uri
が変わると思いますので要チェックです。
Apple Developer 上で作成した Service ID の Returns URLs に該当のリダイレクト先が登録されているか確認しましょう。
アクセストークン・ユーザーID取得
Tokenエンドポイント
Appleのトークン発行エンドポイントにリクエストを行い、トークンの発行を行います。
パラメータ、レスポンスについては Generate and validate tokens に記載があります。
パラメータは Authorization のものを使います。
client_secret
の値のJSON Web Token(JWT)作成
Apple Developers で発行したKeyを用いて、リクエストパラメータ client_secret
に付与するJWTの作成を行います。
JWTとは、 header
と payload
から成るJSON構造をエンコードし電子署名をつけたものです。
デコード&エンコードしたJWTの検証は、 https://jwt.io/ がおすすめです。
rubyでのJWTのエンコード&署名の例は以下です。
jwt/ruby-jwt というGemを利用しています。
kid
や iss
、sub
などは暗号化しておくと良いでしょう。
require 'jwt'
require 'openssl'
# 署名用の鍵を取得
def pem
file = "path/to/key.txt" # Apple Developerで発行したKeyファイル
# key.txtを読み出し、暗号鍵を生成する
OpenSSL::PKey::EC.new(File.read(file))
end
# 署名付きIDトークンを取得
def id_token
header = {
'kid' => 'Apple Developerで発行したKey ID'
}
claim = {
'iss' => 'Apple DeveloperでのTeam ID',
'iat' => Time.now.to_i,
'exp' => Time.now.to_i + 15777000,
'aud' => 'https://appleid.apple.com',
'sub' => 'Apple Developerで発行したService ID'
}
JWT.encode(
claim,
pem,
'ES256',
header
)
end
レスポンスの検証
レスポンスに含まれるIDトークンの検証には、Appleの公開鍵取得が必要です。
エンドポイントについては Fetch Apple's public key for verifying token signature に記載があります。
jwt/ruby-jwt を用いた検証の実装例です。
# IDトークンをデコードし署名を検証する
# public_keysに渡すのは、https://appleid.apple.com/auth/keys の "keys"の値です。
def decode(id_token, public_keys)
JWT.decode(
id_token,
nil,
true,
{ algorithms: 'E256', jwks: { keys: public_keys } }
)
end
IDなどのユーザー情報
sub
の値がユーザーの一意の識別子(ユーザーID)です。
そのほかのレスポンスのIDトークンに含まれる情報については Retrieve the User’s Information from Apple ID Servers
に記載があります。
あとは取得したデータをもとに自サービスでのユーザー認証を完了させましょう。
おしまい
書くと簡単ですが実際はたいへんだったのでiOS boys and girlsの皆様はできる限りネイティブでやりましょう
Thanks to
https://qiita.com/kiwi26/items/5b8cc53ed8d10a403f00
https://developer.okta.com/blog/2019/06/04/what-the-heck-is-sign-in-with-apple
以上です🎉🎉🎉
ありがとうございました😽