1

I am trying to access zapi to get test details from zephyr(Jira) using JWT authorization.But the qsh claim generated is incorrect which makes the JWT incorrect. Is there any alternate method to create qsh in C#?

When tried in Postman client with the generated JWT value, it is giving the error that the incorrect qsh value is used and displays the correct qsh value. I hard coded the displayed value (instead of dynamic qsh creation) for JWT generation and that is giving a success response. The code is given below:

    var canonical_path_t = "GET&" + RELATIVE_PATH_T + QUERY_STRING_T;
    var payload = new Dictionary<string, object>()
    {
        { "sub", ACCOUNT_ID },              //assign subject
        { "qsh", getQSH(canonical_path_t) },  //assign query string hash
        { "iss", ACCESS_KEY },              //assign issuer
        { "iat", iat },                     //assign issue at(in ms)
        { "exp", exp }                      //assign expiry time(in ms)
    };

    string token = JWT.JsonWebToken.Encode(payload, SECRET_KEY, JWT.JwtHashAlgorithm.HS256);

    client.DefaultRequestHeaders.Add("Authorization", "JWT " + token);
    client.DefaultRequestHeaders.Add("zapiAccessKey", ACCESS_KEY);
    client.DefaultRequestHeaders.Add("User-Agent", "ZAPI");         


   //code to generate qsh           
   static string getQSH(string qstring)
    {
        System.Security.Cryptography.SHA256Managed crypt = new 
        System.Security.Cryptography.SHA256Managed();
        StringBuilder hash = new StringBuilder();
        byte[] crypto = crypt.ComputeHash(Encoding.UTF8.GetBytes(qstring), 0, Encoding.UTF8.GetByteCount(qstring));
        foreach (byte theByte in crypto)
            {
                hash.Append(theByte.ToString("x2"));
            }

            return hash.ToString();
    }
Ash G
  • 11
  • 3

2 Answers2

1

TL;DR: qsh is sha256 hexadecimal value of METHOD & RELATIVE PATH & QUERY PARAMETERS SORTED ALPHABETICALLY.

I had some issues related with the qsh parameter for Zephyr as well. I was creating by myself with NodeJS. It seems the major of paths were working correctly but some of them, which are using query parameter, they were giving me qsh incorrect.

The final reason is the query parameters in the qsh field should be sorted alphabetically. It is not related to the path, it is just a requirement to generate the qsh correctly. With NodeJS I only did this one before generating the qsh:

const sortedQueryString = queryString.split("&").sort().join('&');
const canonicalPath = `${method}&${relativePath}&${sortedQueryString}`;

So, for your C# implementation, I supposed it should be something like that (not tested):

var sorted_query_string = QUERY_STRING_T.Split("&").Sort().Join("&");
var canonical_path_t = "GET&" + RELATIVE_PATH_T + "&" + sorted_query_string;
...

Don't forget to include & between the relative path and query parameters. Don't use the ?.

SnakeDrak
  • 3,406
  • 4
  • 28
  • 41
  • This was a life saver. I wouldn't have figured out that it should be alphabetically sorted in node js. Thank you!! – A.J Jul 28 '22 at 11:44
0

i can verify that the getQSH method you are using SHOULD work (as i've had to interact with ZAPI myself) so the first thing i would check is your cannonical path. Make sure you have it structured correctly. For example ...

        var RELATIVE_PATH = $"/public/rest/api/1.0/executions/search/cycle/{cycle}";

        var QUERY_STRING = $"offset={offset}&projectId={project}&versionId={version}";

        var canonical_path = "GET&" + RELATIVE_PATH + "&" + QUERY_STRING;