---
title: "デスクトップアプリのネイティブサインイン（RFC 8252）"
description: "Hermes デスクトップアプリが、システムのブラウザと PKCE を使って認証付きゲートウェイにサインインする仕組み。埋め込みの WebView もセッションクッキーも使いません"
upstream_path: guides/desktop-native-signin.md
upstream_blob: f8402c8229d3c681ef6c2a6efdc1c1c9a63c353f
sources:
  - https://hermes-agent.nousresearch.com/docs/guides/desktop-native-signin
---

# デスクトップアプリのネイティブサインイン（RFC 8252） {#desktop-native-sign-in-rfc-8252}

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

1. **ネイティブサインイン（RFC 8252）** — アプリが**普段使っているシステムのブラウザ**を開き、
   すでに信頼しているそのブラウザ上で承認します。アプリはトークンを受け取り、自分のユーザー
   データ用ディレクトリに、本人だけが読めるファイルとして保存します（設定 → Gateway で、OS の
   キーチェーンによる暗号化も選べます）。**埋め込みの WebView も、ブラウザのセッションクッキーも
   使いません。** ゲートウェイが対応していれば、こちらが既定です。
2. **埋め込みサインイン（従来の代替手段）** — アプリが小さなアプリ内ブラウザの窓を開き、
   ゲートウェイのセッションクッキーを受け取ります。ゲートウェイが古い版で、ネイティブ
   サインインに対応していると表明しない場合に自動で使われます。

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

## ネイティブサインインを選ぶ理由 {#why-native-sign-in}

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` ヘッダーで認証します。

## 動きの流れ {#how-it-works}

```
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 回きりで、寿命も短く
してあります。

## 対応状況の判定と代替手段 {#capability-detection-fallback}

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

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

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

## トークンの寿命 {#token-lifecycle}

- **アクセストークン**: 寿命は短く、数分です。REST の呼び出しのたび、また WebSocket の
  チケットを発行するときに `Authorization: Bearer` として送られます。
- **リフレッシュトークン**: もう少し長持ちし、使うたびに入れ替わります。アクセストークンの
  期限が近づくと、アプリは `/auth/native/refresh` を呼んで両方のトークンを入れ替え、
  保存内容を更新します。
- **完全な失効**: リフレッシュトークンが使えなくなった場合（期限切れ、取り消し、再利用の
  検知）、アプリは保存していたトークンを消し、あらためてサインインを求めます。
- **サインアウト**: そのゲートウェイについて、保存したネイティブのトークンと、従来方式の
  セッションクッキーの両方を消します。

## ゲートウェイを運用する方へ {#for-gateway-operators}

ネイティブサインインは、対話型のセッションプロバイダを登録した認証付きゲートウェイなら
自動的に使えます。設定は不要です。`/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` — アプリのリフレッシュトークンから、トークンを入れ替えます

## 関連ページ {#see-also}

- [SSH 越し・リモートホストでの OAuth](/hermes/docs/guides/oauth-over-ssh/) — 離れた端末で
  プロバイダや MCP の OAuth を行うための、ループバックで受け取る手順です。
- [Nous Portal で Hermes を動かす](/hermes/docs/guides/run-hermes-with-nous-portal/)
