diff --git a/ZB.MOM.WW.SPHistorianClient/src/ZB.MOM.WW.SPHistorianClient/Grpc/HistorianGrpcReadOrchestrator.cs b/ZB.MOM.WW.SPHistorianClient/src/ZB.MOM.WW.SPHistorianClient/Grpc/HistorianGrpcReadOrchestrator.cs index 8fe3f72..d040949 100644 --- a/ZB.MOM.WW.SPHistorianClient/src/ZB.MOM.WW.SPHistorianClient/Grpc/HistorianGrpcReadOrchestrator.cs +++ b/ZB.MOM.WW.SPHistorianClient/src/ZB.MOM.WW.SPHistorianClient/Grpc/HistorianGrpcReadOrchestrator.cs @@ -5,6 +5,7 @@ using ZB.MOM.WW.SPHistorianClient.Models; using ZB.MOM.WW.SPHistorianClient.Wcf; using GrpcHistory = ArchestrA.Grpc.Contract.History; using GrpcRetrieval = ArchestrA.Grpc.Contract.Retrieval; +using GrpcStorage = ArchestrA.Grpc.Contract.Storage; namespace ZB.MOM.WW.SPHistorianClient.Grpc; @@ -16,17 +17,19 @@ namespace ZB.MOM.WW.SPHistorianClient.Grpc; /// /// Operation mapping (2020 WCF → 2023 R2 gRPC): /// Hist.GetInterfaceVersion → HistoryService.GetInterfaceVersion -/// Hist.ValidateClientCredential (loop) → HistoryService.ExchangeKey (loop) +/// Hist.ValidateClientCredential (loop) → StorageService.ValidateClientCredential (loop) /// Hist.OpenConnection2 → HistoryService.OpenConnection /// Retr.StartQuery2 → RetrievalService.StartQuery /// Retr.GetNextQueryResultBuffer2 (loop) → RetrievalService.GetNextQueryResultBuffer (loop) /// Retr.EndQuery2 → RetrievalService.EndQuery /// -/// NOTE: not yet live-verified against a 2023 R2 server. The auth handshake uses -/// HistoryService.ExchangeKey because the gRPC HistoryService dropped ValidateClientCredential -/// (it now lives only on StorageService) and gained ExchangeKey with the identical -/// handle+token→token shape. If a live server rejects this, the handshake op is the first thing -/// to revisit — everything else is the proven 2020 byte protocol. +/// AUTH: the SSPI/Negotiate token loop runs through StorageService.ValidateClientCredential +/// (Handle + InBuff → Status + OutBuff) — per the 2023 R2 contract analysis, that op carries the +/// NTLM/SSPI tokens (the field names inBuff/outBuff match the 2020 native contract), whereas +/// HistoryService.ExchangeKey is a separate key-exchange/cert op (NOT the credential handshake). +/// OpenConnection and the retrieval chain stay on their original services; the server correlates +/// the validated context by the handshake GUID handle. Live-verified 2026-06-19 against a 2023 R2 +/// server (wonder-sql-vd03) — earlier ExchangeKey wiring was rejected at token round 0. /// internal sealed class HistorianGrpcReadOrchestrator { @@ -162,18 +165,19 @@ internal sealed class HistorianGrpcReadOrchestrator { Guid contextKey = Guid.NewGuid(); var historyClient = new GrpcHistory.HistoryService.HistoryServiceClient(connection.Channel); + var storageClient = new GrpcStorage.StorageService.StorageServiceClient(connection.Channel); historyClient.GetInterfaceVersion(new GrpcHistory.GetInterfaceVersionRequest(), connection.Metadata, Deadline(), cancellationToken); HistorianNativeHandshake.RunTokenRounds( (handle, wrapped, _) => { - GrpcHistory.ExchangeKeyResponse response = historyClient.ExchangeKey( - new GrpcHistory.ExchangeKeyRequest { StrHandle = handle, BtInput = ByteString.CopyFrom(wrapped) }, + GrpcStorage.ValidateClientCredentialResponse response = storageClient.ValidateClientCredential( + new GrpcStorage.ValidateClientCredentialRequest { Handle = handle, InBuff = ByteString.CopyFrom(wrapped) }, connection.Metadata, Deadline(), cancellationToken); - byte[] serverOutput = response.BtOutput?.ToByteArray() ?? []; + byte[] serverOutput = response.OutBuff?.ToByteArray() ?? []; byte[] error = response.Status?.BtError?.ToByteArray() ?? []; bool success = response.Status?.BSuccess ?? false; return new HistorianNativeHandshake.TokenExchangeResult(success, serverOutput, error);