- Basic Authentication is a simple but not very safe approach. Credentials are sent in the header on every request and encoding to Base64 is not a proper protection in this case; HTTPS connection is required.
- OAuth authentication - looks a bit complex and requires additional configuration at the JIRA server that is not always possible.
- Cookie-based Authentication - this approach seems to be the most convinient one: credentials are checked once, then the authentication cookie only is sent on subsequent requests.
However, trying to use the cookie-based authentication I encountered an issue. The approach described in the documentation worked partially: I was able to create a new session and get the response containing the session cookie but all subsequent requests using this session cookie were rejected as unauthorized. Spending some time investigating this I found the cause of the issue: JSESSIONID is not the only cookie which is required.
I am testing on a demo account which JIRA provides for trials on Atlassian Cloud. In my case for the proper authentication are also required:
- atlassian.xsrf.token - Atlassian Cloud service's Server ID, a securely-generated random string (i.e. token) and a flag that indicates whether or not the user was logged in at the time the token was generated.
- studio.crowd.tokenkey - authentication cookie which is used for single sign-on (SSO) between Atlassian Cloud applications. Like the JSESSIONID cookie, this cookie also contains a random string and the cookie expires at the end of every session or when the browser is closed.
Below is the example of usage of the cookie-based authentication to retrieve data from JIRA API.
var baseUrl = //URL to your JIRA server, e.g. https://<YOUR ACCOUNT>.atlassian.net var userName = ... var password = ... var dataProvider = new JiraApi(); IDictionary<string, string> cookies; if (dataProvider.TryLogin(baseUrl, userName, password, out cookies)) { //just for example: retrieves all issues of the specified type dataProvider.SearchForIssues(baseUrl, cookies, "type=Story"); dataProvider.Logout(baseUrl, cookies); }
Actually the logout is not a required step; the cookies can be saved somewhere and used until they expired (30 days).
I used RestSharp to keep the below code simple, however the approach should also work with the plain HttpWebRequest. Also for simplicity of the example I used client.Execute instead of more convinient client.Execute<T> which allows to deserialize the response content from JSON to entities.
public class JiraApi { public bool TryLogin(string baseUrl, string userName, string password, out IDictionary<string, string> cookies) { var client = new RestClient(baseUrl); var request = new RestRequest("/rest/auth/1/session", Method.POST); request.RequestFormat = DataFormat.Json; request.AddBody(new { username = userName, password = password }); var response = client.Execute(request); if (response.StatusCode == HttpStatusCode.OK) { cookies = response.Cookies.ToDictionary(x => x.Name, x => x.Value); return true; } else { //handle the cause of the failure based on information provided //by response.StatusCode and response.Content //... cookies = null; return false; } } public void SearchForIssues(string baseUrl, IDictionary<string, string> cookies, string jql) { var client = new RestClient(baseUrl); var request = new RestRequest("/rest/api/2/search", Method.GET); request.RequestFormat = DataFormat.Json; request.AddBody(new { jql = jql }); foreach (var cookie in cookies) request.AddCookie(cookie.Key, cookie.Value); var response = client.Execute(request); if (response.StatusCode == HttpStatusCode.OK) { var content = response.Content; //handle results here //... } else { //handle the cause of the failure based on information provided //by response.StatusCode and response.Content //... } } public void Logout(string baseUrl, IDictionary<string, string> cookies) { var client = new RestClient(baseUrl); var request = new RestRequest("/rest/auth/1/session", Method.DELETE); foreach (var cookie in cookies) request.AddCookie(cookie.Key, cookie.Value); var response = client.Execute(request); if (response.StatusCode == HttpStatusCode.NoContent) { //handle results here //... } else { //handle the cause of the failure based on information provided //by response.StatusCode and response.Content //... } }
Note that the successful deletion of the session returns NoContent status code instead of OK.
Do I need the paid version to work with the API. Is it possible to do it for free?
ReplyDeleteI tested on a free trial Atlassian Cloud account.
Delete