Desktop Native Sign-In (RFC 8252)
تسجيل الدخول الآمن من تطبيق سطح المكتب
What this page is, and what it holds.
This page covers Desktop Native Sign-In (RFC 8252). About 5 minutes to read. Open the channel to yourself first with an allowlist. An open channel means anyone can message your agent.
How the Hermes Desktop app signs in to a gated gateway using your system browser and PKCE — no embedded webview, no session cookies
Outcomes taken from this page, not a template.
- Understand what بوابة المراسلة is and when you need it.
- Read the table and take only the row that applies to you.
- Know the common mistake before you hit it.
Jump to the part you need.
Nothing summarised away.
The documentation body below is reproduced from the official source so commands and identifiers stay exact. Each section carries a short note describing what it contains.
When the Hermes Desktop app connects to a gated gateway (a hosted or self-hosted dashboard that sits behind an OAuth provider), it can sign in two ways:
- Native sign-in (RFC 8252) — the app opens your real system browser, you approve in the browser you already trust, and the app receives tokens it stores in your OS keychain. No embedded webview, no browser session cookies. This is the default whenever the gateway supports it.
- Embedded sign-in (legacy fallback) — the app opens a small in-app browser window and captures the gateway's session cookie. Used automatically when the gateway is an older build that doesn't advertise native sign-in.
You don't choose between these — the app detects what the gateway supports and picks the best one. This page explains what happens and why.
Why native sign-in
Explains the idea itself. Read it slowly; the later sections build on it.
Embedding a browser inside a native app for OAuth has well-known downsides: the login page can't see your existing browser session (so you re-type credentials and re-do MFA), password managers and passkeys often don't work, and the app relies on reading a session cookie out of a private webview. RFC 8252 ("OAuth 2.0 for Native Apps") is the industry best practice that avoids all of that: **do the authorization in the system browser and hand the app its own tokens.**
For Hermes specifically, native sign-in means:
- No embedded webview. The authorization happens in Safari / Chrome / Firefox / Edge — whatever you use — with your logins, extensions, and passkeys intact.
- No session cookies. The app holds an OAuth access token (short-lived) and refresh token, encrypted at rest via your OS keychain (Electron
safeStorage). REST calls and WebSocket tickets are authenticated with anAuthorization: Bearerheader, not a cookie jar.
How it works
Explains the idea itself. Read it slowly; the later sections build on it.
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 OS keychain; use Bearer for REST + WS ticketsThe gateway brokers the flow: it is the authorization server *to the
desktop app and an OAuth client to the upstream identity provider* (Nous
Portal). This is required because the upstream client_id and permitted
redirect URIs are bound to the gateway's own origin — a desktop app can't be a
direct client of the Portal. The desktop still gets the full RFC 8252
experience: its own PKCE pair, its own loopback redirect, and tokens it owns.
PKCE (RFC 7636) protects the loopback hop: the one-time gateway code is useless without the code verifier, which never leaves the app. The code is single-use and short-lived.
Capability detection & fallback
Explains the idea itself. Read it slowly; the later sections build on it.
The desktop reads the gateway's public /api/status endpoint, which advertises
an auth_flows array:
auth_flows value | Meaning |
|---|---|
["cookie", "native_pkce"] | Gateway supports native sign-in → the app uses it |
["cookie"] | Gateway supports only the legacy flow → the app uses the embedded webview |
| (field absent) | Older gateway → the app uses the embedded webview |
If native sign-in is advertised but fails for a local reason — e.g. a security tool blocks the loopback listener, or you close the browser tab — the app falls back to the embedded flow automatically so you can still sign in.
Token lifecycle
Explains the idea itself. Read it slowly; the later sections build on it.
- Access token: short-lived (minutes). Sent as
Authorization: Beareron every REST call and when minting a WebSocket ticket. - Refresh token: longer-lived, rotating. When the access token is near expiry the app calls
/auth/native/refreshto rotate both tokens, then updates the keychain. - Terminal expiry: if the refresh token is dead (expired / revoked / reuse-detected), the app clears its stored tokens and prompts a fresh sign-in.
- Sign out: clears both the native tokens (keychain) and any legacy session cookie for that gateway.
For gateway operators
Explains the idea itself. Read it slowly; the later sections build on it.
Native sign-in is available automatically on any gated gateway with an
interactive session provider registered. No configuration is required — the
/auth/native/* routes and the auth_flows advertisement are part of the
dashboard-auth subsystem. OAuth providers (e.g. the bundled Nous provider)
broker the upstream IDP redirect; password providers (e.g. the bundled
basic-auth plugin) land the system browser on the gateway's /login
credential form instead — which is what lets OS password managers (macOS
Passwords, etc.) autofill the form, something no embedded desktop webview can
offer. Token-only credentials (e.g. drain) are not interactive sign-ins and do
not advertise native_pkce.
The relevant endpoints (all public, pre-auth bootstrap, same as the existing
/auth/* OAuth routes):
GET /auth/native/authorize— starts the brokered PKCE loginPOST /auth/native/token— exchanges the loopback code + verifier for tokensPOST /auth/native/refresh— rotates tokens from the app's refresh token
See also
Explains the idea itself. Read it slowly; the later sections build on it.
- OAuth over SSH / Remote Hosts — the loopback-callback pattern for provider/MCP OAuth on remote machines.
- Run Hermes with Nous Portal
2 questions answered by this page alone.
Every option is a real identifier from the Hermes documentation. The wrong ones are real too, just from other pages.