外部サービス連携

Apps ScriptからSalesforceへ取引先を登録する手順

ユーザー名パスワードフローは新規組織で既定でブロックされています。Apps ScriptからSalesforceへ書き込む認証をクライアントクレデンシャルフローへ直し、コードに鍵を書かない実装を説明します。

2024.05.15

なぜApps Scriptから直接書き込むのか

Googleスプレッドシートに入力した行を、そのままSalesforceの取引先として登録したい場面があります。Web-to-Leadのような受け皿が使えない社内シートが起点のときや、シート側で加工した値をそのまま流し込みたいときです。Apps ScriptからSalesforceのREST APIを直接呼べば、間に別のサービスを挟まずに実装できます。

ユーザー名パスワードフローはもう使えない

以前はUrlFetchAppで grant_type=password を指定し、ユーザー名とパスワードをそのままPOSTする実装がよく使われていました。この方法は、いまは新規組織で既定のまま動きません。Summer '23以降に作成された組織では、OAuth 2.0のユーザー名パスワードフローが既定でブロックされています。有効化するには、設定の「OAuthおよびOpenID Connect設定」で管理者が明示的にオンにする必要があります。

Salesforceはこのフローを安全でないとし、推奨していません。既存の組織で有効になっていても、新しい連携をこの方式で作るのは避けます。

いまの選び方はクライアントクレデンシャルフロー

Apps Scriptのようにユーザーの操作を挟まず、決まった処理を定期的に流すだけなら、OAuth 2.0クライアントクレデンシャルフローが向いています。クライアントIDとクライアントシークレットだけでアクセストークンを得られ、証明書の管理が要りません。

証明書を使うJWTベアラーフローという選択肢もありますが、秘密鍵の発行と保管が増える分、単純な定期連携にはクライアントクレデンシャルフローのほうが構成が軽くなります。個々のユーザーとしてログインする必要がある場合は、認可コードフローを使います。

接続アプリを準備する

  1. 外部クライアントアプリを作る

    設定のクイック検索で「外部クライアントアプリ」を開き、新規作成します。外部クライアントアプリは、従来の「接続アプリケーション」とは別の新しい仕組みです。

  2. OAuth設定を有効化する

    コールバックURLを1つ入力し、スコープに「api」を追加します。

  3. クライアントクレデンシャルフローを有効化する

    ポリシーで「クライアントクレデンシャルフローを有効化」をオンにし、実行ユーザーに、必要な権限だけを与えた専用のインテグレーションユーザーを割り当てます。

  4. コンシューマ鍵とシークレットを控える

    保存後に表示されるコンシューマ鍵・コンシューマシークレットを、次の節で使います。

⚠️ 実行ユーザーに管理者権限のユーザーをそのまま割り当てないでください。取引先の作成だけができる、絞った権限セットのユーザーにします。

認証情報をコードに直書きしない

クライアントIDとシークレットを .gs のソースに直接書くと、プロジェクトを共有した相手やコピーを取った相手にも見えてしまいます。Apps Scriptにはプロジェクトごとの設定値を保存する「スクリプト プロパティ」があり、PropertiesService から読み出せます。ソースには入れず、プロジェクトの設定画面から登録します。

Apps Scriptの実装

function getAccessToken_() {
  var props = PropertiesService.getScriptProperties();
  var response = UrlFetchApp.fetch(
    'https://xxxx.my.salesforce.com/services/oauth2/token',
    {
      method: 'post',
      payload: {
        grant_type: 'client_credentials',
        client_id: props.getProperty('SF_CLIENT_ID'),
        client_secret: props.getProperty('SF_CLIENT_SECRET')
      },
      muteHttpExceptions: true
    }
  );
  return JSON.parse(response.getContentText());
}

スクリプトプロパティからクライアントIDとシークレットを読み、クライアントクレデンシャルフローでアクセストークンを取得します。

function insertAccount() {
  var auth = getAccessToken_();
  var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Account');
  var name = sheet.getRange('A2').getValue();
  var response = UrlFetchApp.fetch(
    auth.instance_url + '/services/data/v67.0/sobjects/Account/',
    {
      method: 'post',
      contentType: 'application/json',
      headers: { Authorization: 'Bearer ' + auth.access_token },
      payload: JSON.stringify({ Name: name }),
      muteHttpExceptions: true
    }
  );
  Logger.log(response.getContentText());
}

シートのA2セルの値を、取引先のName項目として1件登録します。

muteHttpExceptions: true を付け、レスポンスをログに出しています。エラーを黙って握りつぶす実装にすると、登録できていないことに気づけません。

動作確認

  1. シートに値を入れる

    Accountシートの A2 セルに、登録したい取引先名を入力します。

  2. スクリプトを実行する

    Apps Scriptのエディタで関数「insertAccount」を選び、実行します。

  3. 実行ログを確認する

    返ってきたJSONに id が含まれていれば登録成功です。エラーなら errorCode の内容を見ます。

  4. Salesforce側で確認する

    取引先の一覧、またはREST APIのクエリで、登録した名前のレコードがあるかを確かめます。

ここで間違えやすい

間違い何が起きるか
grant_type を password のまま残す新規組織では既定でブロックされ、認証エラーになります
外部クライアントアプリでクライアントクレデンシャルフローを有効化し忘れるトークン取得の時点で unsupported_grant_type になります
スクリプトプロパティを設定し忘れるクライアントIDが null のまま送られ、認証エラーになります
クライアントIDとシークレットをソースに直書きするプロジェクトを共有・コピーした相手にも認証情報が見えます
catch でエラーを握りつぶす登録に失敗していても気づけません

確認した環境

  • 2026年9月/Salesforce Summer '26(APIバージョン67.0)時点の公式ヘルプで、ユーザー名パスワードフローの既定ブロックとクライアントクレデンシャルフローの仕様を確認しています
  • コードはAPIバージョン67.0で書いています

まとめ

  • ユーザー名パスワードフローは、Summer '23以降に作成された組織では既定でブロックされています
  • Apps Scriptのような定期連携には、クライアントクレデンシャルフローが軽い構成で使えます
  • 外部クライアントアプリでクライアントクレデンシャルフローを有効化し、専用のインテグレーションユーザーを割り当てます
  • クライアントIDとシークレットは、ソースに書かずスクリプトプロパティに保存します
  • エラーを握りつぶさず、レスポンスをログへ出す実装にします

Salesforceの導入・運用についてご相談ください

導入前の検討から、お使いの環境の改修・運用、AIとの連携まで承ります。状況を伺ったうえで、進め方をご提案します。