注目イベント!
アドベントカレンダー2024開催します(12/1~12/25)!
一年を締めくくる特別なイベント、アドベントカレンダーを今年も開催します!
初心者からベテランまで楽しめる内容で、毎日新しい技術トピックをお届けします。
詳細はこちらから!
event banner

ChatGPTプラグイン開発でGitHub OAuthを使った認証を試す

| 7 min read
Author: noboru-kudo noboru-kudoの画像

少し前にAWS上でChatGPTプラグインを構築する記事を書きました。

ここでは認証なし(auth: none)の実装としていました。
とはいえ、ユーザー固有のリソースを使う場合等、認証が必要なケースも多くあるかと思います。

現時点では、ChatGPTプラグインは以下3タイプの認証方式を提供しています。

今回は、以前の記事で作成したAWS LambdaベースのGitHub検索プラグインをOAuthを使った認証に切り替えたいと思います。

以下のような認証フローになります。

sequenceDiagram
    actor U as User
    participant C as ChatGPT
    participant P as Plugin(Lambda)
    participant G as GitHub
    U->>C: プラグインインストール
    C-->>U: 認証ページリダイレクト(client_url)
    U->>G: ログインと同意(GitHub App)
    G-->>U: リダイレクト(with 認可コード)
    U->>C: コールバック
    C->>G: ユーザーアクセストークン取得<br>(authorization_url)
    U->>C: プロンプト入力
    C->>P: プラグインAPIコール<br>(with ユーザーアクセストークン)
    P->>G: GitHub APIコール<br>(with ユーザーアクセストークン)

ここで使用したGitHubのソースコードは、こちらで公開しています。

Caution

ChatGPTプラグインは実験的フェーズです。本記事は現時点(2023-06-07)の仕様をもとに試したものです。
今後フィードバックを受けて大きな変更が入ることも予想されます。
実際に試す際は、最新の公式ドキュメントを参照するようにしてください。

Lambda関数の修正

#

プラグインの機能自体は変えませんが、GitHub APIで連携しているトークンはOAuthで取得するアクセストークンに変更します。
変更部分にコメントを入れています。

const octokit = new Octokit({
  // ユーザーアクセストークンを使用するため不要
  // auth: process.env.GITHUB_TOKEN
});
export const search: APIGatewayProxyHandler = async (event: APIGatewayEvent, context: Context) => {
  const { q } = event.queryStringParameters ?? {};
  const resp = await octokit.request('GET /search/repositories', {
    q,
    sort: 'stars',
    order: 'desc',
    per_page: 5,
    // ChatGPTが取得するユーザーアクセストークン(Bearer ghu_xxxxxxx)をそのまま指定
    headers: {
      authorization: event.headers.Authorization
    }
  });
  const repos: Repo[] = resp.data.items.map(item => ({
    full_name: item.full_name,
    url: item.url,
    star: item.stargazers_count,
    description: item.description
  }));
  return {
    statusCode: 200,
    body: JSON.stringify({
      repos
    }),
    headers: {
      ...CORSHeaders,
      'Content-Type': 'application/json'
    }
  };
};

ChatGPTではOAuthで認証した際のアクセストークンを、HTTPリクエストのAuthorizationヘッダにそのままの形(Bearer ghu_xxxxxxxx)で連携してくれます。
ここでは、これをそのままGitHub APIのAuthorizationヘッダに指定するように修正しました。

AWS CDKスクリプトの修正

AWS CDK側でも一部修正が必要です。変更点は以下2点です。

  • Authorizationヘッダをオリジン(API Gateway + Lambda)まで連携するようキャッシュポリシーのホワイトリストに追加
  • Lambdaの環境変数に指定していたGitHubトークンを削除(ユーザーアクセストークンを使用するので不要)

変更後のソースコードはこちらです。

プラグインマニフェスト修正

#

次に、プラグインマニフェスト(static/.well-known/ai-plugin.json)を修正します。
ここでプラグインの認証方式を指定します。

今後変わるかもしれませんが、現時点では2段階のマニフェスト変更が必要でした。
初期段階は以下のようにします。

{
  "schema_version": "v1",
  "name_for_human": "GitHub Search",
  "name_for_model": "GitHubSearch",
  "description_for_human": "Plugin for searching GitHub repositories, You can search the repository by entering your search query.",
  "description_for_model": "Plugin for searching GitHub repositories, You can search the repository by entering your search query.",
  "auth": {
    "type": "oauth",
    "client_url": "https://github.com/login/oauth/authorize",
    "authorization_url": "https://github.com/login/oauth/access_token",
    "scope": "",
    "authorization_content_type": "application/json",
    "verification_tokens": {
      "openai": ""
    }
  },
  "api": {
    "type": "openapi",
    "url": "https://chatgpt.mamezou-tech.com/openapi.yaml"
  },
  "logo_url": "https://chatgpt.mamezou-tech.com/logo.png",
  "contact_email": "support@chatgpt.mamezou-tech.com",
  "legal_info_url": "https://chatgpt.mamezou-tech.com/legal"
}

auth配下が変更部分です。前回の記事ではnoneのみでしたが、ここではOAuthを表すoauthを指定し、各種プロパティを設定します。
client_url等の各プロパティの仕様は、ChatGPTプラグインの公式ドキュメントの通りです。

なお、verification_tokens.openaiの値はプラグインのセットアップ時に設定しますが、この段階でもフィールド自体は必要でした(省略したらマニフェストを認識してくれませんでした)。

GitHub Appを作成する

#

以下のGitHubの公式ドキュメントを参照してユーザーアクセストークンベース(OAuth)のGitHub Appを作成しました。

GitHub App作成時の認証後のコールバックURLの値ですが、ちょっとここはハマりました。
ChatGPTプラグインでのコールバック先は以下の形式になります。

https://chat.openai.com/aip/{plugin-id}/oauth/callback

ここで問題になるのが、パスに含まれるプラグインID({plugin-id})です。
これを探すのにだいぶ時間を費やしてしまいました。これはChatGPTプラグインのセットアップ時に分かります。ここでは適当なもので問題ありませんでした。

作成後はOAuthのクライアントシークレットを生成します。これは後ほどChatGPTプラグインセットアップ時にクライアントIDと共に登録することになります。

GitHub App - Create client secret

ChatGPTプラグインをセットアップする

#

それでは、ChatGPTプラグインをセットアップします。
認証なしの場合の時と同様にChatGPTのUIからプラグインストアを表示し、「Develop your own plugin」を選択してドメインを入力します。
すると、以下のようにOAuthのクライアントIDとシークレットの入力を求められました。

ここには、先ほどGitHub App作成時に払い出されたものを入力して、「Next」をクリックします。
以下のようにトークン検証を求めるダイアログが表示されました。

ここでプラグインマニフェストの修正と再デプロイが必要になります。
表示されたトークンをai-plugin.jsonverification_tokens.openaiに設定して再度デプロイします。ここではCloudFront経由ですので、CDNキャッシュもクリアしました。

また、もう1つ重要なことがあります。先ほどGitHub AppのOAuthコールバックURLに指定する必要があったプラグインIDを確認します。
前述しましたが、これが見つけられなくて苦戦しました。公式ドキュメントをよく読むと以下のように記載されています。

The plugin_id is passed via the request made to your OAuth endpoint (note that it is not visible in the ChatGPT UI today but may be in the future). You can inspect the request there to see the plugin_id.

現時点ではChatGPTのUIからは確認できなさそうです。
最終的には、Chromeのデベロッパーツールで確認できましたが、この見方が正しいのかは全く自信がありません(きっと正しくない)。

上記赤枠部分がプラグインIDのようです。
先ほど、GitHub App作成時コールバックURLに適当な値を指定していましたが、ここで正しいパスに置き換えて更新しておきます。

ChatGPTのUIに戻って先ほど表示されていたダイアログの「Verify tokens」をクリックします。
以下のダイアログが表示されますので「Install for me」をクリックします。

「Continue」をクリックします。

ここでログインが求められます。「Log in with <プラグイン名>」をクリックします。

GitHubのログインとGitHub Appに対する許可が求められます。

これで「Authorize...」をクリックすればプラグインのインストールは完了です。
特にプラグインの機能は変えていないのでできることは同じですが、ユーザーアクセストークンを使ってGitHub APIが実行されるようになりました。

まとめ

#

OAuthに関する実装をすることなく、プラグインからアクセストークンを利用できました。

今回使ったGitHubレポジトリを検索するだけのプラグインでは、OAuthを使うメリットはほとんどありません。
ですが、このアクセストークンをうまく活用して機能拡張すれば、レポジトリ作成やコミット、PR作成等、開発作業に必要なことがほぼできてしまいます(もちろんコーディングはChatGPT)。
ChatGPTと会話しながら、ノーコードで開発を進めていく日も近いのかなと思います。GitHub Copilotが拡張して、そういうサービスが近々出てくるんだろうなと思ったりもしますが、、、

豆蔵では共に高め合う仲間を募集しています!

recruit

具体的な採用情報はこちらからご覧いただけます。