- 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