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の使用を停止」をクリック

終わりに

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

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

どや会!

はじめまして。9月にオトバンクに入社しました、エンジニアのyukimuraです。 入社してもうすぐで半年・・時間の流れが本当に早いですね〜。

さて、オトバンクではリモートワークが推奨されており、エンジニアもリモートワークで働いている方が多いです。 普段はslackでコミュニケーションをとっていますが、月一くらいは顔を合わせてわいわいしたいよね・・

ということで、 開発チームで過去に行なっていた「どや会」が復活しました😄

今回はどや会とは何?というところから、どや会の様子を紹介していきたいと思います。

どや会とは?

  • 月に1回、月末に開催
  • 開発メンバー全員でご飯を食べながら、今月のリリースをふりかえります
  • 1ヶ月の働きをねぎらうべく、毎回おいしいご飯を用意しています

どや会のアジェンダ

  1. 乾杯
  2. 今月のリリース共有
  3. 雑談 「どや会」は乾杯からはじまります! リリースの共有では、GithubのcloseされたPullRequest一覧と事前資料をベースに行います。 事前資料には自主的に書き込む項目があり、「これはイケてるっしょ!」というものを各自書き込み&発表(どやぁ)してもらいます。

9月のどや会 〜第1回目〜

第1回目のどや会は社内で開催しました!

乾杯の後はご飯を食べながら各自今月のリリース内容等をどやぁしていきます。
私は入社したてで分からない内容もありましたが、先輩方のどやぁを聞きながら一緒に分からない事を教えていただき勉強になりました!

気付いたら途中からゲーム大会に😎

10月のどや会 〜スーパー銭湯回〜

10月は会社近くの「らくスパ」というスーパー銭湯で、リモートワーク & どや会を行いました。

スーパー銭湯ですが、コワーキングスペースがあり仕事ができます!

f:id:yukimura0301:20200123205124j:plain

どや会はイベントルームにて。

f:id:yukimura0301:20200123205651j:plain

各自好きなドリンクを頼んで乾杯!
どでかハイボールを飲む方もいれば、かわいい( ? )メロンソーダを飲む方も🐸

f:id:yukimura0301:20200124123219j:plainf:id:yukimura0301:20200124123225j:plain


仕事が終わったら一汗流して美味しいものを食べる・・最高でした〜😇

11月のどや会 〜リモート勉強会〜

11月のどや会は社内で行いました。
どやぁ!が終わった後、LT勉強会「kyotoLT」さんに参加させていただきました。
「kyotoLT」さんは京都で開催されているLT形式の勉強会です。

kyotolt.connpass.com

オトバンクの事務所は東京なので直接参加はできませんが、京都の会場にリモート接続して参加させていただきました。
お互いリモート接続は初の試みでしたが、無事接続できて沢山勉強になるお話を聴くことができました!

オトバンク からは @kalibora さんが発表してくれました。

こちらは発表の資料です。
オトバンク はAPIの開発にswagger(OpenAPI)、静的解析ツールとしてphpstanを利用しております。
どのように利用しているか気になる方は是非資料を読んでみてください!
私はうっかりミスが多いのでphpstanにはいつも助けられています😅

speakerdeck.com

kyotoLTの皆さん、ありがとうございました!

1月のどや会 〜ピザ回〜

1月はどやぁ!の後はピザを食べながらゆる〜く雑談。
エンジニア以外の方々も集まってくれて楽しかったです!

まとめ

どや会は普段リモートワークで交流が少ないメンバーともお話ができて良いイベントだなと思います。その回の幹事によって方向性も違うため毎回面白いですね!

1月・2月は大型の開発案件があり開発チームは忙しくなりますが、次回のどや会を楽しみに頑張っていきましょう〜。

GitHub Actionsで業務を効率化する ~OSSライセンス情報の自動反映 編~

どうも、けいまです。 最近は日中の暑さも和らぎ、夜は窓を開けていると涼しくていい気候になってきましたね。

さて、今回はスマホアプリの開発チームの業務改善をした話をします。

重要だけど毎回気にするほどでもないので忘れがちなタスク

例えばOSSライセンス情報のような、重要だけど毎回気にするほどでもないので忘れがちなタスクってありますよね。 「アプリ内のOSSライセンス情報の更新」などがまさしくそういったタスクだと思います。 気の利くメンバーが「ライセンス情報更新した?」と聞いてくれて初めて「アッやべぇ」と思い出したり。。。 そういったタスクはCIにやらせましょう、というのは、モバイルアプリ開発においては定番の発想であったりします。

しかし、PRのたびにライセンス情報を精査してdiffがあればDangerで警告するような対応も、 まぁ出来なくはないですが、そこまでやるほど頻繁に起こることでもないので、ずっと対応が後手に回っていました。

せっかくなので、毎週末にCIにライセンス情報更新のPRを作るところまでやって貰えたら凄くハッピーだな、と思いました。

PRをつくるのって意外と難しい!

弊社ではBitriseを3並列で運用しています(並列数のアピールは、いちおうこういうところにおカネかけてますよアピールですっ)。 Bitriseには定期実行トリガーの機能があるので、この機能を使えば定期的な実行は実現できそうです。

ところが、BitriseのstepsにはGitHubにPRを作成するstepが存在しないようです。これは困った。 別の解決方法としてはfastlaneを使う方法があるのですが、Androidプロジェクトとの兼ね合いもありRubyに依存したくなかったので断念。。。

docs.fastlane.tools

fastlaneなどの実装からBitrise stepsを作成することも考えたのですが、そこまでやる余力はありませんでした。

GitHub Actions リリース!

そんな最中に弊社リポジトリでもGitHub Actionsが解禁されました。 バックエンドはAzure Pipelinesベースなのだとか

まぁBitriseを辞めることは今は考えていないものの、軽量なワークフローはActionsでやらせたいね~と情報収集していたところ、 まさにずっと探し求めていたPullRequestを作成するActionがありました!

github.com

(発見当初はLinuxコンテナベースでのWorkflowにのみ対応していました。よってmacOSコンテナやWindowsコンテナでは動作しませんでした。しかし現在はベータ版扱いとしてすべてのプラットフォームで動作するようになっています)

そんなわけでせっせとWorkflowを書き、目的の動作を得ることが出来ました!

解説

name: Update licenses files

on:
  schedule:
  # “At 19:00 on Friday (UTC)” -> At 05am on Saturday (JST)
  - cron: '00 19 * * FRI'

jobs:
  build:

    runs-on: macOS-latest

    steps:
      - uses: actions/checkout@v1

      - name: set up JDK 1.8
        uses: actions/setup-java@v1
        with:
          java-version: 1.8

      - name: Read .node-version
        run: echo "::set-output name=NODE_VERSION::$(cat .node-version)"
        id: node

      - name: Use Node.js (w/ .node-version)
        uses: actions/setup-node@v1
        with:
          node-version: "${{ steps.node.outputs.NODE_VERSION }}"

      - name: Setup yarn
        run: npm install -g yarn

      - name: yarn install
        run: yarn install

      - name: Run updateLicenses.sh
        run: ./scripts/updateLicenses.sh

      - name: Create Pull Request
        uses: peter-evans/create-pull-request@v1.2.1-multi
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          REPO_ACCESS_TOKEN: ${{ secrets.REPO_ACCESS_TOKEN }}

これが我々のチームで使用しているWorkflowの現物です。 順に解説すると:

  • .on.schedule[].cron
    • UTCで毎週金曜日19時に実行されるようにしました。日本標準時だと土曜日の朝5時。
  • .jobs.build.runs-on: macOS-latest
    • 弊社アプリはReact Nativeを採用しています。よって、iOSもAndroidも面倒が見れるmacOSコンテナを使用します
  • .jobs.build.steps
    • Actionを記述しています。
    • 前半はmacOSコンテナにないものを用意しています。
    • 後半は yarn install した後に updateLicenses.sh を実行しています。
    • .node-versionを読んで実際にインストールするnodeバージョンを指定するくだりはけっこうエグい感じですが、こういう感じっぽいです(記法どうにかならなかったのか?)

updateLicenses.shについては、おおよそ以下の処理をしています。

  • npmパッケージ一覧からライセンス情報を含むJSONを生成する
  • iOSはCocoaPodsが Pods-***-acknowledgements.plist なファイルを生成しており、中にはライセンス情報が含まれているので、 plutil -convert json でJSONに変換します
  • Androidは cookpad/license-tools-plugin を使っているので(licenses.ymlを設定するダルさを乗り切れば) ./gradlew checkLicenses generateLicenseJson で済みます

生成されたJSONはRN側ではファーストクラスのオブジェクトとして扱えるので、あとは煮るなり焼くなり。

peter-evans/create-pull-request についてですが本記事を書くに当たり調査したところ、どうも REPO_ACCESS_TOKEN は設定不要のようです。 この設定は意外と説明が面倒くさかったので、省けて良かったです。

github.com

実際の動作

さて実際の動作としては、けなげに土曜の早朝にPRをつくってくれています。カワイイ!

f:id:p-side:20191009010328p:plain

これで我々のチームはまたひとつ本業に専念することが出来るようになりました。。。!

Actions実装で気をつけること

Actionsを実装するうえで気をつけることとして、現時点ではいくつか問題があります。

1. HCL記法は使えず、YAML記法で書く

少し前までGitHub ActionsではHCLという記法が採用されていましたが、現在は使うことが出来ず、YAML記法のみサポートしています。 しかし、一部の古いActionのREADMEではHCL記法でのサンプルが書かれているため、読み替える必要があります。

大雑把には以下のように読み替えると良いと思います。

action "Action Name" {
  needs = "Master"
  uses = "org/repo@version"
  args = "branch master"
}
- name: Action Name
  needs = "Master"
  uses: org/repo@version
  with:
    args: branch master

2. Docker container形式のActionはmacOS, Windowsコンテナでは動かない

help.github.com

ここにあるように、Actionは2種類あり、macOS, WindowsコンテナではJavaScriptで実装されたもののみ動作します。 Docker containerスタイルのものはActions実行時にエラーとなり時間の無駄になるので気をつけてください。

どうしても使いたいActionが非対応だった場合はIssueを立ててお願いしてみてもいいかも。

なお、そのActionがJavaScriptスタイルかDocker containerスタイルのどっちで実装されているかは、 リポジトリの action.yml を確認してnodeが使われているならほぼJavaScriptスタイルだと断言できそうです。

runs:
  using: 'node12'
  main: 'index.js'

まとめ

GitHub ActionsはGitHubを使えるならみんな使える機能(のはず)なので、皆さんもGitHub Actionsで業務改善していこうな!

それでは~