Zendesk ZIS で固定認証の外部連携を OAuth へ中継してみた

Zendesk ZIS で固定認証の外部連携を OAuth へ中継してみた

固定 Basic 認証しか設定できない外部サービスから ZIS inbound webhook へイベントを送り、OAuth connection を使って Zendesk API を呼ぶ構成を試しました。検証用チケットへ非公開コメントを追加するまでの構成と手順を紹介します。
2026.09.01

はじめに

Zendesk API トークンを OAuth へ移行するとき、呼び出し元が自作プログラムなら、client_credentialsでアクセストークンを取得して期限ごとに更新できます。一方、管理画面に固定の認証情報を入力するだけの SaaS など、トークンの取得処理を実装できない呼び出し元もあります。

この条件では、期限付きのアクセストークンを固定値として貼り付けても、期限切れ後に連携が止まります。そこで、外部サービスからのリクエストを Zendesk Integration Services (ZIS) の inbound webhook で受け、ZIS が管理する OAuth connection で Zendesk API を呼ぶ構成を試しました。

この構成で ZIS が保持する Zendesk 用 OAuth connection のアクセストークンは、期限切れせず、refresh token も使いません。期限のたびに ZIS がトークンを更新する構成ではなく、初回に管理者が認証して作成した connection を継続して使います。そのため、外部サービス側にトークンの取得や更新を実装する必要がありません。

結果として、外部から固定 Basic 認証でイベントを送り、検証用チケットへ非公開コメントを 1 件追加できました。本記事では、検証に使った最小構成を紹介します。

対象読者

  • Zendesk API トークンから OAuth への移行を検討している方
  • 呼び出し元に OAuth トークンの取得処理を実装できない方
  • ZIS inbound webhook と OAuth connection の組み合わせを試したい方

参考

構成

外部サービスには、inbound webhook の作成時に返る URL、ユーザー名、パスワードを設定します。外部サービスは固定 Basic 認証でイベントを送るだけです。イベントを受けた ZIS は job spec に対応する flow を開始し、OAuth connection を使って Zendesk Support API を呼びます。

検証には Zendesk の検証用テナントと、初期コメントを 1 件持つ新規チケットを使いました。移行時の初期構築として、bundle と job spec の投入には管理者 API トークンを使いました。一方、構築後のチケット更新では API トークンを使わず、OAuth connection を使います。ゴールは、一意な本文を持つ非公開コメントを、イベント送信前の 0 件から送信後の 1 件へ増やすことです。

ZIS OAuth トークンの取得

ここでは、ZIS integration が登録済みであることを前提とします。

ZIS_INTEGRATIONには任意の文字列ではなく、integration を登録したときの名前を指定します。

export ZENDESK_SUBDOMAIN='your_subdomain'
export ZENDESK_ADMIN_EMAIL='admin@example.com'
export ZENDESK_API_TOKEN='your_api_token'
export ZIS_INTEGRATION='your_zis_integration'
export TICKET_ID='12345'
export STAMP=$(date -u +%Y%m%d%H%M%S)

ZIS integration を登録すると、zis_${ZIS_INTEGRATION}という識別子の OAuth クライアントが自動生成されます。

get-zis-client.sh
zis_client_id=$(curl -sS \
  "https://${ZENDESK_SUBDOMAIN}.zendesk.com/api/v2/oauth/clients.json" \
  -u "${ZENDESK_ADMIN_EMAIL}/token:${ZENDESK_API_TOKEN}" \
  | jq -er --arg identifier "zis_${ZIS_INTEGRATION}" \
    '[.clients[] | select(.identifier == $identifier)]
     | if length == 1 then .[0].id else error("ZIS client count must be 1") end')

取得した ID を使い、ZIS API の呼び出しに使う OAuth トークンを発行します。

create-zis-token.sh
token_response=$(curl -sS -X POST \
  "https://${ZENDESK_SUBDOMAIN}.zendesk.com/api/v2/oauth/tokens.json" \
  -u "${ZENDESK_ADMIN_EMAIL}/token:${ZENDESK_API_TOKEN}" \
  -H "Content-Type: application/json" \
  -d "{\"token\":{\"client_id\":${zis_client_id},\"scopes\":[\"read\",\"write\"]}}")

export ZIS_ACCESS_TOKEN=$(echo "$token_response" | jq -er '.token.full_token')
export ZIS_ACCESS_TOKEN_ID=$(echo "$token_response" | jq -er '.token.id')
unset token_response

OAuth connection の作成

zendeskという名前の OAuth connection を作ります。

まず、OAuth の開始 API を呼び出します。

create-connection.sh
response=$(curl -X POST \
  "https://${ZENDESK_SUBDOMAIN}.zendesk.com/api/services/zis/connections/oauth/start/${ZIS_INTEGRATION}" \
  -H "Authorization: Bearer ${ZIS_ACCESS_TOKEN}" \
  -H "Content-Type: application/json" \
  -d "{
    \"name\": \"zendesk\",
    \"oauth_client_name\": \"zendesk\",
    \"oauth_url_subdomain\": \"${ZENDESK_SUBDOMAIN}\",
    \"origin_oauth_redirect_url\": \"https://example.local\",
    \"permission_scopes\": \"read write\",
    \"allow_offline_access\": false
  }")

echo "$response" | jq -r '.redirect_url'
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100   666    0   425  100   241    321    182  0:00:01  0:00:01 --:--:--   503
https://zis.zendesk.com/api/services/zis/connections/oauth/start_redirect?flow_token=ey****

出力された URL を管理者のブラウザで開き、画面の案内に従って管理者として認証します。認証が完了すると、ZIS は OAuth アクセストークンを connection として保持し、ブラウザを https://example.local へ戻します。example.localは動作確認用のダミー URL なので、ブラウザには接続エラーが表示されます。

zendeskoauth01

これは想定どおりです。ブラウザの表示ではなく、次の API で zendesk connection が作成されたことを確認します。

show-connection.sh
curl \
  "https://${ZENDESK_SUBDOMAIN}.zendesk.com/api/services/zis/connections/${ZIS_INTEGRATION}?name=zendesk" \
  -H "Authorization: Bearer ${ZIS_ACCESS_TOKEN}" \
  | jq '{name, permission_scope, token_type}'
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100   869    0   869    0     0   1712      0 --:--:-- --:--:-- --:--:--  1714
{
  "name": "zendesk",
  "permission_scope": "read write",
  "token_type": "bearer"
}

bundle のアップロード

今回使った bundle は、チケットへ非公開コメントを追加する Action、その Action を呼ぶ Flow、inbound webhook のイベントを Flow へ結び付ける JobSpec の 3 リソースで構成します。

bundle.template.json
{
  "name": "ZIS inbound OAuth bridge __STAMP__",
  "description": "Add one private comment to a disposable verification ticket",
  "zis_template_version": "2019-10-14",
  "resources": {
    "blogcomment": {
      "type": "ZIS::Action::Http",
      "properties": {
        "name": "blogcomment",
        "definition": {
          "method": "PUT",
          "path": "/api/v2/tickets/__TICKET_ID__.json",
          "connectionName": "zendesk",
          "headers": [{"key": "Content-Type", "value": "application/json"}],
          "requestBody": {
            "ticket": {
              "comment": {"body.$": "$.comment_body", "public": false}
            }
          }
        }
      }
    },
    "blogflow": {
      "type": "ZIS::Flow",
      "properties": {
        "name": "blogflow",
        "definition": {
          "StartAt": "AddPrivateComment",
          "States": {
            "AddPrivateComment": {
              "Type": "Action",
              "ActionName": "zis:__INTEGRATION__:action:blogcomment",
              "Parameters": {"comment_body.$": "$.input.comment_body"},
              "End": true
            }
          }
        }
      }
    },
    "blogjob": {
      "type": "ZIS::JobSpec",
      "properties": {
        "name": "blogjob",
        "event_source": "blog_zis_e2e___STAMP__",
        "event_type": "comment_requested",
        "flow_name": "zis:__INTEGRATION__:flow:blogflow"
      }
    }
  }
}

プレースホルダーへ値を入れた JSON を生成します。

render-bundle.sh
jq \
  --arg stamp "$STAMP" \
  --arg ticket_id "$TICKET_ID" \
  --arg integration "$ZIS_INTEGRATION" \
  'walk(
    if type == "string" then
      gsub("__STAMP__"; $stamp)
      | gsub("__TICKET_ID__"; $ticket_id)
      | gsub("__INTEGRATION__"; $integration)
    else .
    end
  )' bundle.template.json > rendered-bundle.json

生成したファイルをアップロードし、job spec をインストールします。

deploy-bundle.sh
curl -X POST \
  "https://${ZENDESK_SUBDOMAIN}.zendesk.com/api/services/zis/registry/${ZIS_INTEGRATION}/bundles" \
  -u "${ZENDESK_ADMIN_EMAIL}/token:${ZENDESK_API_TOKEN}" \
  -H "Content-Type: application/json" \
  --data-binary @rendered-bundle.json

curl -X POST \
  "https://${ZENDESK_SUBDOMAIN}.zendesk.com/api/services/zis/registry/job_specs/install?job_spec_name=zis:${ZIS_INTEGRATION}:job_spec:blogjob" \
  -u "${ZENDESK_ADMIN_EMAIL}/token:${ZENDESK_API_TOKEN}"

inbound webhook の作成

job spec と同じ event_sourceevent_type を指定します。

create-inbound-webhook.sh
curl -X POST \
  "https://${ZENDESK_SUBDOMAIN}.zendesk.com/api/services/zis/inbound_webhooks/generic/${ZIS_INTEGRATION}" \
  -H "Authorization: Bearer ${ZIS_ACCESS_TOKEN}" \
  -H "Content-Type: application/json" \
  -d "{
    \"source_system\": \"blog_zis_e2e_${STAMP}\",
    \"event_type\": \"comment_requested\"
  }"

レスポンスの pathusernamepassword を外部サービスの設定に使用します。

動作確認

作成時に返された値を環境変数へ設定し、inbound webhook へイベントを送ります。

send-event.sh
export ZIS_WEBHOOK_PATH='response_path'
export ZIS_WEBHOOK_USERNAME='response_username'
export ZIS_WEBHOOK_PASSWORD='response_password'

curl -X POST \
  "https://${ZENDESK_SUBDOMAIN}.zendesk.com${ZIS_WEBHOOK_PATH}" \
  -u "${ZIS_WEBHOOK_USERNAME}:${ZIS_WEBHOOK_PASSWORD}" \
  -H "Content-Type: application/json" \
  -d "{\"comment_body\":\"ZIS_BLOG_E2E_${STAMP}\"}"

POST は 200 を返しました。5 秒後にチケットのコメント一覧を確認すると、送信した本文と完全一致する非公開コメントが 1 件追加されていました。

確認項目 結果
inbound webhook への POST 200
一意な本文の一致数 0 件から 1 件
追加されたコメント ID 1 件
追加コメントの public false

この結果から、外部サービスは固定 Basic 認証のまま、ZIS 内部の OAuth connection を使って Zendesk API を呼べることを確認できました。

まとめ

固定 Basic 認証しか設定できないケースでも、ZIS inbound webhook を中継して Zendesk API を OAuth で呼び出せました。Zendesk 用 OAuth connection のアクセストークンは期限切れしないため、外部サービス側にトークンの取得や更新を実装する必要はありません。なお、本番環境では、呼び出し元が対応している場合 OAuth や署名付き webhook を優先してください。


Zendeskの導入支援ならクラスメソッドへ

クラスメソッドはZendeskのライセンス販売パートナーです。Zendesk導入にあたって、設定代行、オペレーターや管理者向けのトレーニング、独自のアプリ開発、データ移行など、様々なサービスをご提供しております。既に導入済みのお客様に対してもご支援できますので、まずはお気軽にご相談ください。

Zendeskの導入支援の詳細を見る

この記事をシェアする

関連記事