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.
Community moderators have prevented the ability to post new answers.
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:
type Status report
message
description This request requires HTTP authentication ().
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
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
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
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
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:
We use the following setup to get a consumer secret and signing the requests:
More info & code can be found here: https://keestalkstech.com/2019/06/connect-to-jira-with-a-private-key-using-net/
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
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?
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
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?
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
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
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
Glad to hear that. Please let me know if you had any other problem.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
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.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
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?
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
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; } }
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
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.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
Community moderators have prevented the ability to post new answers.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.