[Bitrise+fastlane]2FA必須化をさくっと解決する

## 2FA（2ファクタ認証）が必須に

### すべてのロールで2FA必須
2019年にAccount Holderでの2FAが必須になりましたが、ロールに関係なく2FAが必須になったようです。
https://developer.apple.com/jp/support/authentication/
`2021年3月以降、App Store Connectにサインインするすべてのユーザーは、2ファクタ認証または2ステップ確認を行う必要があります。`

### これまでのBitriseでの対応方法

これまではAccount Holder権限以外のアカウントを使用していて、2FAを無効にしてBitriseに設定していました。
しかし、Account Holder以外も2FA必須化の対象になった影響で、本番リリース用のWorkflowにも影響が発生して、2FAエラーで失敗するようになっていました。

最初に出たエラーはこのようになっていて、非常に分かりづらかったです。

```bash
Need to acknowledge to Apple's Apple ID and Privacy statement. Please manually log into https://appleid.apple.com (or https://appstoreconnect.apple.com) to acknowledge the statement.
```

同じようなエラーで困っている方の参考になれば幸いです。

## 前提条件

- Bitriseを使用している。
- BitriseのWorkflowでfastlaneステップを使用している。
- fastlaneの本番申請用の設定ではpilotを利用している。（Test Flightにアップロードして、App Store Connectで手動でビルドを選択する方法）
- fastlane ver 2.176.0

このような前提条件ですが、やり方やCIが違っても設定などは参考になる部分があると思います。
調べた感じだと色々とやり方があるようです。
FASTLANE_SESSIONなどはセッション切れのタイミングで再度対応する必要がある部分があります。
App Store Connect APIを使用したやり方は一度設定してしまえば変更の必要がないので現状（2021年3月現在）だと一番良いかなと感じています。（個人の感想です）

## 対応内容

実はタイトルのようにサクッと解決したわけではありません。
いろんな記事を見て、激闘の末になんとか解決しています。
ただ、実際の対応内容はそんなに大したことはありません。

### App Store Connect APIを利用する
[App Store Connect API](https://developer.apple.com/jp/app-store-connect/api/)を利用すれば2FAを気にする必要はありません。

### App Store Connect APIキーを発行する
App Store Connect APIキーを発行済みの方は飛ばしてください。
まず、Admin以上の権限でApp Store Connectにアクセスしてください。
[キーを発行するにはAdmin以上の権限が必要になるからです。](https://developer.apple.com/documentation/appstoreconnectapi/creating_api_keys_for_app_store_connect_api)
「ユーザとアクセス」から「キー」をクリックします。

![スクリーンショット 2021-03-03 0.07.24.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/596715/34610c7a-b4e4-cb8e-9756-49a0ec251dea.png)

そうすると、こちらの画面が表示されます。
+ボタンを押してApp Store Connect APIキーを発行しましょう。
（初めての場合は「APIキーを生成」ボタンが表示されていると思います。）

モーダルが表示されるので、名前とアクセス権限を設定します。
「名前」は識別するためなので好きな名前をつけてください。
私はBitriseで使用することが分かるような名前にしています。
「アクセス」ではアクセス権限を設定します。
プルダウンからBitriseで実行するアップルディベロッパーアカウントの権限に合わせて設定しましょう。
![スクリーンショット 2021-03-03 14.14.52.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/596715/50329e9b-1a01-7050-97fc-5a00ff8ab2d6.png)

### App Store Connect APIキーをダウンロード
キーの発行に成功すると、「APIキーをダウンロード」するボタンがありますのでそこをクリックして、キーファイルをダウンロードします。
このファイルは1度しかダウンロードできません。
ダウンロードしたらきちんと保管してください。

## Bitrise側の設定
APIキーを発行し終えたら今度はBitrise側の設定に移ります。
先程のキーを発行した画面にある「Issuer ID」、「キーID」、「ダウンロードしたキーファイル」を使用します。

![スクリーンショット 2021-03-03 0.07.24.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/596715/34610c7a-b4e4-cb8e-9756-49a0ec251dea.png)

### Bitriseの環境変数を設定

Workflowエディタから環境変数を設定します。
私の環境では、下記のように設定しています。
キー名は自分自身の好きなように決めてください。

APPLE_API_KEY_ISSUER_ID => Issuer ID
APPLE_API_KEY => キーID
APPLE_API_KEY_CONTENT => ダウンロードしたキーファイルの中身

![スクリーンショット 2021-03-03 14.26.56.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/596715/e568b289-b540-d674-2c91-e8ab0d3831f2.png)

ダウンロードしたキーファイルの中身は、テキストエディタなどで開いてコピペしてください。
このような内容になっているはずです。

```
-----BEGIN PRIVATE KEY-----
...........................
-----END PRIVATE KEY-----
```

## Fastfileの編集

次はfastlane側の設定です。

### 編集前

編集前のFastfileはこのようになっていました。
（公開用に一部情報を編集しています）
pilotというのを利用しています。
pilotはTestFlightにバイナリをアップロードするために使っています。
TestFlightにバイナリをアップロードしたら、手動でビルドを選択してAppleに申請をかけています。

```vim
desc "Upload to AppStore"
lane :release_appstore do
  slack(message: ":man-running: start upload to AppStore")
  gym(
    workspace: WORK_SPACE,
    configuration: RELEASE,
    scheme: RELEASE,
    silent: true,
    clean: true,
    output_name: "xxx.ipa",
    export_method: "app-store",
  )

  pilot(
    skip_submission: true,
    skip_waiting_for_build_processing: true
  )

  slack(message: ":tada: successfully uploaded to AppStore")
end 
```

### 編集後

api_keyにapp_store_connect_api_keyの設定をしています。
「key_id」、「issuer_id」、「key_content」に先程Bitrise側で設定した環境変数を記載します。

```vim

desc "Upload to AppStore"
lane :release_appstore do
  slack(message: ":man-running: start upload to AppStore")
  gym(
    workspace: WORK_SPACE,
    configuration: RELEASE,
    scheme: RELEASE,
    silent: true,
    clean: true,
    output_name: "xxx.ipa",
    export_method: "app-store",
  )
  
  # App Store Connect APIキーを設定
  api_key = app_store_connect_api_key(
    key_id: ENV['APPLE_API_KEY'],
    issuer_id: ENV['APPLE_API_KEY_ISSUER_ID'],
    key_content: ENV['APPLE_API_KEY_CONTENT'],
  )

  pilot(
    # APIキーを設定
    api_key: api_key,
    skip_submission: true,
    skip_waiting_for_build_processing: true
  )

  slack(message: ":tada: successfully uploaded to AppStore")
end 
```

## Workflow実行
ここまで完了したらあとは本番リリース用のWorkflowを実行するだけです。
私の環境では、これで無事に成功することができました。