Google Sheets APIでService Accountが403になるとき共有権限・認証主体・Workspace制約を確認する

Google Sheets APIでService Accountが403になるとき、認証主体とスプレッドシートの共有権限を分けて確認します。

AIものさし編集部

まず結論:API scopeとファイル共有は別々に満たす

Google Sheets APIでService Accountを使い、認証情報を作成してSheets APIも有効にしたのに403が返る場合、まず「実際のrequest principal」と「対象spreadsheetの共有権限」を確かめます。Service Accountは人のGoogleアカウントの代理ではなく、独立したGoogle identityとしてAPIを呼びます。人のGoogleアカウントにシートを共有していても、そのService Accountへ自動的に権限が渡るわけではありません。

OAuth scopeは、アプリがどの範囲のGoogleデータ操作を求めるかを示します。scopeを持つことと、Drive上の特定ファイルを読める・編集できることは別条件です。scopeが正しくても、対象fileのACLでrequest principalへ共有されていなければ403になり得ます。反対にfileを共有しても、OAuth scope、API有効化、Workspace policy、操作に必要なroleが不十分なら失敗します。

403を直すために共有リンクを公開したり、組織全体の共有制限を緩めたりしないでください。認証主体、OAuth scope、file permission、共有Drive/domain policyを分けて確認し、必要最小限の権限だけを使います。

実際のrequest principalを特定する

アプリがどの認証方法で動いているかを確認します。

表は横にスクロールできます

認証方法APIのrequest主体主な確認点
Service Account専用のservice identityservice account email、対象file ACL
OAuth user credential同意したGoogleユーザーuser account、grant済みscope
API keyAPI projectを識別するkeyprivate file access権限とは別
Domain-wide delegationWorkspace userを代理するService Account管理者承認、委任scope、subject user

403が出たら、コードの想定ではなく、実行中の環境変数・credential source・token subjectでどの主体が認証されたかを確認します。ローカルではOAuth user、CIやCloud RunではService Accountを使うよう設定され、環境ごとに主体が異なることがあります。Google Cloud projectの番号や名称だけではprincipalを確定できません。

調査表に残すのはprincipal email、credential type、project、API method、spreadsheet ID、OAuth scope、error reasonです。秘密鍵JSON、access token、refresh token、secret値は貼り付けません。Service Account emailはDrive共有の宛先になり得ますが、秘密鍵は認証秘密として別管理します。

対象fileのACLとroleを照合する

既存spreadsheetをService Accountから操作する一般的な方法は、ファイルをそのService Accountのemailへ直接共有することです。Googleのcredential guideは、特定のDriveファイルへのアクセス方法として、fileまたはfolderをService Account emailへ共有する手順を説明しています。

対象spreadsheetの共有設定で、実行中のService Accountが存在するか、正しいroleが付いているかを確認します。読み取りだけならViewer、書き込みをするならEditor相当が必要ですが、実際の処理に合わせて最小権限を選びます。folder共有から権限が継承される場合は、親folderと共有制限も確認します。

OAuth scopeを広げてもfile ACLは追加されません。DriveでEditorを付けても、token scopeがreadonlyなら書き込みは通りません。両方を別の項目として確認してください。エラー解消のために「Anyone with the link」へ変えるのは、private dataの露出を広げるため診断策にしてはいけません。

OAuth scopeとSheets APIの役割を確認する

Google Sheets APIはOAuth 2.0 scopeでアプリのアクセス範囲を制御します。公式scope表では、spreadsheetsはSheets全体の閲覧・編集・作成・削除、spreadsheets.readonlyは閲覧、drive.fileはアプリが利用する特定ファイルへのアクセスとして説明されています。どのscopeが適切かは、fileを選ぶ方式と必要な処理に合わせます。

scopeはDrive共有を代替しません。またSheets scopeはspreadsheetファイルに適用され、個々のtabだけへ制限するscopeではありません。シート内の一部範囲を保護したい場合はProtectedRangeなど、Sheets側の仕組みを使います。

確認項目:

  1. requestに載るOAuth scopeが処理に必要最小限か。

  2. OAuth同意後のtokenにそのscopeが付与されているか。

  3. Sheets APIが正しいGoogle Cloud projectで有効か。

  4. scopeとfile ACLが両方そろっているか。

  5. spreadsheet内のProtectedRangeやtab protectionが書込みを拒否していないか。

  6. local、CI、cloudで同じcredential sourceを使う想定か。

認証情報を再発行しても、別の古いtokenや別principalを参照していれば解決しません。どのcredential sourceが実行時に選ばれたかまで確認します。

WorkspaceとShared Driveの制約を切り分ける

Workspace組織のspreadsheetでは、外部Service Accountへの共有を組織ポリシーが制限することがあります。Shared Driveでは、Drive membership、個別file permission、parent folder permission、domain sharing設定が関係します。個別fileへ共有しようとしても、外部共有やShared Driveの管理設定によって許可されない可能性があります。

Service Accountは組織外identityとして扱われる場合があります。共有設定で招待できるか、Workspace policyが外部共有を許可するかを管理者に確認します。共有できないからといって、組織制限を外したり、公開リンクへ切り替えたりしないでください。管理者へはrequest時刻、対象file、実principal、API method、error reasonを、秘密値なしで渡します。

Shared Driveではファイルの所有・roleがMy Driveと異なります。Google公式仕様ではService AccountはDriveの個人storage quotaを持たず、ファイルを所有できません。Shared Drive上のファイル作成、既存spreadsheetの読取、既存spreadsheetの編集は別の操作なので、混同せず必要なmembership/permissionを確認します。既存ファイルの403を直す目的でdomain-wide delegationへ飛びつかず、必要性と管理者承認を別に評価してください。

観測結果ごとの原因候補

表は横にスクロールできます

観測原因候補次に確認
人のOAuthなら成功、Service Accountだけ403ACLにService Accountがない、Workspace外部共有制限principal、file share、admin policy
Service Accountが共有一覧にない誤ったemail、別credential、共有失敗実際のtoken principal、file ACL
fileは開けるがAPIで403scope、ProtectedRange、API methodtoken scope、read/write、protected range
読取成功、書込だけ403role不足、readonly scope、保護範囲Editor相当、scope、tab/cell protection
CIやcloud環境だけ403credential sourceやprojectの差runtime principal、OAuth grant、project
Shared Driveだけ失敗membership/role/domain restrictionShared Drive access、external sharing policy
API keyでprivate fileを読もうとしているAPI keyをユーザー権限と混同OAuthまたはService Accountの適切な設計
permissionはありそうだが403quota/billing、API errorの別理由responseのreason/domain、quota metrics

403だけで上の候補を断定しません。API responseのerror reason/domain、実principal、Drive permission、quota状況を照合します。permission errorとrate/quota errorを分け、原因に合わない権限拡大を避けます。

安全な再現と最小権限の再試験

機密spreadsheetを公開する必要はありません。まずテスト用の非機密spreadsheetを用意し、同じprincipalとscopeでread-only requestを試します。Service Account emailへViewerとして共有し、既知のセルやmetadataを読めるか確認します。実対象ではowner/adminの承認を得て、そのfileに限り必要なroleを設定します。

readは成功しwriteのみ失敗する場合は、OAuth scope、role、ProtectedRange、tab protectionを順に見ます。操作にwriteが不要なら、scopeやroleを拡大しません。Domain-wide delegationは、既存のfile sharingでは実現できない明確な要件がある場合だけにし、Workspace admin approvalと委任scopeを別途管理します。

再試験の記録に残すのはcredential type、principal、OAuth scope、API method、file location、role、HTTP status/error reason、成功した操作です。秘密鍵、token、spreadsheetの機密内容は保存しません。共有範囲を変更した場合は前後を比較し、診断で広がった権限が残っていないかも確認します。

403調査チェックリスト

  • 実際にAPIを呼ぶprincipal emailを特定した

  • Service Account / OAuth user / API keyのどれか確認した

  • Sheets APIが対象projectで有効か確認した

  • OAuth scopeが必要最小限か確認した

  • OAuth scopeとDrive file ACLを別々に確認した

  • Service Accountへ対象fileまたは必要folderが共有されている

  • read/write操作とViewer/Editor roleが一致している

  • ProtectedRangeやtab protectionを確認した

  • Shared Drive membershipとWorkspace外部共有policyを確認した

  • Domain-wide delegationを軽い修正として使っていない

  • error reasonとquota errorを区別した

  • public linkや全体共有へ変更していない

  • private key、token、sheet dataをログへ出していない

次に行うこと

最初にrequest principal、API method、spreadsheet ID、OAuth scope、file permission、HTTP error reasonを、secretを除いて並べます。Service Accountが主体なら、そのemailを非機密のテストspreadsheetへ必要なroleで共有し、read-only operationから確認します。テストfileで成功し実対象だけ失敗するなら、実file ACL、Shared Drive、組織の外部共有policyを管理者と確認します。対象Workspaceやspreadsheetの現状は未確認なので、公開範囲を広げずこの順で原因を分離してください。

Sources