Jira OAuth authentication using DotNetAuth

In this post, we will try to connect to Jira with oAuth authentication in DotNet, I did some research, and mainly followed the instructions on Is there any JIRA OAuth implementation example in .NET and JIRA REST API Example – OAuth authentication

Generate self-signed certificate

OpenSSL is used for generating the certificate,

openssl genrsa -des3 -out private.pem 2048

openssl rsa -in private.pem -outform PEM -pubout -out public.pem

openssl pkcs8 -topk8 -nocrypt -in private.pem -out private.pcks8

public.pem will be required in public key field of your application in Jira application links

private.pcks8 will be required in the client (variable consumerSecret)

Configuring Jira

First we need to go to Jira administration – application to register a new application link. Detailed step is the same as the step 1 of the JIRA REST API Example – OAuth authentication.

Using PKCS8 format private key in C#

Jira currently is using RSA-SHA1 as the aAuth signing type. Class RSACryptoServiceProvider will be used for the sign, but it only supports xml format key. So we need to convert the PKCS8 format to supported XML format, for which opensslkey is used here.(opensslkey can be downloaded from here)

var consumerSecret = jiraPrivateKey.Replace("-----BEGIN PRIVATE KEY-----", "").Replace("-----END PRIVATE KEY-----", "").Replace("\r\n", "").Replace("\n", "");

RSACryptoServiceProvider keyInfo = opensslkey.DecodePrivateKeyInfo(Convert.FromBase64String(consumerSecret));

Sample Code

Below is the code, I put everything together. After the successful authentication, we should receive accessToken and accessTokenSecret; and send oAuth authenticated request with those information.

public class JIRAConnectController : Controller
{
        private ApplicationCredentials JiraApplicationCredentials;
        private JIRAOAuth1aProvider JiraOAuth1AProvider;
        private OAuth10aStateManager OAuth10AStateManager;

        public JiraConnectController()
        {
            var consumerSecret = "MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDFkPMZQaTqsSXI+bSI65rSVaDzic6WFA3WCZMVMi7lYXJAUdkXo4DgdfvEBO21Bno3bXIoxqS411G8S53I39yhSp7z2vcB76uQQifi0LEaklZfbTnFUXcKCyfwgKPp0tQVA+JZei6hnscbSw8qEItdc69ReZ6SK+3LHhvFUUP1nLhJDsgdPHRXSllgZzqvWAXQupGYZVANpBJuK+KAfiaVXCgA71N9xx/5XTSFi5K+e1T4HVnKAzDasAUt7Mmad+1PE+56Gpa73FLk1Ww+xaAEvss6LehjyWHM5iNswoNYzrNS2k6ZYkDnZxUlbrPDELETbz/n3YgBHGUlyrXi2PBjAgMBAAECggEAAtMctqq6meRofuQbEa4Uq5cv0uuQeZLV086VPMNX6k2nXYYODYl36T2mmNndMC5khvBYpn6Ykk/5yjBmlB2nQOMZPLFPwMZVdJ2Nhm+naJLZC0o7fje49PrN2mFsdoZeI+LHVLIrgoILpLdBAz/zTiW+RvLvMnXQU4wdp4eO6i8J/Jwh0AY8rWsAGkk1mdZDwklPZZiwR3z+DDsDwPxFs8z6cE5rWJd2c/fhAQrHwOXyrQPsGyLHTOqS3BkjtEZrKRUlfdgV76VlThwrE5pAWuO0GPyfK/XCklwcNS1a5XxCOq3uUogWRhCsqUX6pYfAVS6xzX56MGDndQVlp7U5uQKBgQDyTDwhsNTWlmr++FyYrc6liSF9NEMBNDubrfLJH1kaOp590bE8fu3BG0UlkVcueUr05e33Kx1DMSFW72lR4dht1jruWsbFp6LlT3SUtyW2kcSet3fC8gySs2r6NncsZ2XFPoxTkalKpQ1atGoBe3XIKeT8RDZtgoLztQy7/7yANQKBgQDQvSHEKS5SttoFFf4YkUh2QmNX5m7XaDlTLB/3xjnlz8NWOweK1aVysb4t2Tct/SR4ZZ/qZDBlaaj4X9h9nlxxIMoXEyX6Ilc4tyCWBXxn6HFMSa/Rrq662Vzz228cPvW2XGOQWdj7IqwKO9cXgJkI5W84YtMtYrTPLDSjhfpxNwKBgGVCoPq/iSOpN0wZhbE1KiCaP8mwlrQhHSxBtS6CkF1a1DPm97g9n6VNfUdnB1Vf0YipsxrSBOe416MaaRyUUzwMBRLqExo1pelJnIIuTG+RWeeu6zkoqUKCAxpQuttu1uRo8IJYZLTSZ9NZhNfbveyKPa2D4G9B1PJ+3rSO+ztlAoGAZNRHQEMILkpHLBfAgsuC7iUJacdUmVauAiAZXQ1yoDDo0Xl4HjcvUSTMkccQIXXbLREh2w4EVqhgR4G8yIk7bCYDmHvWZ2o5KZtD8VO7EVI1kD0z4Zx4qKcggGbp2AINnMYqDetopX7NDbB0KNUklyiEvf72tUCtyDk5QBgSrqcCgYEAnlg3ByRd/qTFz/darZi9ehT68Cq0CS7/B9YvfnF7YKTAv6J2Hd/i9jGKcc27x6IMi0vf7zrqCyTMq56omiLdu941oWfsOnwffWRBInvrUWTj6yGHOYUtg2z4xESUoFYDeWwe/vX6TugL3oXSX3Sy3KWGlJhn/OmsN2fgajHRip0=";

            var secretArray = Convert.FromBase64String(consumerSecret);
            var key = opensslkey.DecodePrivateKeyInfo(secretArray);

            var baseUrl = "http://localhost:8080";
            JiraApplicationCredentials = new ApplicationCredentials
            {
                ConsumerKey = "hardcoded-consumer",
                ConsumerSecret = key.ToXmlString(true)
            };

            JiraOAuth1AProvider = new JIRAOAuth1aProvider(baseUrl);
            OAuth10AStateManager = new OAuth10aStateManager((k, v) => Session[k] = v, k => (string)Session[k]);

        }

        [HttpGet]
        public ActionResult Initate()
        {
            var callback = Request.Url.GetLeftPart(UriPartial.Authority) + "/JiraConnect/Callback";
            var authorizationUri = OAuth1aProcess.GetAuthorizationUri(JiraOAuth1AProvider, JiraApplicationCredentials,
                                                                      callback, OAuth10AStateManager);
            authorizationUri.Wait();
            return new RedirectResult(authorizationUri.Result.AbsoluteUri);
        }

        [HttpGet]
        public ActionResult Callback()
        {
            var processUserResponse = OAuth1aProcess.ProcessUserResponse(JiraOAuth1AProvider, JiraApplicationCredentials,
                                                                         Request.Url, OAuth10AStateManager);
            processUserResponse.Wait();
            Session["access_token"] = processUserResponse.Result.AllParameters["oauth_token"];
            Session["accessTokenSecret"] = processUserResponse.Result.AllParameters["oauth_token_secret"];
            return Redirect("/JiraConnect/IssueInfo");
        }

        public ActionResult IssueInfo()
        {
            var provider = JiraOAuth1AProvider;
            var jiraCredentials = JiraApplicationCredentials;
            var accessToken = Session["access_token"] as string;
            var accessTokenSecret = Session["accessTokenSecret"] as string;

            const string issueId = "MW-103";
            var http = new Http { Url = new Uri("http://localhost:8080/rest/api/2/issue/" + issueId) };

            http.ApplyAccessTokenToHeader(provider, jiraCredentials, accessToken, accessTokenSecret, "GET");
            var response = http.Get();
            return Content(response.Content);
        }
}
 
public class JIRAOAuth1aProvider : OAuth1aProviderDefinition
{
    public JIRAOAuth1aProvider(string JIRA_BASE_URL)
    {
        RequestTokenEndopointUri = JIRA_BASE_URL + "/plugins/servlet/oauth/request-token";
        AuthorizeEndpointUri = JIRA_BASE_URL + "/plugins/servlet/oauth/authorize";
        AuthenticateEndpointUri = JIRA_BASE_URL + "/plugins/servlet/oauth/authorize";
        AccessTokenEndpointUri = JIRA_BASE_URL + "/plugins/servlet/oauth/access-token";
    }
    public override string GetSignatureMethod()
    {
        return "RSA-SHA1";
    }
    public override string Sign(string stringToSign, string signingKey)
    {           
        var privateKey = new RSACryptoServiceProvider();    
        privateKey.FromXmlString(signingKey);
 
        var shaHashObject = new SHA1Managed();
        var data = Encoding.ASCII.GetBytes(stringToSign);
        var hash = shaHashObject.ComputeHash(data);
                 
        var signedValue = privateKey.SignHash(hash, CryptoConfig.MapNameToOID("SHA1"));
 
        var result = Convert.ToBase64String(signedValue, Base64FormattingOptions.None);
        return result;
    }
}