I am trying to set a user property (/rest/api/3/user/properties/?accountId=xxxxx) via the Jira Cloud REST API outside the connect app scope, but for the moment I get 401 unauthorized response.
What I am trying to accomplish is as follows:
User clicks on connect to our service on an issue glance
A window is opened where he logs into our system with his credentials (from our system)
I send the context JWT (from Cloud app) as a parameter as well to the window (and on installed hook I save the tenant data as well to db)
I use the clientKey (present in context JWT sent earlier) to retrieve tenant data from our db (validate token and generate a JWT token which I would use to set a user property (our API key) for the user by accountId
The documentation says that I am able to:
POST https://.atlassian.net/jira/rest/api/2/issue/AC-1/attachments
âAuthorizationâ header value: "JWT " but I get unauthorized when I try to access the endpoint.
Yes, the lack of QSH is the reason why youâre getting a 401. This is a required claim. If youâre using NodeJS or Java, you can check how Atlassian creates the JWT in ACE / ACSB source code.
I added urlencode but still nothing:
$qsh = hash(âsha256â, utf8_encode(urlencode($cannonicalMethod.â&â.$cannonicalUrl.â&â.$cannonicalQueryString))).
I wrote a better query string hash specification for Bitbucket long ago (not sure why the doc was never ported to Jira & Confluence docs because it is the same). This should help you test a QSH calculation function in any language: https://developer.atlassian.com/cloud/bitbucket/query-string-hash/
Specifically, I think you have the urlencode method in the wrong place. The canonical query string should be URL encoded, but if you put it around the whole canonical request, then you incorrectly encode the ampersands (should be & not %26).
Edit: And URL encoding should not apply to the equal in the canonical query string (should be = not %3D).
Thank you for your link I will look into in more detail. For now I tried moving the urlencode to the canonical query string but I still have some errors because I receive 401 Unauthorized.
Also here because I have only 1 parameter without spaces and ampersands urlencode does not help I think in my case.
In my final use case I will have only 1 GET parameter because I want to set a user property to a specific account (PUT ./rest/api/3/user/properties/myProperty?accountId=5cdae9b3254e450fd8d21090 and property data in body with Authorization header JWT ). I am trying to use the GET properties endpoint for now to try to get a sense of a valid qsh claim.
The problem is somewhere else I think, maybe on the utf8 bytes or constructing the sha256. Do you know some kind of sandbox area where I could validate if my qsh is in a correct format? or only the documentation that you provided exists?
I would âbisectâ the problem. Construct the canonical request (the string just before hashing) and then hash it. Post both here and I can act as your validator. As a hash, thereâs nothing secret in there. I can help make sure you are constructing the canonical request correctly and there arenât any PHP-specific tricks in hashing.
Generally, I recommend following the examples in my link with unit tests so you can make sure that any QSH would work.
I confirm the fullCannonicalQueryString looks correct. So I agree with your assessment that weâre getting tangled up in hash. That bit about utf8 encoding as bytes might be too language specific. I think thatâs necessary in JavaScript, and maybe Java too. But it looks like modern PHP may already handle that in the hash function. Could you try a known example:
I tried hashing what you sent and indeed it is the same that the known hash that you sent. This was without using utf8_encode.
I tried my qsh without using utf8_encode and same result (401 Unauthorized)
So to recap I am making a GET request to my develop mode jira cloud site /rest endpoints where I have my Connect app. The request is made from outside but I have the tenant details stored (crypted) in db and I use that shared secret to sign the JWT token. I provide an Authorization header called JWT with this token.
So can you please clarify if I am doing something incorrect in the steps above?
Auth is such a complicated chain! Fortunately, weâve eliminated the QSH, which is often the âprime suspectâ in JWT. For background context, you might find this JWT explainer video useful:
But to debug your case (and try to eliminate JWT itself), can you post the JWT token?
This is my JWT:
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiI3NGE0NjFlNi0yN2U0LTM1YWYtYTE1OS0yYWM0MGEwY2I2ODYiLCJzdWIiOiI1Y2RhZTliMzI1NGU0NTBmZDhkMjEwOTAiLCJxc2giOiI0NTcwZmNhOTcyNjFkZmI0OTIxMDM5MGFiMmNmZWJiNWQxOTNiZGM0ZjBmZjlmMTk0MzlhMzcxMjQ0NzBmMzEzIiwiaWF0IjoxNjIzMjQ2OTgwLCJleHAiOjE2MjMyNDc4ODB9.Q4Bu8i6si_Yg-w3Sh1RvSb3Mt__ou73Yq3iZupckpKA
Does setting a user property require user impersonation? or can I set a user property using the available tenant details (/rest/api/3/user/properties/myProperty?accountId=123)?
I tried a request without the sub claim and still got 401
Thank you very much for your time and effort but for now I think I found an alternative path for handling this:
I redirect the user to our site where he logs in (using his credentials in our site) and I validate that his Cloud JWT token is valid
I generate the API key required in our side in case I receive a valid JWT and store the API key in the DB associating it to that user
When the window closes I make a request to our endpoint providing a secret AccessKey and the user account id and if there is a record set for the ApiKey for him then I retrieve it and set the user property inside the Cloud app in the users context.
I know this is not a great solution, but until I can fully clarify what is the issue from what was discussed above this remains an option for me. I hope it is regarded as a viable solution for this problem.