Zoom Cobrowse SDKを試してみた

# Zoom Cobrowse SDKとは

![cover-opt-828.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/442226/706fd13d-0a03-298b-10ec-a87b89498069.jpeg)

入力フォームやソフトウェアの設定方法、複雑なオンライン申請方法など、リモートから説明しようとして画面を直接みながら説明できたらいいのに、と思うことはよくありますよね。私も家族にGoogle Photoで家族の写真の共有方法を伝えたくてもどかしい思いをしたこととか、よくあります。

![DALL·E 2025-01-20 16.39.45 - A heartwarming and slightly humorous illustration showing a tech support staff assisting an elderly man over a video call. The elderly man is seated a.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/442226/8564eaee-474d-80a7-a66a-a2aae99df5d5.jpeg)
(イラスト：DALL-E作成)

Zoomを使えば画面共有ができますし、デスクトップならリモート操作も（相手がリモート操作の許可の仕方さえわかれば！）簡単にできます。でも、教える相手側のプライバシーが見えてしまうとまずい場合はどうでしょうか。

例えば、ショッピングサイトでクレジットカード情報を入れる必要があったり、マイナンバーカードの番号だったり、IDとパスワードだったり。相手が自分の親ならともかく、サポートスタッフとお客様の関係などであれば「みられたくない」「みたくない」というシチュエーションも多くありますよね。

Cobrowse（コラボレーティブブラウジング）は、サポートセッション中にエージェントが顧客のウェブブラウザを安全に閲覧・操作できるリアルタイムテクノロジーです。ユーザーのデバイスを制御することなく、エージェントはユーザーの画面を見ながらトラブルシューティングやサポートを行うことができます。

自社サイトのフォームであれば、あらかじめ共有する（あるいは共有しない）フィールドを設定することができ、これによりマイナンバーカードの番号やクレジットカード情報、その他プライバシーに関わるためエージェント側が取得したくないフィールドのデータをマスクすることができるため、セキュリティやプライバシー規制への準拠が可能です。

![hero.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/442226/fa8db82e-a2dc-ea4e-d337-0b7013e66631.png)

今回は、これを使った簡単なサンプルをご紹介します。

https://github.com/bitzed/cobrowse-tinysample

# 事前準備
- NodeJSやNPMなどの環境
- Zoom SDKのUniversal Credit

Zoom SDK Universal Creditとは、以下のような様々な機能の利用に使うことができる、共通のプリペイドクレジットです。

- Video SDK Sessionの従量課金時間
- オーディオカンファレンスの利用時間
- 翻訳・文字起こしの利用時間
- Cloud Room Connectorの利用時間
- Cloud Recordingの利用時間・ストレージ利用量

詳細は、以前ご紹介したこちらの記事

https://qiita.com/michitakasugi/items/11d2af1c6f853bfd531e

または、こちらの価格のページをご覧いただくか、お問い合わせください。

https://zoom.us/pricing/developer?showRate=true

# SDK Key & Secret を準備する
上記準備が整ったら、SDK Key および Secret を取得します。Zoom SDK Universal Creditを契約しているアカウントで[Zoom Marketplace](https://marketplace.zoom.us/) へログインし、Build Appの画面で Cobrowse SDKのタブをクリックします。

そこで表示されたSDK KeyとSDK Secretをコピーしておきます。（すぐ下にAPI credentialsという項目がありますが、そちらではなく SDK Credentialsの方をコピーしてください）

![Screenshot 2025-01-20 at 16.53.12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/442226/7517b254-cbf5-3033-8fbf-5ad3ecc94896.png)

# Tokenの取得
SDKの初期化にあたって、まず先に取得した Key および Secret を用いてTokenを取得します。TokenはJWT形式になっており、事前にToken生成の仕組みを用意しておく必要があります。

サンプルとしてこのようなスクリプトを用意しておきました。基本的にはVideo SDKのJWTトークンの生成ロジックと何ら変わりません。ポイントは、エージェントとカスタマーでroleの設定を違える必要があるというところです。エージェントは`role_type`が`2`、カスタマーは`1`になります。

```javascript
const express = require('express')
const cors = require('cors')
const KJUR = require('jsrsasign')

const app = express()
const port = 8080
const path = require('path')
require('dotenv').config({ path: path.join(__dirname, '.env') })

app.use(express.static(path.join(__dirname, 'public')))
app.use(express.json(), cors())
app.options('*', cors())

app.post('/', (req, res) => {
  const requestBody = (req.body)
  const { role } = requestBody
  console.log('role:', role);

  const iat = Math.floor(new Date().getTime() / 1000)
  const user_id = Math.random().toString(36).substring(2)
  const user_name = user_id
  const exp = iat + 60 * 60 * 2//2時間に設定
  const oHeader = { alg: 'HS256', typ: 'JWT' }
  const oPayload = {
    app_key: process.env.ZOOM_SDK_KEY,//.envファイルから読み込み
    role_type: role,//1:customer, 2:agent
    user_id,
    user_name,
    iat,
    exp
  }
  const sHeader = JSON.stringify(oHeader)
  const sPayload = JSON.stringify(oPayload)
  const sdkToken = KJUR.jws.JWS.sign('HS256', sHeader, sPayload, process.env.ZOOM_SDK_SECRET)
  console.log('sdkToken:', sdkToken);
  res.json({
    sdkToken: sdkToken
  })
})

app.listen(port, () => console.log(`Zoom Video SDK for Web Sample. port: ${port}! Open http://localhost:${port} with your browser.`))
```
出力される`sdkToken`を使って、カスタマー、エージェントそれぞれでSDKを起動します。今回は、`user_id` を適当に作っていますが、もちろん任意で作成いただくこともできます。

# カスタマー
トークンが取得できたら、カスタマー側はまずSDKを初期化します。SDK自体はCDNから読み込むことができ、以下のスニペットをHTMLのヘッダなどに追加するだけです。
```javascript
(function(r,a,b,f,c,d){r[f]=r[f]||{init:function(){r.ZoomCobrowseSDKInitArgs=arguments}};
var fragment=a.createDocumentFragment();function loadJs(url) {c=a.createElement(b);d=a.getElementsByTagName(b)[0];c.async=false;c.src=url;fragment.appendChild(c);};
loadJs(`https://us01-zcb.zoom.us/static/resource/sdk/${ZOOM_SDK_KEY}/js`);d.parentNode.insertBefore(fragment,d);})(window,document,'script','ZoomCobrowseSDK');
```
カスタマー側での実際の初期化方法の一例としては以下の通り。`ZoomCobrowseSDK.init`で初期化できます。

```javascript
function initializeCobrowseSDK(sdkToken) {
  if (typeof ZoomCobrowseSDK === 'undefined') {
    console.log('SDK is not loaded yet.');
    return;
  }
  const settings = {
    allowCustomerAnnotation: true,
    piiMask:{
      maskType: 'custom_input',
      maskCssSelectors: '.hideme',
    }
  };

  ZoomCobrowseSDK.init(settings, function({ success, session, error }) {
    if (success) {
      console.log('SDK has been initialized successfully.');
      const startButton = document.getElementById('startCobrowse');
      //ボタンクリックでセッションを開始できるように
      startButton.disabled = false;
      //初期化完了までボタンをdisable
      startButton.addEventListener('click', function() {
        startCobrowseSession(session, sdkToken);
      });
    } else {
      console.error('Failed to initialize SDK:', error);
    }
  });
}
```
このinitパラメータにある`piiMask`ですが、こちらの[piiMaskOptions](https://developers.zoom.us/docs/cobrowse-sdk/features/#piimask-options) にある通り、 `maskType: "all_input"`または`maskType: "custom_input"` を選ぶことができ、`custom_input`の場合はCSSクラスやIDなど様々な要素を設定できます。複数設定も可能です。

初期化が完了したら、`startCobrowse`ボタンをクリックします。すると、以下のようにCobrowseセッション開始用のPINコードが表示されます。

![Screen Recording 2025-01-20 at 17.14.16.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/442226/46d05b1b-39db-c3ad-2df4-18809a337617.gif)

# エージェント
エージェントが同じCobrowse SDK セッションに参加するには、iFrameやWebViewでCobrowse SDKのエージェント向けのエンドポイントを開くだけです。例えば以下のようなコードをBodyのルートに置けば完了です。`COBROWSE_AGENT_TOKEN`を実際に取得したTokenに置き換えます。

```html
<iframe
  width="1024"
  height="768"
  src="https://us01-zcb.zoom.us/sdkapi/zcb/frame-templates/desk?access_token=COBROWSE_AGENT_TOKEN"
  allow="autoplay *; camera *; microphone *; display-capture *; geolocation *;"
>
</iframe>
```
Tokenの取得からiFrameの描画まで成功すると、PINコード入力画面が表示されます。
![Untitled.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/442226/adde4528-2a62-59c5-edcd-b979da1b6c64.gif)

ここで、先ほどカスタマー側で表示されたPINコードを入力すると、自動的にCobrowseセッションが開始されます。

# お試しサイト
一時的ではありますが、以下にテスト用の画面をご用意しました。カスタマー側のHTMLはフォームのサンプルとなっており、Phone Numberフォームのみ`piiMask`を適用しているので、アスタリスクに変換して共有されます。状況によってダウンしている場合もあるかもしれません。その際はご容赦ください。。。

![Untitled (1).gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/442226/9ebe9548-2baa-eec4-1de7-4c4c28e247d3.gif)


## カスタマー

https://cobrowse-tinysample-985755840719.asia-northeast1.run.app/customer.html

## エージェント

https://cobrowse-tinysample-985755840719.asia-northeast1.run.app/agent.html

# まとめ
Cobrowse SDK自体がまだリリースされて間もないため、今後も機能追加などが予定されています。現状のKnown Limitationなどは[Limitations and support](https://developers.zoom.us/docs/cobrowse-sdk/features/#limitations-and-support)に記載されていますので、併せてご確認ください。

# リンク集
冒頭にも挙げましたが、こちらに今回のgithubのサンプルがあります。

https://github.com/bitzed/cobrowse-tinysample

また、ZoomのGithubに公式のQuickStartおよびAuthEndpoint Sampleがございます。

https://github.com/zoom/CobrowseSDK-Quickstart

https://github.com/zoom/cobrowsesdk-auth-endpoint-sample

併せて、こちらのDocumentationおよびBlogも参考にしていただけると幸いです。

https://developers.zoom.us/docs/cobrowse-sdk/

https://developers.zoom.us/blog/build-secure-collaborative-browsing-with-the-zoom-cobrowse-sdk/