OTOBANK Engineering Blog

オトバンクはコンテンツが大好きなエンジニアを募集しています!

Sign In with Apple サーバ実装時に悩んだポイント〜名前・emailが取得できない編〜

こんにちは。 コロナウイルス対策で引きこもり属性が高まっているサーバエンジニアyukimuraです。

突然ですが、アプリのソーシャルログインといえば何が思い浮かびますか?


Facebook?


Google?


Apple?





あれ、Appleのサインインボタンってあんまり見たことないな〜と思った方はいないでしょうか?


それもその筈、Appleのソーシャルログインは、2019年6月3日にAppleが発表した新機能なんです。

このAppleによるソーシャルログイン、「Sign In with Apple」ですが、既にApple以外のソーシャルログインを実装しているIOSアプリは、2020年6月30日までに実装する必要があります。(2020/3/30現在)
※ 改定の可能性があるため、最新の情報はAppleのガイドラインを確認してくださいね。

ですので今後、Sign In with Appleを実装したアプリが増えていくことと思います!

弊社のアプリaudiobook.jpもただいまSign In with Appleを絶賛実装中です😁
audiobook.jpにはIOSアプリ・androidアプリ・webアプリがありますので、それぞれに実装を行なっています。 私はサーバサイドエンジニアとして認証api・webアプリのサインイン機能の実装に携わっております。

これから実装する方もいらっしゃるのではと思いますので、私が実装時に悩んだ内容を残しておこうと思います!

名前・メールアドレスが取得できず困る

まず、webアプリケーションにsiwaを実装する場合、認証用のURLを用意する必要があります。 (Sign In with Apple JSを利用する場合はパラメータをセットするだけで自動的にURLが生成されますが、利用しない想定です。)

URLに必要なクエリパラメータについては公式ページに記載があります。 https://developer.apple.com/documentation/sign_in_with_apple/sign_in_with_apple_js/incorporating_sign_in_with_apple_into_other_platforms

さて、URLを組み立ててAppleの認証画面で認証すると、認証したユーザの名前とメールアドレスが取得できるはず・・。

ということで以下のようなURLを作成して試してみました。

https://appleid.apple.com/auth/authorize

クエリパラメータ
  client_id = xxx (アプリ毎に異なるid)
  redirect_uri = xxx
  response_type = code id_token
  response_mode = form_post

認証の結果、以下認可codeとIDトークンを取得できました。

code: xxxxxxx
id_token:yyyyyy

取得したIDトークンをデコードすると・・(値はサンプルです)

{
  "iss": "https://appleid.apple.com",
  "aud": "xxx",
  "exp": 1234567890,
  "iat": 1234567890,
  "sub": "xxx",
  "c_hash": "123456",
  "auth_time": 1234567890,
  "nonce_supported": true
}

email・名前は含まれていませんね。 よくURLを見てみると、scopeを設定していなかったためscopeを指定して再試行します・・

https://appleid.apple.com/auth/authorize

クエリパラメータ
  client_id = xxx (アプリ毎に異なるid)
  redirect_uri = xxx
  response_type = code id_token
  response_mode = form_post
  scope = email name

しかしレスポンスされるデータ構造には変化がなく、email・名前が取得できません・・!

原因

Appleとの初回認証時にscopeを設定しておく必要がありました。 2回目以降の認証だと、scopeを設定しても効果はありません。

初回認証時にscopeを設定している場合は以下のような結果が取得できます。

code: xxxxxxx
id_token:yyyyyy
user: {
  "name":{"firstName":"太郎","lastName":"音羽"},
   "email":"otohatarou@example.com"
}

userというフィールドが追加され、名前とemailが取得できました!

取得したIDトークンをデコードすると・・(値はサンプルです)

{
  "iss": "https://appleid.apple.com",
  "aud": "xxx",
  "exp": 1234567890,
  "iat": 1234567890,
  "sub": "xxx",
  "c_hash": "123456",
  "email": "otohatarou@example.com",
  "email_verified": "true",
  "auth_time": 1234567890,
  "nonce_supported": true
}

IDトークンにもemailが含まれています。 なお、Appleで初回認証時にメールの共有・非公開を選択できますが、共有を選択した場合にはemail_verifiedtrue、非公開を選択した場合にはfalseになります。

注意点として、このscopeを設定していた場合でも2回目以降の認証の場合userフィールドは取得できません・・。 2回目以降でもemailはIDトークンに含まれているため、2回目以降で取得可能なのはemailのみとなります。

ユーザが初回認証後操作をキャンセルして再認証した場合、名前は取得できなくなりますので、名前を取得できないことも考慮して実装を行う必要があります。
Facebookのようにユーザ情報を取得するUserInfo Endpointがあれば良いのですが、今のところ残念ながらそのようなapiは用意されていないんですよね・・。

再度初回認証を試したい場合

AppleIDのアカウントページから自分のアプリの紐付け情報を削除すれば、再度初回認証の確認ができます。 https://appleid.apple.com/#!&page=signin

ログイン後、以下手順で紐付け情報を削除できます。

> 「セキュリティ」を選択
> 「Apple IDを使用しているAppとWebサイトの管理… 」を選択
> ご自分のアプリを選択
> 「Apple Idの使用を停止」をクリック

終わりに

ソーシャルログインの実装に慣れている方であればすぐに原因が分かる内容かもしれませんが、認証系の実装は初めてだったため原因が分からず苦労しました・・!

この内容がお役に立てたようでしたら嬉しいです😊