1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

JSONなしでGoogleAPIやFirebaseに認証する方法 ~CloudFunctions サーバーレスでの認証~

Last updated at Posted at 2023-09-07

Google系のサービスの認証は大体JSON!

2023/9/22追記、9/28修正あり

※Pythonでの紹介となります

プログラムからGoogleSheetの操作をするSheetsAPIやNoSQLでデータが保持できるFirebaseなどGoogle系のサービスに接続する場合、秘密鍵が入ったJSONファイルをダウンロードして、その情報から接続を行います。

これは慣れると便利なのですがCloudFunctionsからGoogleSheetを読み取るものを作ろうとしたとき気づきました…

ファイルを置ける場所がない…

CloudFunctionsなどサーバレスのサービスはどんな小さなファイルもサーバに置いておくことができません。またサーバがある場合でもこのようなファイルを置いておくのは、セキュリティ上なんか不安…なんて方もいるかと思います。

失礼しました。新規でファイルを作成すればmain.pyと同じディレクトリのファイルとして扱うことができます。ただファイルを置きたくない方のために以下の方法を残しておきます。

JSONファイルを使わずに認証をする方法

そこで…

環境変数から取得して、その場でJSONファイル作ればいいじゃん!

ってのを思いつき、プログラマ心をくすぐられたのですが、そんなことする必要はありませんでした。

SheetsAPI:from_json_keyfile_dictを利用

結果から言うとJSONの中身の一部をdict型の変数に入れて、それをセットすればいいみたいです。

Google推奨(修正前)
scope = ['https://spreadsheets.google.com/feeds','https://www.googleapis.com/auth/drive']
credentials = ServiceAccountCredentials.from_json_keyfile_name('auth/charged.json', scope)
gc = gspread.authorize(credentials)
今回の修正後
scope = ['https://spreadsheets.google.com/feeds','https://www.googleapis.com/auth/drive']
credentials = ServiceAccountCredentials.from_json_keyfile_dict(charged_dict, scope)
gc = gspread.authorize(credentials)

わかりづらいですがfrom_json_keyfile_namefrom_json_keyfile_dictになっています。
charged_dictというdict型の変数に(名前はなんでもよい)キーたちをセットし引数にしてやればいいとのことです。

charged_dictへのセットですが、そのダウンロードしたJSONってのはこんな感じなので

charged.json ※修正あり
{
  "type": "service_account",
  "project_id": "charged-polymer-376***",
  "private_key_id": "f822b1********************",
  "private_key": "-----BEGIN PRIVATE KEY----- MIIEv******************,
  "client_email": "google-sheet-263@charged-polymer-376204.iam.gserviceaccount.com",
  "client_id": "115324932356144364298",
  "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": "https://www.googleapis.com/robot/v1/metadata/x509/google-shee",
  "universe_domain": "googleapis.com"
}

このうち一部だけを環境変数にセットします。環境変数名はなんでもいいです。
(キー保存の常套手段として環境変数に入れておきましたが、中身が使えればなんでもいいです)

環境変数へのセット
os.environ["GS_TYPE"] = "service_account"
os.environ["GS_PRIVATE_KEY"] = "-----BEGIN PRIVATE KEY-----\nMIIEm3YZhOzcW\nIV9qjoE4YuzXrs0Zdzq5Vn0=\n-----END PRIVATE KEY-----\n"
os.environ["GS_CLIENT_EMAIL"] = "google-sheet-263@charged-polymer-3******.iam.gserviceaccount.com"
os.environ["GS_PRIVATE_KEY_ID"] = "f822b1207797f********************"
os.environ["GS_CLIENT_ID"] = "11532493235614*******************"

※CloudFunctionsの場合は作成ウィザードの"ランタイム環境変数"でキーと値をセットして下さい

JSONの全てのキーが必要ではなく、SheetsAPI(gspreadでの利用)では
type、private_key、client_email、private_key_id、client_id
の5つのキーだけです。
ただこれはSCOPEにもよると思いますので、試しながらやってみてください。
(キーが足りないとエラーになります。)
後はdict型の変数(charged_dict)にセット。

charged_dict = {
    "type":os.environ["GS_TYPE"],
    "private_key":os.environ["GS_PRIVATE_KEY"],
    "client_email":os.environ["GS_CLIENT_EMAIL"],
    "private_key_id":os.environ["GS_PRIVATE_KEY_ID"],
    "client_id":os.environ["GS_CLIENT_ID"]
}

これでfrom_json_keyfile_dict(charged_dict, scope) により接続が行えるようになります。

「まぁそりゃそうだろ」
って言う方もいるかと思いますが、私は目から鱗でした。

Firebase:dictをぶっこんで大丈夫

Firebase(Firestore)の認証は

cred = credentials.Certificate('auth/test-01.json')
app = firebase_admin.initialize_app(cred)
db = firestore.client()

こんな感じでCertificateの引数にJSONのパスを入れるのですが、これはdictを入れても勝手に判断してくれるようです。2023-09-07 222202.png
なのでSheetsAPIと同じように環境変数へJSONの中身をセットしておき、認証時dict型変数に入れてCertificateの引数にします。ただSheetsAPIとキーが異なるので注意してください。

環境変数へのセット
os.environ["FB_TYPE"] = "service_account"
os.environ["FB_PROJECT_ID"] = "test-01-fc****"
os.environ["FB_PRIVATE_KEY"] = "-----BEGIN PRIVATE KEY-----\nMIIEvQIBADANB***************E KEY-----\n"
os.environ["FB_CLIENT_EMAIL"] = "firebase-adminsdk-******@test-01-.iam.gserviceaccount.com"
os.environ["FB_TOKEN_URI"] = "https://oauth2.googleapis.com/token"
dictへのセット
charged_dict = {
    "type":os.environ["FB_TYPE"],
    "project_id":os.environ["FB_PROJECT_ID"],
    "private_key":os.environ["FB_PRIVATE_KEY"],
    "client_email":os.environ["FB_CLIENT_EMAIL"],
    "token_uri":os.environ["FB_TOKEN_URI"]
}
認証処理(修正後)
cred = credentials.Certificate(charged_dict)
app = firebase_admin.initialize_app(cred)
db = firestore.client()

キーはtype、project_id、private_key、client_email、token_uriとなります。
(こちらもエラーになったら、確認してみてください。)

おわりに

この記事だけ読んだ方は「環境変数に入れたり出したり何がしたいの?」
って思われると思いますが、認証はJSONがいる というわけではないことがわかって頂ければ幸いです。

2023/9/22追記

すいません。以上の方法ですが、CloudFunctionsなど環境によってキー内の改行コード \n\\n に変換されてしまう場合があり、その場合は以下の対処をお願いします。

cert["キー名"] = cert["キー名"].replace("\\n","\n")

特にprivate_keyが改行を含んでいるので、一度dictに入れた後にこの方法で変換してください。
※キーをdictにセットする際にPythonが勝手に \\n に変えてしまうようです。

参考にさせて頂いた記事

1
2
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
1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?