mirror of
https://github.com/openai/codex.git
synced 2026-10-20 17:38:45 +00:00
Add renewable EMA HTTP authentication and credential versioning (#49987)
## What changed - Add `EmaAuthenticatedHttpClient` to cache and renew resource bearers, serialize concurrent renewals, and include authentication time in the request deadline. Confine bearers to the configured MCP endpoint and reject redirects. - Return authentication-required errors for `401` responses without replaying MCP operations. Preserve a newer cached bearer when a delayed response rejects an older one. - Add revocable request authority that cancels pending requests and ends response streams. - Pin enterprise credentials to a persistent version advanced on save or deletion. Use short credential locks and invalidate rejected grants only when their record and version are still current, preserving newer logins. - Hide keyring account identifiers from enterprise credential errors and traces while preserving authentication failure classification. ## Testing Add tests for token expiry and renewal, endpoint confinement, redirects, request deadlines, revocation during renewal and streaming, delayed `401` responses, credential replacement and logout races, and credential error privacy. GitOrigin-RevId: ee3e5cf7ecbba3d09e1ab29ed4fbb2a35eacc60d
This commit is contained in:
@@ -19,6 +19,7 @@ use tokio::runtime::Handle;
|
||||
use tokio::sync::OwnedSemaphorePermit;
|
||||
use tokio::sync::mpsc;
|
||||
use tokio::sync::mpsc::error::TrySendError;
|
||||
use tokio_util::sync::CancellationToken;
|
||||
use tracing::debug;
|
||||
|
||||
use crate::client::ExecServerError;
|
||||
@@ -75,6 +76,7 @@ enum HttpResponseBodyStreamInner {
|
||||
/// id until EOF or a terminal error.
|
||||
pub struct HttpResponseBodyStream {
|
||||
inner: HttpResponseBodyStreamInner,
|
||||
cancellation: Option<CancellationToken>,
|
||||
}
|
||||
|
||||
impl HttpResponseBodyStream {
|
||||
@@ -93,6 +95,7 @@ impl HttpResponseBodyStream {
|
||||
inner: HttpResponseBodyStreamInner::Local {
|
||||
body: Box::pin(body),
|
||||
},
|
||||
cancellation: None,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -101,6 +104,7 @@ impl HttpResponseBodyStream {
|
||||
inner: HttpResponseBodyStreamInner::Local {
|
||||
body: Box::pin(response.bytes_stream()),
|
||||
},
|
||||
cancellation: None,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -118,14 +122,32 @@ impl HttpResponseBodyStream {
|
||||
pending_eof: false,
|
||||
closed: false,
|
||||
},
|
||||
cancellation: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Ends this stream when its owning request authority is revoked.
|
||||
pub fn cancel_on(mut self, cancellation: CancellationToken) -> Self {
|
||||
self.cancellation = Some(cancellation);
|
||||
self
|
||||
}
|
||||
|
||||
/// Receives the next response-body chunk.
|
||||
///
|
||||
/// Returns `Ok(None)` at EOF and converts sequence gaps or stream-side
|
||||
/// stream errors into protocol errors.
|
||||
pub async fn recv(&mut self) -> Result<Option<Vec<u8>>, ExecServerError> {
|
||||
let Some(cancellation) = self.cancellation.clone() else {
|
||||
return self.recv_inner().await;
|
||||
};
|
||||
tokio::select! {
|
||||
biased;
|
||||
() = cancellation.cancelled() => Ok(None),
|
||||
result = self.recv_inner() => result,
|
||||
}
|
||||
}
|
||||
|
||||
async fn recv_inner(&mut self) -> Result<Option<Vec<u8>>, ExecServerError> {
|
||||
match &mut self.inner {
|
||||
HttpResponseBodyStreamInner::Local { body } => match body.next().await {
|
||||
Some(chunk) => match chunk {
|
||||
|
||||
Reference in New Issue
Block a user