Google Cloud APIの認証設定

## はじめに
Rails初学者です。GCのStorageとAPIを使用したアプリケーションをHerokuにデプロイする際、認証設定に苦労したのでメモを書き留めておきます。

【使用環境】
・MacOS BigSur
・Ruby 2.6.5, Ruby on Rails 6

## GCPのサービスアカウントについて
サービスアカウントを作成するとjson形式の鍵ファイルをダウンロードでき、そのファイルパスを環境変数``GOOGLE_APPLICATION_CREDENTIALS``に設定することで認証ができます。

```
export GOOGLE_APPLICATION_CREDENTIALS="/home/user/Downloads/my-key.json"
```
しかし、この鍵ファイルをGitやHerokuに渡すわけには行きません。

また、公式ドキュメントにありますが、開発環境と本番環境ではサービスアカウントを使い分ける必要があるようです。そのため、Rails 6からサポートされたMulti environment credentialsでそれぞれの秘密鍵情報を記載しました。

## Multi environment credentialsの設定
credentialsファイルから読み込む設定

```config/storage.yml
google:
  service: GCS
  credentials:
    type: "service_account"
    project_id: <%= Rails.application.credentials.dig(:gcs, :project_id) %>
    private_key_id: <%= Rails.application.credentials.dig(:gcs, :private_key_id) %>
    private_key: <%= Rails.application.credentials.dig(:gcs, :private_key).dump %>
    client_email: <%= Rails.application.credentials.dig(:gcs, :client_email) %>
    client_id: <%= Rails.application.credentials.dig(:gcs, :client_id) %>
    auth_uri: "https://accounts.google.com/o/oauth2/auth"
    token_uri: "https://oauth2.googleapis.com/token"
    auth_provider_x509_cert_url: "https://www.googleapis.com/oauth2/v1/certs"
    client_x509_cert_url: <%= Rails.application.credentials.dig(:gcs, :client_x509_cert_url) %>
  project: <%= Rails.application.credentials.dig(:gcs, :project) %>
  bucket: <%= Rails.application.credentials.dig(:gcs, :bucket) %>
```
次のコマンドで本番環境用のcredentialsファイルを作成

```
% bundle exec rails credentials:edit --environment production
```
configディレクトリ内にcredentialsというディレクトリが作成され、設定ファイルとキーファイルが対で生成されます。
config
　∟**credentials**
　　∟ **production.key**
　　∟ **production.yml.enc**
　∟ master.key
　∟ credentials.yml.enc

設定ファイルは暗号化されているため、次のコマンドでviを使用して編集します。

```
% EDITOR="vi" bin/rails credentials:edit --environment production
```

```config/credentials/production.yml.enc
secret_key_base: ******
gcs:
  project_id: ******
  private_key_id: ******
  private_key_id: ******
  private_key: "-----BEGIN PRIVATE KEY-----\n******************
***************************************************************
*********************************\n-----END PRIVATE KEY-----\n"
  client_email: ******@******.iam.gserviceaccount.com
  client_id: ******
  client_x509_cert_url: https://www.googleapis.com/robot/v1/metadata/x509/******.iam.gserviceaccount.com
  project: ******
  bucket: ******
```
secret_key_baseは元々のcredentials.yml.encと同じ。
サービスアカウントを作成して、ダウンロードしたjson形式の鍵ファイルの中に記載されている各項目を書いていきます。private_keyはとても長く改行文字を含んでいるためダブルクォーテーション（”）で囲みます。

そして、production.keyの中身をHerokuに環境変数 RAILS_MASTER_KEYとして保存します。

```
% heroku config:set RAILS_MASTER_KEY=`cat config/credentials/production.key`
```

## これでいけるかと思いきや
GCSへのデータアップロードは出来るのですが、APIにリクエスト送信すると「認証できないよ」というエラーが。結局、環境変数``GOOGLE_APPLICATION_CREDENTIALS``を読み込めないと駄目なのか。

いろいろ調べた中で、次の方法を試しました。

まず、Herokuに任意の名前で環境変数を作成、鍵ファイルの中身を保存する。

```
% heroku config:set GOOGLE_CREDENTIALS="$(< /PATH/TO/my-key.json)"
```

次に、appディレクトリ内に空のjsonファイルを作成し、そのjsonファイル内に先程の環境変数を出力するという記述を.profileファイル内に書き込む。
aplicationディレクトリ
　∟app
　　∟**gcp_key.json**
　∟**.profile**

```gcp_key.json
```

```:.profile
echo ${GOOGLE_CREDENTIALS} > /app/gcp_key.json
```

最後に、Herokuに環境変数``GOOGLE_APPLICATION_CREDENTIALS``にjsonファイルパスを設定。

```
% heroku config:set GOOGLE_APPLICATION_CREDENTIALS=/app/gcp_key.json
```

.profileに記述したコマンドは、Herokuのアプリケーションを格納するコンテナ（Dyno）が起動すると同時に実行されるとのこと。

ここまでの設定で漸く本番環境でも期待の動作をしてくれました。

## 補足　サービスアカウントのロール（権限）について
オーナー、編集者、閲覧者といった基本ロールの他に、膨大な数のロールが用意されています。基本ロールは設定権限が多いため、本番環境には使用するなとドキュメントにはあったものの、どれを選んで良いものか。
今回、色々と試行した中では「**サービスアカウントユーザー**」（オペレーションをサービスアカウントとして実行）と「**ストレージ管理者**」（GCSリソースの全てを管理）の２つを選んで上手く行きましたが、今後不具合などあれば加筆します。