LoginSignup
34
28

More than 3 years have passed since last update.

Sign in with Appleをサーバーサイドで実装した

Last updated at Posted at 2020-02-05

Sign in with Apple 実装のデッドラインは4月 ですね☺️
Web/iOS/Androidなど、複数プラットフォームをサポートするためWebViewでログイン機能を実装しているサービスや、外部サービスAPI利用の際に独自のAPIを噛ませているなど、サーバーサイドでSign in with Appleに対応するケースはあるかと思います。

iOSエンジニアであるわたしがサーバーサイドで実装した際に、わかりにくいな〜🤯と感じたポイントについて記載します。
自分のスキルセットとして、認証・認可、Webの知識はほぼ0でした🤯
実装したソースコードをの抜粋を載せていますが、言語はrubyです。

やること

今回ご説明する Sign in with Apple の全体のざっくりフローは、

  1. Apple Developer サイトでの設定
  2. Apple ID サインイン画面表示(Authorizeエンドポイント)、認可コード受取り
  3. 認可コードを使い、AppleのユーザーIDを取得(Tokenエンドポイント)

です。

取得したいデータがemailとnameのみの場合 → ステップ2まで
ユーザーIDを取得したい場合 → ステップ3まで
実装する必要があります。

参考までに前知識

Auth関連

まず最初に、OAuth周りの知識があると理解が早いと思います。
この方のqiitaが非常に参考になりました。(ありがとうございました。)

Appleガイドライン関連

Apple Developer サイトでのもろもろ

ではさっそくApple Developerサイトを開きます。

Servise ID作成からドメイン認証

  1. 対象の App ID を作成し、Capabilities として Sign in with Apple を追加する
  2. Apple Developer の Certificates, Identifiers & Profiles から Service ID を追加する
  3. Primary App ID を 1 のAppに指定する
  4. Domainsに認可コードのリダイレクト先のドメインと Return URLs を指定する
  5. Domainの検証ファイル apple-developer-domain-association.txt をダウンロードする
  6. Domainの検証ファイルをWebサイトの https://.../.well-known/ ディレクトリに置く

Service IDReturn 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 PlatformsAdd 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_modefragmentまたはform_postである必要がある
  • 氏名やメールアドレスを取得したい(scopeを指定する)場合、response_mode = form_postにする必要がある

など、諸々の制約があります。

サインインページが表示されると、ユーザーによるApple IDの認証を受け付けます。
認証が完了すると、指定したリダイレクトURIにリダイレクトが行われます。
受け取れる値は認可コードと、scopeに指定がある場合初回1度のみユーザーID以外の個人情報(メールアドレス、氏名など)が受け取れます。

サインインページがうまく表示されない

invalid_uriinvalid_client が表示されたら、焦らず client_idredirect_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とは、 headerpayload から成るJSON構造をエンコードし電子署名をつけたものです。
デコード&エンコードしたJWTの検証は、 https://jwt.io/ がおすすめです。

rubyでのJWTのエンコード&署名の例は以下です。
jwt/ruby-jwt というGemを利用しています。

kidisssubなどは暗号化しておくと良いでしょう。

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の皆様はできる限りネイティブでやりましょう:tada:

Thanks to

https://qiita.com/kiwi26/items/5b8cc53ed8d10a403f00
https://developer.okta.com/blog/2019/06/04/what-the-heck-is-sign-in-with-apple

以上です🎉🎉🎉
ありがとうございました😽

34
28
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
34
28