It's not the same without you

Join the community to find out what other Atlassian users are discussing, debating and creating.

Atlassian Community Hero Image Collage

Is there any JIRA OAuth implementation example in .NET?

Hi everybody,

We are working on a Jira extension platform in .NET, and we want to provide user access to some secure datas (like download attachments).

So we are trying to implement JIRA OAuth Authentication in .NET (with Application Links and Generic application). But we use RestSharp as REST Service, and they didn't implement the RSA-SHA1 crypto which is used for JIRA OAuth.

I'm looking for some examples or advices about this.

Thanks for your help.

12 answers

1 accepted

1 vote
Answer accepted

I have a bad habbit of ignoring what I find incorrect. I just did not save into my memory what 2 legged and 3 legged means. The term 2 legged caused a lot of confusion for me.

The thing that you are asking for is signed calls. You can sign your requests with a combinations of access token and consumer key and consumer secrect key. If you omit the access token part then it still may work. For JIRA's case, it is supported if you configure your application correctly.

So what I mean by omitting the access token part? When making a request using an RestSharp.Http you just need to call ApplyAccessTokenToHeader method supplying String.Empty for access_token and access_token_secret parameters. No need to have an access token, then no need to go through the process of obtaining access token, then no need to involving an end user to grant access.

Now the code that you need is as follows:

(The JIRAOAuth1aProvider is exactly the same before)

public class JIRAConnectController : Controller
{
    public ActionResult AppInfo()
    {
        var JiraApplicationCredentials = new ApplicationCredentials
        {
            ConsumerKey = "dpf43f3p2l4k3l03",
            ConsumerSecret = @"<RSAKeyValue><Modulus>sRWuzU5HG5Q+FKS81mmY8wWVl2dWy+F+9rK0IysHhPmRSNU2tHRkJnROy6Z0K9lVkIthpOAnVz5DeW406zM5KjHUCZSuY/VmzzjksY0Nw4LqaE18ViFCP2XJ1M3peNpi6oLVjokmXXQpSsf72uSiYQ6YQVYDuZLWlPkR1ttOhi8=</Modulus><Exponent>AQAB</Exponent><P>4RXYdxrnAoeu5uKMWm9TM2NM9Pjl6coY5awhqri+UXn0JSPbW5AK9tBkcxo0k9FIKzE4IjgLof3OxiWRxgv4sQ==</P><Q>yWgaH21XynI+fAf5+cJBlwfdDkAi2h1FTzFg2TzpyHfnz0aLKq7YqwbRrVY0SKB8scbA5wuS0JJjwTCWoGgk3w==</Q><DP>UwLlhHetfudff9NJhAjOnIMg3K/2yxQ3vmyPNhFQT2g0vIXO3qPvBzuv8CtX/LQQc62BUE1vLN0YJ0evzWSY4Q==</DP><DQ>gnWXLjIygqT7ynWUO27T85OZYcizipsZMRHoDO9C86KPmhi0voIhQK8bFG9ZUI38F1+Jxl8dy1J3oUTq3J54+w==</DQ><InverseQ>jj2GNTEVk+aRMkcPIQYrxa971A/KH3cYFQ05tjsFnZeYnDcp4tWSkK5LOcW+PC/6dUDY52dDe6Lo1UrWxNfQXA==</InverseQ><D>EdbHVzNNgn+6iUXXKBpXKBQl76yfV8aNjbiO4QQSq9igRCAGdMNM23rX/Tf1d/1a20bPPVa/X9CL542skw0D8ws/Ju3PVerunQl7PCNsOuCnINVGVmLTJuBQzjFY+GyKdxbtwIgaXXFMh3whVglxLCrrpJzmtVTZAk3GxFpNLkE=</D></RSAKeyValue>",
        };
        var JiraOAuth1AProvider = new JIRAOAuth1aProvider("http://localhost:8411");

        var provider = JiraOAuth1AProvider;
        var jiraCredentials = JiraApplicationCredentials;
        var accessToken = "";
        var accessTokenSecret = "";

        const string issueId = "MIO-1";
        var http = new Http {Url = new Uri("http://localhost:8411/rest/api/2/issue/"+issueId)}; 
        // RestSharp.RestRequest is not supported yet. So the following ApplyAccessToken is only available for RestSharp.Http
        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;
    }
}

In the AppInfo method, it demonstrates a sample of how to make a signed request without having an access token.

If you get the following message, then this means that you JIRA Application Link is not configured to allow so-called two legged authentication:

HTTP Status 401 -


type Status report

message

description This request requires HTTP authentication ().


Apache Tomcat/7.0.29

In that case the following article would be helpful: http://blog.michael.kuron-germany.de/2013/04/two-legged-oauth-between-php-and-jira/

Hi

Your post was very helpfull during implementation of the oauth request to the jira.

Nevertheless I still have some strange problems. When I want to call OAuth1aProcess.GetAuthorizationUri method , user is allways ask for the application permission to resources.

I expect , that if the user will be logged into the jira there will be no messages and he will get new access tokens. Instead of that jira is asking for permission each time I call this method.

You may ask why I execute the OAuth1aProcess.GetAuthorizationUri. I just want to find out if the user is loged into the Jira so that I can obtain a user name and set authorization cookie in my application for that specific user. I`m not quite sure if this aporoach is proper, Maybe I have to set up authorization mechanizm in my apps and just store the jira access token for each user in database( if user has one) ?

Please advise

Hi,

I have the exact same problems. I've posted a separate question here. I'd appreciate any answer on why this behavior is like this.

Cheers,

Tudor

I have impelemented a library called DotNetAuth for using OAuth in .Net that I hope would be useful for you. It has some support for RestSharp as well.

To include in your project you need to nuget it: The nuget package for .Net 3.5 is not working so in that case you need to get the codes and compile it.

install-package DotNetAuth

JIRA is using OAuth 1.0a, so the DotNetAuth.OAuth1a namespace has tools that you need.

The main type you can need to use is DotNetAuth.OAuth1a.OAuth1aProcess

You need to call OAuth1aProcess.GetAuthorizationUri to get a uri from JIRA and then redirect user to that uri. Once user confirms your application then you need to process user's response with OAuth1aProcess.ProcessUserResponse

For the above two methods the first argument is of type OAuth1aProviderDefinition, this is the place that DotNetAuth can support different OAuth providers. However I have not checked if the default implementation of this class works for JIRA or not. Hopefully it works. If it works then you might use the following code. Although you have to implement the Sign method for RSA-SHA1

public class JIRAOAuth1a : OAuth1aProviderDefinition
    {
        public JIRAOAuth1a(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)
       {
           // TODO : Sign using RSA-SHA1
       }
    }

For RestSharp you need to use DotNetAuth.OAuth1a.RestSharp.SetOAuth1aAuthorization.

Please let me know if did not work and kindly issue a bug in DotNetAuth's Issue Tracking.

Also please let me know if the explanation above is not enough or you have any question.

This is an working example including a sample of how to use RestSharp.

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

        public JIRAConnectController()
        {
            JiraApplicationCredentials = new ApplicationCredentials
                {
                    ConsumerKey = "dpf43f3p2l4k3l03",
                    ConsumerSecret = @"<RSAKeyValue><Modulus>sRWuzU5HG5Q+FKS81mmY8wWVl2dWy+F+9rK0IysHhPmRSNU2tHRkJnROy6Z0K9lVkIthpOAnVz5DeW406zM5KjHUCZSuY/VmzzjksY0Nw4LqaE18ViFCP2XJ1M3peNpi6oLVjokmXXQpSsf72uSiYQ6YQVYDuZLWlPkR1ttOhi8=</Modulus><Exponent>AQAB</Exponent><P>4RXYdxrnAoeu5uKMWm9TM2NM9Pjl6coY5awhqri+UXn0JSPbW5AK9tBkcxo0k9FIKzE4IjgLof3OxiWRxgv4sQ==</P><Q>yWgaH21XynI+fAf5+cJBlwfdDkAi2h1FTzFg2TzpyHfnz0aLKq7YqwbRrVY0SKB8scbA5wuS0JJjwTCWoGgk3w==</Q><DP>UwLlhHetfudff9NJhAjOnIMg3K/2yxQ3vmyPNhFQT2g0vIXO3qPvBzuv8CtX/LQQc62BUE1vLN0YJ0evzWSY4Q==</DP><DQ>gnWXLjIygqT7ynWUO27T85OZYcizipsZMRHoDO9C86KPmhi0voIhQK8bFG9ZUI38F1+Jxl8dy1J3oUTq3J54+w==</DQ><InverseQ>jj2GNTEVk+aRMkcPIQYrxa971A/KH3cYFQ05tjsFnZeYnDcp4tWSkK5LOcW+PC/6dUDY52dDe6Lo1UrWxNfQXA==</InverseQ><D>EdbHVzNNgn+6iUXXKBpXKBQl76yfV8aNjbiO4QQSq9igRCAGdMNM23rX/Tf1d/1a20bPPVa/X9CL542skw0D8ws/Ju3PVerunQl7PCNsOuCnINVGVmLTJuBQzjFY+GyKdxbtwIgaXXFMh3whVglxLCrrpJzmtVTZAk3GxFpNLkE=</D></RSAKeyValue>",
                };
            JiraOAuth1AProvider = new JIRAOAuth1aProvider("http://localhost:8411");
            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/AppInfo");
        }

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

            const string issueId = "MIO-1";
            var http = new Http {Url = new Uri("http://localhost:8411/rest/api/2/issue/"+issueId)}; 
            // RestSharp.RestRequest is not supported yet. So the following ApplyAccessToken is only available for RestSharp.Http
            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;
        }
    }

Sam, do you have any idea about my last post?

Thank you Sam!

I'm gonna take a look as soon as possible, and keep you in touch; i'm pretty sure it's an excellent solution.

Sam,

This is an awesome and working example, thank u so much!

But - sorry there is a "but - in our case we only need/we can only use 2-legged (because our customer can't have access to Jira plateform; so they are not be able to provide application access from Jira callback page); I'm new in OAuth domain, so I couldn't clearly explain enough this part.

How can I change your code (using OAuth2ProviderDefinition if I understand) with Jira?

Thank you so much, this is exactly what i was looking for. I made some misunderstanding about the difference between Authentication and Authenticated request.

Now, we can access secure Jira data (like download attachment) without OAuth authentication.

Glad to hear that. Please let me know if you had any other problem.

Hi,

thank you for the library. Just to mention, the current request signing doesn't play well with requests containing jql, for example:

/rest/api/2/search?jql=issuetype=epic

this can be easily fixed by changing ParameterSet.FromResponseBody:

public static ParameterSet FromResponseBody(string responseBody)

{

var arguments = responseBody.Split(new[] { '&' }, StringSplitOptions.RemoveEmptyEntries);

var result = new ParameterSet();

foreach (var i in arguments)

{

var index = i.IndexOf("=", StringComparison.CurrentCulture);

var left = i.Substring(0, index);

var right = i.Substring(index + 1, i.Length - (index + 1));

result.Add(new Parameter(left, right));

}

return result;

}

Best,

Robert

Hi Sam and Thank you for your useful answer. Just one question, you have used classes like Http and ApplicationCredentials, I don't know where they are come from, are they from RestSharp?

Hi Sam, is there any way that I can authenticate to JIRA using this solution and if everything is ok I could redirect user to JIRA dashboard?

We have a slightly different use case, but it might people that are looking here. We use OAuth with a private key in a backend service - in this case Jira requests cannot be authenticated by a user with a browser. By using the private key we have signed requests for our service.

We use the following NuGet packages:

  • Atlassian.SDK - a .NET implementation of a Jira API Client
  • OpenSSL.PrivateKeyDecoder - decoder for private keys

We use the following setup to get a consumer secret and signing the requests:

  1. Get the access token, access token secret, consumer key and private key
  2. Decode the private key into a consumer secret
  3. Add a RestSharp authenticator to the Jira Client (using the consumer secret) to authenticate requests to Jira.

More info & code can be found here: https://keestalkstech.com/2019/06/connect-to-jira-with-a-private-key-using-net/

Suggest an answer

Log in or Sign up to answer
Community showcase
Posted in United States

Live! - Austin ACE -- Summer BASH 2019

Hey Austin! Come join us for our 2019 Atlassian Community Summer BASH as we transition ourselves into party status! Bring an attachment, log a great time, and collaborate with new friends as we celeb...

12 views 0 0
View post

Community Events

Connect with like-minded Atlassian users at free events near you!

Find an event

Connect with like-minded Atlassian users at free events near you!

Unfortunately there are no Community Events near you at the moment.

Host an event

You're one step closer to meeting fellow Atlassian users at your local event. Learn more about Community Events

Events near you