I am seeing a strange behavior while authenticating with the Xray Cloud API from a C# backend application.
Below is the code I initially used for authentication:
private async Task SetAuthHeaderAsync(HttpClient client, string clientId, string clientSecret) { var authTokenRequest = new JObject { ["client_id"] = clientId, ["client_secret"] = clientSecret, }; var httpResponse = await client.PostAsync( "authenticate", CreateHttpContent(authTokenRequest)); if (httpResponse.StatusCode == HttpStatusCode.OK) { var authToken = await DeserializeResponseAsync<string>(httpResponse); client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue( XRayConnectorConstants.BearerAuthScheme, authToken); return; } var message = httpResponse.StatusCode == HttpStatusCode.Unauthorized ? ErrorCodes.Validation_XRayConnector_Client_Id_Or_Secret_Incorrect : ErrorCodes.Validation_XRayConnector_Connection_Not_Reachable; _logger.LogError( "Xray authentication failed with StatusCode: {statusCode} and having Response: {response}", httpResponse.StatusCode, await httpResponse.Content.ReadAsStringAsync()); throw new HttpClientApiException(message, httpResponse.StatusCode); }private HttpContent CreateHttpContent(object content) { HttpContent httpContent = null; if (content != null) { var ms = new MemoryStream(); SerializeJsonIntoStream(content, ms); ms.Seek(0, SeekOrigin.Begin); httpContent = new StreamContent(ms); httpContent.Headers.ContentType = new MediaTypeHeaderValue(MediaTypeNames.Application.Json); } return httpContent; }
Calling:https://xray.cloud.getxray.app/api/v2/
returns 401 Unauthorized
Calling:https://eu.xray.cloud.getxray.app/api/v2/
returns 200 OK
So initially I assumed this was related to Xray data residency.
However, things get interesting here:
Calling the global endpoint (https://xray.cloud.getxray.app/api/v2/) works perfectly from:
Postman
curl
And even from the same C# backend, it starts working if I change the request content creation logic to this:
private HttpContent CreateHttpContent(object content)
{
if (content == null)
{
return null;
}
var json = JsonConvert.SerializeObject(content);
return new StringContent(
json,
Encoding.UTF8,
MediaTypeNames.Application.Json);
}After this change, the global endpoint also returns 200 OK.
So now I am confused about what exactly is happening here:
Is Xray validating something specific in the request body formatting?
Is there some difference between StreamContent and StringContent that affects authentication?
Could this be related to content length, encoding, or serialization behavior?
Has anyone faced something similar with the Xray API?