mirror of
https://github.com/openai/codex.git
synced 2026-09-10 20:26:47 +00:00
Enable user verification for local Codex Desktop sessions (#44613)
## What changed Allow app-server to advertise `openai/elicitation.userVerification` and route verification requests to local stdio clients named `Codex Desktop` when `experimentalApi` is enabled and the device supports verification. Preserve support for the in-process `codex-tui` client. Document the experimental verification API, local credential lifecycle, transport restrictions, and GUI requirements for cancellation and late proofs. ## Testing Extend activation tests to cover desktop capability advertisement and request eligibility, including rejection for other transports, unrecognized client names, missing opt-in, and unsupported devices. GitOrigin-RevId: ba4fd4b83b9c24541df3a490396d6c715e7b7433
This commit is contained in:
@@ -44,6 +44,67 @@ after a client tries to archive or delete it.
|
||||
After the owner releases the worker, its saved conversation can be archived or
|
||||
deleted normally. Ordinary client-controlled threads keep their existing behavior.
|
||||
|
||||
## User verification (experimental)
|
||||
|
||||
Codex app-server advertises `openai/elicitation.userVerification` to the
|
||||
host-owned plugin service for bundled, in-process TUI sessions (`codex-tui`) and
|
||||
local stdio desktop sessions (`Codex Desktop`) on devices with supported biometric
|
||||
hardware and the `experimentalApi` opt-in. This is an app-server decision,
|
||||
independent of whether a key exists; TUI/Desktop/mobile do not advertise this MCP
|
||||
capability. Mobile integration requires a separate rollout. Other clients and
|
||||
network connections do not receive this mode, even with a recognized client name.
|
||||
Before sending verification requests to desktop sessions, deploy a GUI that
|
||||
handles the typed verification request, cancellation, and late proofs. The general
|
||||
`experimentalApi` opt-in does not identify a compatible GUI version.
|
||||
|
||||
Local UI clients use five methods. They require the existing
|
||||
`experimentalApi` opt-in. The local provider reports
|
||||
`unavailable/providerUnavailable` on unsupported platforms or without the required
|
||||
ChatGPT account identity.
|
||||
|
||||
| Method | Params | Result |
|
||||
| --- | --- | --- |
|
||||
| `userVerification/status` | `{}` | `{credentialId, unavailableReason, unavailableMessage}` |
|
||||
| `userVerification/enroll` | `{}` | `{credentialId}` |
|
||||
| `userVerification/delete` | `{}` | `{}` |
|
||||
| `userVerification/verify` | `{challenge, title, description}` | `{proof: {credentialId, signature}}` |
|
||||
| `userVerification/cancel` | `{requestId}` | `{}` |
|
||||
|
||||
Status reads local readiness without prompting or contacting a backend. A null
|
||||
`unavailableReason` means local checks passed, not that registration is valid.
|
||||
Unsupported platforms and missing account identity are reported in the status
|
||||
response's `unavailableReason` field.
|
||||
The initial enrollment creates or reuses the local key only. Backend
|
||||
registration and revocation are integration TODOs; local success is not server
|
||||
enrollment. Deletion currently removes that local key synchronously.
|
||||
Enrollment and deletion coordinate credential lifecycle; callers do not issue
|
||||
separate generate or rotate commands. Identity comes from the authenticated
|
||||
account; this API exposes no caller-selected scope.
|
||||
|
||||
Verify signs 1–4096 decoded challenge bytes using P-256 ECDSA with SHA-256. The
|
||||
challenge and DER signature use unpadded base64url. Title is 1–256 UTF-8 bytes;
|
||||
description is at most 4096 bytes. The UI obtains approval for that display
|
||||
context before calling. Verify does not require a pending elicitation; a UI with
|
||||
its own authenticator can return proof directly in elicitation response content.
|
||||
The calling flow owns pending-request checks and discards late proofs.
|
||||
Native enroll, delete, and verify accept local stdio and in-process connections.
|
||||
WebSocket and remote-control peers must use their own device authenticator;
|
||||
status remains available for local readiness. Dropping an embedded RPC, disconnecting,
|
||||
or changing authentication cancels its native operation. Responses recheck the
|
||||
captured identity after waiting for outbound queue capacity.
|
||||
Canceling or resolving an elicitation does not itself stop a separate
|
||||
`userVerification/verify` RPC. The GUI must use `userVerification/cancel` to
|
||||
cancel that RPC and discard late proofs when an approval is canceled or resolved.
|
||||
See [User verification cancellation](#user-verification-cancellation-experimental)
|
||||
for request ID and acknowledgment semantics.
|
||||
Only one native worker runs per app-server. If an OS call remains active after
|
||||
cancellation or timeout, subsequent local operations return `failed/providerError`
|
||||
until that worker exits.
|
||||
|
||||
Failures use the normal JSON-RPC error envelope with closed `{type, reason}` data:
|
||||
`invalidRequest`, `unavailable`, `cancelled`, or `failed`. UI clients branch on
|
||||
these values rather than message text. Native diagnostic payloads stay private.
|
||||
|
||||
# Amazon Bedrock authentication
|
||||
|
||||
If `model_providers.amazon-bedrock.aws.credential_export` is configured, Bedrock setup and
|
||||
|
||||
@@ -14,6 +14,7 @@ use codex_protocol::mcp::OPENAI_ELICITATION_EXTENSION_ID;
|
||||
use super::*;
|
||||
use crate::message_processor::ConnectionSessionState;
|
||||
use crate::message_processor::InitializedConnectionSessionState;
|
||||
use crate::transport::ConnectionOrigin;
|
||||
|
||||
const NON_ORIGINATING_CLIENT_NAMES: &[&str] = &["codex_app_server_daemon", "codex-backend"];
|
||||
|
||||
@@ -95,14 +96,14 @@ impl InitializeRequestProcessor {
|
||||
"Invalid clientInfo.name: '{name}'. Must be a valid HTTP header value."
|
||||
)));
|
||||
}
|
||||
// The bundled TUI shares this build and implements the typed verification UI.
|
||||
// Independently deployed UIs need their own rollout before receiving this mode.
|
||||
// Activate only the embedded TUI and local desktop host. Client-supplied
|
||||
// extensions cannot opt other hosts into verification.
|
||||
let user_verification_enabled = experimental_api_enabled
|
||||
&& matches!(
|
||||
session.origin,
|
||||
crate::transport::ConnectionOrigin::InProcess
|
||||
(session.origin, name.as_str()),
|
||||
(ConnectionOrigin::InProcess, "codex-tui")
|
||||
| (ConnectionOrigin::Stdio, "Codex Desktop")
|
||||
)
|
||||
&& name == "codex-tui"
|
||||
&& tokio::task::spawn_blocking(self.user_verification.device_supported)
|
||||
.await
|
||||
.unwrap_or(false);
|
||||
|
||||
@@ -26,6 +26,32 @@ async fn user_verification_initialize_owns_advertisement_and_eligibility() -> Re
|
||||
(ConnectionOrigin::InProcess, "other-ui", true, true, false),
|
||||
(ConnectionOrigin::InProcess, "codex-tui", false, true, false),
|
||||
(ConnectionOrigin::InProcess, "codex-tui", true, false, false),
|
||||
(ConnectionOrigin::Stdio, "Codex Desktop", true, true, true),
|
||||
(
|
||||
ConnectionOrigin::InProcess,
|
||||
"Codex Desktop",
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
),
|
||||
(
|
||||
ConnectionOrigin::WebSocket,
|
||||
"Codex Desktop",
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
),
|
||||
(
|
||||
ConnectionOrigin::RemoteControl,
|
||||
"Codex Desktop",
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
),
|
||||
(ConnectionOrigin::Stdio, "other-ui", true, true, false),
|
||||
(ConnectionOrigin::Stdio, "codex_desktop", true, true, false),
|
||||
(ConnectionOrigin::Stdio, "Codex Desktop", false, true, false),
|
||||
(ConnectionOrigin::Stdio, "Codex Desktop", true, false, false),
|
||||
] {
|
||||
let probe: fn() -> bool = if supported { || true } else { || false };
|
||||
let mut h = Harness::new(origin, probe).await?;
|
||||
|
||||
Reference in New Issue
Block a user