Hermes Agent Wiki 非公式・日本語wiki

デスクトップアプリのネイティブサインイン(RFC 8252)

目次

Hermes のデスクトップアプリが認証付きゲートウェイ(OAuth プロバイダの後ろに置かれた、 ホスト型または自前で立てたダッシュボード)につなぐとき、サインインの方法は 2 つあります。

  1. ネイティブサインイン(RFC 8252) — アプリが普段使っているシステムのブラウザを開き、

すでに信頼しているそのブラウザ上で承認します。アプリはトークンを受け取り、自分のユーザー データ用ディレクトリに、本人だけが読めるファイルとして保存します(設定 → Gateway で、OS の キーチェーンによる暗号化も選べます)。埋め込みの WebView も、ブラウザのセッションクッキーも 使いません。 ゲートウェイが対応していれば、こちらが既定です。

  1. 埋め込みサインイン(従来の代替手段) — アプリが小さなアプリ内ブラウザの窓を開き、

ゲートウェイのセッションクッキーを受け取ります。ゲートウェイが古い版で、ネイティブ サインインに対応していると表明しない場合に自動で使われます。

どちらを使うかを選ぶ必要はありません。アプリがゲートウェイの対応状況を調べて、良いほうを 選びます。このページでは、そのとき何が起きているのか、なぜそうしているのかを説明します。

ネイティブサインインを選ぶ理由

OAuth のためにネイティブアプリの中へブラウザを埋め込むやり方には、よく知られた弱点が あります。ログイン画面が既存のブラウザのセッションを見られないので、認証情報を打ち直し、 多要素認証もやり直しになります。パスワード管理ソフトやパスキーが動かないことも多く、 さらにアプリは非公開の WebView からセッションクッキーを読み出すことに頼っています。RFC 8252("OAuth 2.0 for Native Apps")は、こうした問題をまとめて避けるための業界の定石です。 やることは一言でいえば、認可はシステムのブラウザで行い、アプリには自分のトークンを 渡すということです。

Hermes に即していうと、ネイティブサインインは次のことを意味します。

  • 埋め込みの WebView を使いません。 認可は Safari、Chrome、Firefox、Edge など、普段

使っているブラウザで行われます。ログイン状態も、拡張機能も、パスキーもそのまま使えます。

  • セッションクッキーを使いません。 アプリが持つのは OAuth のアクセストークン(短命)と

リフレッシュトークンで、本人だけが読めるファイルとして保存されます。設定 → Gateway の キーチェーンの切り替えを有効にすれば、OS のキーチェーン(Electron の safeStorage)で 保存時に暗号化されます。REST の呼び出しと WebSocket のチケットは、クッキーではなく Authorization: Bearer ヘッダーで認証します。

動きの流れ

Desktop app                Gateway (/auth/native/*)          Nous Portal (IDP)
   │ 1. open loopback 127.0.0.1:<random port>
   │ 2. system browser ─►  /auth/native/authorize
   │    (PKCE challenge)    (starts the normal PKCE login) ─► /oauth/authorize
   │                        ◄──── code ──── /auth/callback ◄──┘
   │                        3. mint one-time gateway code
   │ ◄─ 302 127.0.0.1/cb?code=… ─┘
   │ 4. POST /auth/native/token (code + PKCE verifier)
   │ ◄─ 5. { access_token, refresh_token, expires_at } ───────┘
   │ 6. store in local token store; use Bearer for REST + WS tickets

ゲートウェイはこの流れを仲立ちします。すなわち、*デスクトップアプリから見れば*認可サーバー であり、*上流の ID プロバイダ*(Nous Portal)から見れば OAuth のクライアントです。これは、 上流の client_id と許可されたリダイレクト先がゲートウェイ自身のオリジンに結びついていて、 デスクトップアプリが Portal の直接のクライアントになれないためです。それでもデスクトップ側は RFC 8252 の恩恵をそのまま受けられます。自分の PKCE の組、自分のループバックのリダイレクト先、 そして自分が持つトークンです。

PKCE(RFC 7636) はループバックの受け渡しを守ります。使い捨てのゲートウェイコードは、 アプリの外に出ない検証子がなければ役に立ちません。このコードは 1 回きりで、寿命も短く してあります。

対応状況の判定と代替手段

デスクトップ側はゲートウェイの公開エンドポイント /api/status を読みます。ここには auth_flows という配列が載っています。

auth_flows の値 意味
["cookie", "native_pkce"] ゲートウェイがネイティブサインインに対応 → アプリはこちらを使います
["cookie"] ゲートウェイは従来の方式のみ対応 → アプリは埋め込みの WebView を使います
*(項目そのものがない)* 古いゲートウェイ → アプリは埋め込みの WebView を使います

ネイティブサインインに対応していると出ているのに、手元の事情で失敗することもあります。 たとえばセキュリティソフトがループバックの待ち受けを止めた、ブラウザのタブを閉じた、 といった場合です。そのときアプリは自動的に埋め込みの方式へ切り替えるので、サインインは そのまま続けられます。

トークンの寿命

  • アクセストークン: 寿命は短く、数分です。REST の呼び出しのたび、また WebSocket の

チケットを発行するときに Authorization: Bearer として送られます。

  • リフレッシュトークン: もう少し長持ちし、使うたびに入れ替わります。アクセストークンの

期限が近づくと、アプリは /auth/native/refresh を呼んで両方のトークンを入れ替え、 保存内容を更新します。

  • 完全な失効: リフレッシュトークンが使えなくなった場合(期限切れ、取り消し、再利用の

検知)、アプリは保存していたトークンを消し、あらためてサインインを求めます。

  • サインアウト: そのゲートウェイについて、保存したネイティブのトークンと、従来方式の

セッションクッキーの両方を消します。

ゲートウェイを運用する方へ

ネイティブサインインは、対話型のセッションプロバイダを登録した認証付きゲートウェイなら 自動的に使えます。設定は不要です。/auth/native/* の経路と auth_flows の表明は、 ダッシュボードの認証機構の一部だからです。OAuth のプロバイダ(たとえば同梱の Nous プロバイダ)は上流の ID プロバイダへのリダイレクトを仲立ちします。パスワード型の プロバイダ(たとえば同梱の basic-auth プラグイン)の場合は、システムのブラウザが ゲートウェイの /login の入力画面に着地します。これによって OS のパスワード管理 (macOS のパスワードなど)が入力欄を自動補完でき、埋め込みのデスクトップ WebView には 真似できない使い勝手になります。トークンだけの認証情報(drain など)は対話型のサインイン ではないので、native_pkce を表明しません。

関係するエンドポイントは次のとおりです(いずれも公開で、認証前の入口として、既存の /auth/* の OAuth 経路と同じ扱いです)。

  • GET /auth/native/authorize — 仲立ちされた PKCE ログインを始めます
  • POST /auth/native/token — ループバックのコードと検証子をトークンに交換します
  • POST /auth/native/refresh — アプリのリフレッシュトークンから、トークンを入れ替えます

関連ページ

プロバイダや MCP の OAuth を行うための、ループバックで受け取る手順です。