Connect to Jira with a Private Key (OAuth) using .NET

We have a .NET service that needs to connect with Jira to create, update and read issues. It would be easy if we could use a username & password for authentication, but for this integration we need to implement the connection to Jira with a Private Key. All requests have to be signed with OAuth.

There are some resources online showing how to do this with C#, but none of them worked for us. After some fiddling around we came up with some code that works.

Packages

We use the following packages (make sure you have them installed):

  • Install-Package Atlassian.SDK -Version 12.4.0
  • dotnet add package Atlassian.SDK --version 12.4.0
  • <PackageReference Include="Atlassian.SDK" Version="12.4.0" />

Getting the consumer secret

Jira uses OAuth version 1. There are 4 elements involved:

  • Access Token
  • Access Token Secret
  • Consumer Key
  • Consumer Secret

Three of these you already have, but the Consumer Secret needs to be generated out of the Private Key. Your key looks like this:

----BEGIN PRIVATE KEY-----
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQC0PIZ3R2ZPJuH0
lYJZShidrd7kwQtKUPR70ZwRzdchcII1lM98B7i9cSFUNzb511K9Bp9fMfAVwZ+U
HOiKTaZv8wi4XXLzjjJageUKHmFKOztRjeJB33bUspj+zlFRg1EkZSDTVraDz6iv
imj5tz1/1VL2/R81CCREFqKa0k2eEohQLecEeIMz7WITzLmbqTwULiLJZs028Hyb
eIoQVnbYE4YhIGGT50VWlEa4kXbh7Sof8gk6JnS1rP29raeNgr61tOXaO0/g141/
U7RleapyohfooLhNEiTEO3rd48wszrWGF4vWuRmTCOFW8cM6qLKb6EP3fCD0jLcJ
EKbgojghAgMBAAECggEAD4V41P577owRgGUnDQvG/pPnHTm3VbE5ZLetN5FLmgXH
iNgQXYTdYXRueNiqbi30t5zsOPVNe39+/8Z/i3gwFvWjMMayFrcOWgypTfZGIu+N
O7LleGPHUX4x6RuZFX/NYJN+61mignZtzJ7gTG+ofFZBfKNZaSaS157qu5Ip0gIZ
d9w4I2Q3eJBKoqHq/1NE7KTHv7KxKBYvsMqQLlkVrZItTj5EC/ffJ28EbSYR6hIY
GRJ1bM5ycvc2t0TIJh0d1VPMpazwFmF3M6eIdnBEluBjtq7C3AqcZwfOtnvQi4f7
igiObRExtgqEF+jZyUlSL43gcT29EEpAOT6YeMskmwKBgQD6MTR2mqHnzIkNya0o
tPDElt2IH0ZaYY0Whcg16c61MKbxAVvfyt4UaRwm/1Xh+97qyI/QmJR6+Zf8ijQj
1rlDW6krEZM33GTQeD/iQw8uxXAahN0LBzaxkMv8bmdKLV+zgk4ubdtTtHPIeQtl
yAQ/fwp+3chZzR9NG6aN7BuK5wKBgQC4a5mtosVPCVPetOtSR4M90bYpK04UOWW8
wIuoMplTDbVQaJDh76+IdQR+k8vhs1noE+bUPyV+r8RmkI1LfQheBaBR8dLmqN2Q
L0FLtL/g0EQ4WntjLhFRD8nr70MYyVV7iA6u5kOXgGvjxQXZv6Rzb5lFIvpCi4Nv
YNbYEPYLtwKBgQCF2Q8dKIrXjKgB1VQrA+oO8jsgGMM1lRy64OWEWko4ywd0xepV
5p06xCTIhC95D5tpddTins5IoAD8nR9Z0QUaEQ6GuQdOijzw/nQG4yNbPUtFFLGA
teI/ypwmtxXRLEcXrO2QjzsYI+ERbhh34jLLmXaO+q21xTQqt1E6egceHwKBgGtw
3I5pRvQ95evYkURVP2OzmqGvhgSIT/pAXty15deaI+jdkpLelfA05FJt/pjWaWmo
tpEu3MRK2Gw8iOTSyh4kvvsenJUfCj3nGe5mNmdeTnBaXoowm4wTW7sloHx/R1r5
sDw7EGPiQHjpHvh2CvPpr2y07QH5z7ACxggJEZ9PAoGBALdbNqiHJycRVizc1Qrn
A6sFLovEXsn9371w2l+SLrUeFlL73v0DKXWcRLz3RLcDGdq8gKMjIMCkdmX9lFPr
pK/+p6NMlLef8aSOxsDv4rxKNcSwcsfJ90tc4ex0JRN41zaAOcO7bOKUasNURgRS
Ki5GFciH4qixEtN7TxMi8lRF
-----END PRIVATE KEY-----

Note: the breaks are just for formatting and can be excluded, but the headers (begin and end) should be included with your key value!

Let's use the PrivateKeyDecoder of the OpenSSL package to decode the key and convert it into a consumerSecret

var privateKey = ""; // <- get your private key in here
var decoder = new OpenSSL.PrivateKeyDecoder.OpenSSLPrivateKeyDecoder();
var keyInfo = decoder.Decode(privateKey);
var consumerSecret = OpenSSL.PrivateKeyDecoder.RSAExtensions.ToXmlString(keyInfo, true);

Why do we need the extension method? Well, the keyInfo.ToXmlString(true) does not work in .NET Core (check this discussion from 2017) and will throw an error. Fortunately the OpenSSL package provides us with an implementation that we can use. Note: it will work in .NET Framework.

Make the HTTP Client behave

The Jira client uses a RestSharp HTTP client to connect to the API. We need to modify that HTTP client to include an OAuth authenticator that uses the consumerSecret:

var client = Atlassian.Jira.Jira.CreateRestClient(jiraUrl);
client.RestClient.RestSharpClient.Authenticator = 
    RestSharp.Authenticators.OAuth1Authenticator.ForProtectedResource(
        consumerKey,        // <- fill it
        consumerSecret,     // <- generated
        accessToken,        // <- fill it
        accessTokenSecret,  // <- fill it
        RestSharp.Authenticators.OAuth.OAuthSignatureMethod.RsaSha1
    );

This client will now sign all of our requests to Jira with the Private Key. This solution should work with both .NET Framework and .NET Core.

  1. Hollie says:

    Hi, thanks for the article – this is what we’re trying to do as well – and failing miserably! What is RSAExtensions? I get a “this doesn’t exist in the namespace” error :(

    1. Kees C. Bakker says:

      .NET Core does not contain an implementation of XML serialization, that’s why the Open SSL package wrote their own implementation of the ToXmlString. The namespace is OpenSSL.PrivateKeyDecoder (according to: https://github.com/StefH/OpenSSL-X509Certificate2-Provider/blob/master/src/OpenSSL.PrivateKeyDecoder/RSAExtensions.cs ). Are you sure you are also working in .NET Core?

      1. Hollie says:

        Agh no I missed that it had to be done in Core, I’ve been attempting it in a bog standard web application. Will give it a go in Core and see how far I get. Thanks!

        1. Kees C. Bakker says:

          It does not have to be done in Core. If you want to use .NET Framework, you don’t need the call to the extensions class, just do: keyInfo.ToXmlString(true) and it will work.

    2. Kees C. Bakker says:

      In .NET Framework the keyInfo.ToXmlString(true) works. I’ve clarified it in the article.

  2. Red Herring says:

    The article starts out by assuming that we already have three of these:
    Access Token
    Access Token Secret
    Consumer Key
    Consumer Secret

    Now Consumer Key is explained in the https://developer.atlassian.com/server/jira/platform/oauth/ article, and this article describes how to generate Consumer Secret (Although trying to call decoder.Decode throws a System.NotSupportedException, in .NET Core. I assume I can generate the Consumer Secret in .NET Framework and then use the result in .NET Core).

    That leaves the Access Token and Access Token Secret, which I can’t find any explanation for anywhere. The previously mentioned Atlassian link generates these, using Java code, but doesn’t explain how to do the same, when not writing Java.

    Does anyone have a fully working sample code or can anyone explain how to generate the Access Token and Access Token Secret values?

    1. Victoria says:

      Red,
      Please let me know if you ever figured out where to get Access Token and Access Token Secret,
      thank you in advance!
      Victoria

      1. Red Herring says:

        Unfortunately I did not. Because I couldn’t find a working example anywhere, I ended up going the username/password path instead. It’s unfortunate that better security comes with such complexity, as the lack of functioning examples means I had to choose the lesser secure path in order to get things working.

        Unfortunately the project I needed this for has now been closed, which means even if I found a working example, I won’t be able to use it.

  3. CJWarnke says:

    Sorry to Resurrect this, but still the best (only) example of c# connecting via Oauth to Jira that I can find. A couple questions/challenges…

    First, i am trying to connect to Jira Cloud. Will this work with Cloud?

    I used the page https://developer.atlassian.com/cloud/jira/platform/jira-rest-api-oauth-authentication/ as the starting point, and was able to connect via the Java client fine. I was able to retrieve the issue we were using as a test.

    However, when using the code above I continue to get a “Token Rejected” error.

    I believe one of my values are incorrect, but the terminology seems different every site I find and not sure what is what.

    I used the following values:
    Consumer Key: the value from the public Key field from the application link creation process in the example at the Atlassian site
    Consumer Secret: created in Code
    Access token: The Access_Token value from the Config.Properties as described in that atlassian site
    Secret: The Secret from the same Config.Properties file as above

    It seems to go through your code, but as soon as I try the following it generates the error:
    var iss = client.Issues.Queryable.Where(i => i.Key == “ISSUE-1”).FirstOrDefault();

    So the value included is the Access token. Do i need to first somehow grab a request token prior to trying to make any queries to the Jira service?

  4. CJWarnke says:

    Sorry to Resurrect this, but still the best (only) example of c# connecting via Oauth to Jira that I can find. A couple questions/challenges…

    First, i am trying to connect to Jira Cloud. Will this work with Cloud?

    I used the page https://developer.atlassian.com/cloud/jira/platform/jira-rest-api-oauth-authentication/ as the starting point, and was able to connect via the Java client fine. I was able to retrieve the issue we were using as a test.

    However, when using the code above I continue to get a “Token Rejected” error.

    I believe one of my values are incorrect, but the terminology seems different every site I find and not sure what is what.

    I used the following values:
    Consumer Key: the value from the public Key field from the application link creation process in the example at the Atlassian site
    Consumer Secret: created in Code
    Access token: The Access_Token value from the Config.Properties as described in that atlassian site
    Secret: The Secret from the same Config.Properties file as above

    It seems to go through your code, but as soon as I try the following it generates the error:
    var iss = client.Issues.Queryable.Where(i => i.Key == “ISSUE-1”).FirstOrDefault();

    Any ideas what i may be doing wrong?

expand_less