AIエージェント×OAuth 2.0:Device Flowで社内データの安全な認可を実装した話 - エムスリーテックブログ

エムスリーテックブログ

エムスリー(m3)のエンジニア・開発メンバーによる技術ブログです

AIエージェント×OAuth 2.0:Device Flowで社内データの安全な認可を実装した話

【SREチーム ブログリレー 2日目】 こんにちは、SREチームの青木です。

エムスリーには、アクセスログや社内レポートを配信する仕組みがあります。レポートには部署や案件ごとに見てよい人・見てはいけない人がいるため、アクセス制御を行う認可システムを内製し運用しています。

この認可システムはこれまでブラウザでの SSO ログインを前提としていましたが、「Claude CodeのようなAIエージェントにレポートを読ませて集計や調査をさせたい」という要望に応えるため、OAuth 2.0 の Device Authorization Grant(RFC 8628、いわゆる Device Flow)を実装しました。本記事では認可システムへCLIでのアクセスのニーズとのDevice Flowの実装について紹介します。

浅草寺の宝蔵門(仁王門)

社内レポートの認可システムとは

システム構成は次のような役割分担になっています。

  • レポートファイルの実体は nginx が配信する
  • 認可システムはファイル本体は中継せず nginx の auth_request ディレクティブから呼ばれ、HTTP ステータス(200/401/403)を返します
  • 認証は社内 SSO(OIDC)、認可はユーザー・グループ・ロールとリソースの紐付けで判定する

レポートは「この案件の担当者だけが見られる」「この部署のメンバーだけが見られる」といった制御が必要なため、リソース(URL パスのプレフィックス)に対して ユーザー / グループ / ロール を紐付けるモデルで担当者ごとの権限を管理しています。

変更前のシーケンス図

一方で、認証はブラウザ前提でした。SSO でログインするとセッション Cookie が発行され、auth_request はその Cookie でユーザーを解決します。つまり:

  • 踏み台サーバやコンテナ内シェルなど、ブラウザを起動できない環境からレポートを取得できない
  • Claude CodeなどのAIエージェントにレポートを渡す手段がない

特に後押しになったのが AIエージェントの普及です。「先週のレポートを読んでエラーの傾向をまとめて」とエージェントに頼みたくても、取得手段がブラウザしかないと、人間がダウンロードしてファイルを手渡しするしかありません。かといって、エージェントにブラウザのセッション Cookie をコピーして渡すのは危険です。セッションはそのユーザーの全権限そのものであり、有効期限も失効手段もエージェント単位では制御できません。

方針検討

CLI から認証する方法として、いくつかの案を比較しました。

評価
認可システム自身が Device Flow の Authorization Server になる 採用。追加インフラなし、既存の認可ロジックを無改修で流用できる
SSO 基盤の前段に Lambda + API Gateway で Device Flow を構築 不採用。単一システムの要件に対してインフラ追加が過剰
Authorization Code + PKCE + ローカル loopback 不採用。毎回ブラウザ起動が必要で「ブラウザのない環境」という要件に反する
Web UI で Personal Access Token (PAT) を発行して手でコピー 部分採用。Device Flow の前段として Phase 1 でリリース

ポイントは、利用している社内 SSO(OIDC IdP)がDevice Authorization Grant にネイティブ対応していないことでした。IdP 側に手を入れる案はコストが大きすぎるため、「認証(誰であるか)は従来通り SSO に任せ、CLI 用トークンの発行・検証だけを認可システム自身が担う」という責務分離で対応することにしました。

また、いきなり Device Flow を作るのではなく、Phase 1 として PAT(Web でトークンを発行して手でコピーする方式)を先行リリースしました。トークンの保存形式・Bearer 認証ミドルウェア・失効管理という基盤は PAT と Device Flow で完全に共通なので、PAT で基盤を検証してから Device Flow を載せる、という段階的な進め方ができます。

Device Flow の全体像

Device Flow は「入力手段の乏しいデバイス(テレビやスマートスピーカーなど)でも OAuth 認可を通せるようにする」仕様ですが、「トークンを使う主体(ブラウザのない端末やAIエージェント)と、承認する主体(ブラウザを持った人間)が分かれている」という構図は、社内CLIやAIエージェントのユースケースにも当てはまります。

利用イメージはこうです:

$ report login
以下のURLを開き、コードを入力してください:
  URL:  https://reports.example.com/device
  Code: WDJB-MJHT
承認待ち... (有効期限 10 分)
✓ ログインしました

$ curl -H "Authorization: Bearer $(report token)" \
       https://reports.example.com/path/to/report.txt

シーケンス図は以下の通りです。

Device Flowシーケンス図

  1. CLI が POST /device/code を叩き、device_code(CLI が持つ秘密値)と user_code(人間が入力する短いコード)のペアを受け取る
  2. CLI は URL と user_code を画面に表示し、POST /device/tokeninterval 秒間隔でポーリングし始める
  3. ユーザーは手元の PC のブラウザで検証 URL を開く。未ログインなら既存の SSO ログイン(OIDC)に流れる
  4. ログイン済みのブラウザで user_code を入力して承認すると、サーバ側で device_code にユーザーが紐付き、トークンが発行される
  5. 次のポーリングで CLI が access_tokenrefresh_token を受け取る

認証はあくまで既存の SSO が行い、認可システムは「SSO でログイン済みのユーザーが、この user_code を承認した」という事実だけを扱います。パスワードやセッションが CLI 側に渡ることはありません。

実装のポイント

ここからは、実装の中で考えどころだった点を紹介します。

トークンは平文を保存しない

発行するトークンはランダム値の不透明トークン(opaque token)で、DBにはSHA-256ハッシュのみを保存します。DBが漏れてもトークンとして使える文字列は復元できません。ユーザーへの表示は発行時の一度きりです。

JWTのような自己完結型トークンにしなかったのは、失効を即時反映したいためです。不透明トークンならDBの該当行に失効時刻を打つだけで、次のリクエストから確実に拒否できます。単一システムに閉じたトークンなので、検証のたびにDBを引くコストも問題になりません。

また、トークンには用途がわかるプレフィックス(access token / refresh token で別)を付けています。GitHub の ghp_ などでおなじみの形式で、ログや Slack にうっかり貼られたときに秘密情報だと気付きやすくする効果と、secret scanning に載せやすくする狙いがあります。

既存の認可ロジックは無改修

Bearer 認証はミドルウェアとして追加し、トークンからユーザーを解決したら、あとは既存のセッション認証と同じ認可判定(ユーザー / グループ / ロール とリソースの紐付け)を適用できます。

Bearer トークンでのファイルアクセス

認可システムの本体である「担当者ごとの権限判定」には一切手を入れていないため、「CLI 経由だと見えるはずのないレポートが見える」という事故が構造的に起きません。nginx 側の変更も、auth_request のサブリクエストに Authorization ヘッダを転送する 1 行だけでした。

proxy_set_header Authorization $http_authorization; 

まとめ

  • 担当者ごとの権限管理を行う社内レポートの認可システムに、Device Flow を実装しました
  • SSO(IdP)には手を入れず、認可システム自身が Authorization Server になることで、追加インフラゼロ・既存認可ロジック無改修で CLI・AIエージェント対応ができました
  • 「使う主体(エージェント)と承認する主体(人間)の分離」という Device Flow の性質は、AIエージェントに社内データへのアクセスを許可する仕組みとしてもそのまま機能します

同種の「社内システムの CLI 対応」でも再利用しやすい構成だと思います。どなたかの参考になれば幸いです。

We are hiring!!

エムスリーではSREを募集中です。社内基盤も手を抜かずに作りたいエンジニアを募集しています。カジュアル面談もやっていますので、お気軽にお声がけください!

jobs.m3.com open.talentio.com