feat(providers): add credential-fenced OAuth key cleanup

This commit is contained in:
ZheFox
2026-07-28 11:32:11 +08:00
parent 531cf11025
commit f0f87b56a3
17 changed files with 702 additions and 142 deletions
@@ -5,10 +5,11 @@ pub use snapshot::ProviderCatalogSnapshot;
pub use types::{
ProviderCatalogKeyAdaptiveState, ProviderCatalogKeyAdaptiveStateUpdate,
ProviderCatalogKeyHealthStateUpdate, ProviderCatalogKeyListOrder, ProviderCatalogKeyListQuery,
ProviderCatalogKeyOAuthCredentialFence, ProviderCatalogKeyOAuthRuntimeStateCasUpdate,
ProviderCatalogKeyRuntimeMetadataUpdate, ProviderCatalogKeyStatusSnapshotUpdate,
ProviderCatalogReadRepository, ProviderCatalogUpstreamMetadataNamespaceUpdate,
ProviderCatalogWriteRepository, StoredProviderCatalogEndpoint, StoredProviderCatalogKey,
ProviderCatalogKeyOAuthCredentialCasDelete, ProviderCatalogKeyOAuthCredentialFence,
ProviderCatalogKeyOAuthRuntimeStateCasUpdate, ProviderCatalogKeyRuntimeMetadataUpdate,
ProviderCatalogKeyStatusSnapshotUpdate, ProviderCatalogReadRepository,
ProviderCatalogUpstreamMetadataNamespaceUpdate, ProviderCatalogWriteRepository,
StoredProviderCatalogEndpoint, StoredProviderCatalogKey,
StoredProviderCatalogKeyMaintenanceSummary, StoredProviderCatalogKeyPage,
StoredProviderCatalogKeyStats, StoredProviderCatalogProvider,
};
@@ -79,6 +79,15 @@ pub struct ProviderCatalogKeyOAuthCredentialFence {
pub provider_type: String,
}
/// Atomic key deletion fenced by the exact OAuth credential generation that
/// produced the terminal failure.
#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
pub struct ProviderCatalogKeyOAuthCredentialCasDelete {
pub key_id: String,
pub expected_encrypted_auth_config: Option<String>,
pub expected_credential: ProviderCatalogKeyOAuthCredentialFence,
}
/// Agent/runtime-owned OAuth state update fenced by the exact encrypted
/// auth_config and, when supplied, credential context observed before the
/// refresh started. Repositories must update only these fields and return
@@ -853,6 +862,16 @@ pub trait ProviderCatalogWriteRepository: Send + Sync {
async fn delete_key(&self, key_id: &str) -> Result<bool, crate::DataLayerError>;
async fn compare_and_delete_key_oauth_credential(
&self,
_delete: &ProviderCatalogKeyOAuthCredentialCasDelete,
) -> Result<bool, crate::DataLayerError> {
Err(crate::DataLayerError::InvalidConfiguration(
"provider catalog OAuth credential CAS deletes are not supported by this repository"
.to_string(),
))
}
async fn clear_key_oauth_invalid_marker(
&self,
key_id: &str,