Rija Development: Using OAuth Tokens to Make REST API Calls

In my last Rija development post I talked about getting an OAuth access token for Jira so other people can use Rija. This post covers using the access token to make calls to Jira’s REST API. You must perform the following tasks to make API calls with an OAuth token:

  • Get the user’s Jira cloud ID
  • Get the URL for Jira’s REST APIs
  • Add the access token to the authorization header

Get User’s Cloud ID

Each Jira site has a unique cloud ID. You can’t make calls to Jira’s REST API with an OAuth token without the cloud ID for the user’s Jira site. The first thing you must do is get the cloud ID. To get the cloud ID, make a URL request to the following URL:

https://api.atlassian.com/oauth/token/accessible-resources

Add the access token string to the authorization header. Run a URL session with the URL request. The following code retrieves a Jira cloud ID:

func retrieveCloudID() async -> String {
  // Build the URL    
  var components = URLComponents()
  components.scheme = "https"
  components.host = "api.atlassian.com"
  components.path = "/oauth/token/accessible-resources"
  var request = URLRequest(url: components.url!)
            
  guard let accessToken = getAccessToken() else {
    return ""
  }
            
  // Add the access token to the authorization header
  let authString = String("Bearer \(accessToken)")
  request.addValue(authString, forHTTPHeaderField: "Authorization")
  request.addValue("application/json", forHTTPHeaderField: "Content-Type")
            
  // Retrieve the cloud ID
  do {
    let (data, error) = try await URLSession.shared.data(
      for: request, delegate: nil)
    let decoder = JSONDecoder()
    let cloudDictionary = try decoder.decode([JiraCloudID].self, 
      from: data)
    return cloudDictionary.first?.id ?? ""
  } catch {
    print(error)
    return ""
  }
}

JiraCloudID is a struct I created with the properties id, name, and url. The id property contains the cloud ID.

Get Jira’s REST API URL

Jira has a different URL for making API calls with an OAuth access token than they have for making calls with a personal access token. The base URL is the following:

https://api.atlassian.com/ex/jira

Append the cloud ID and the path to the REST API call you’re making.

Add the Access Token to the Authorization Header

I showed the code to add the access token in the code to retrieve the cloud ID. But let’s show it again.

guard let accessToken = getAccessToken() else {
  return ""
}
            
let authString = String("Bearer \(accessToken)")
request.addValue(authString, forHTTPHeaderField: "Authorization")

Jira’s REST API requires the string Bearer followed by the access token string. Add the string to the URL request’s authorization header.

If you read Apple’s documentation for the URLRequest class, they tell you not to set the Authorization header manually because it’s a reserved header. But Apple provides no alternative to manually setting the header manually so I have to set the Authorization header manually.

An Example API Call

The following code fetches someone’s Jira projects:

func fetchProjects() async throws -> JiraProjectList {
  let cloudID = await getCloudID()
            
  // Build the URL 
  var components = URLComponents()
  components.scheme = "https"
  components.host = "api.atlassian.com"
  components.path = "/ex/jira/" + cloudID 
    + "/rest/api/3/project/search"
        
  // Sort projects alphabetically by default
  components.queryItems = [
    URLQueryItem(name: "orderBy", value: "name")
  ]
  var request = URLRequest(url: components.url!)
            
  // JiraProjectList contains an array of projects.   
  guard let accessToken = getAccessToken() else {
    return JiraProjectList()
  }
            
  // Add access token to authorization.
  let authString = String("Bearer \(accessToken)")
  request.addValue(authString, forHTTPHeaderField: "Authorization")
  request.addValue("application/json", forHTTPHeaderField: "Content-Type")
            
  let (data, error) = try await URLSession.shared.data(for: 
    request, delegate: nil)
  let decoder = JSONDecoder()
  return try decoder.decode(JiraProjectList.self, from: data)
}

I start by building the URL. I want to sort the projects alphabetically so I supply a URL query item. Then I add the access token to the authorization header. Finally I run the URL request.

Jira’s REST API returns the projects in a dictionary instead of an array. I have to create a JiraProjectList struct to decode the JSON properly. All the struct contains is an array of projects.

About Rija

Rija is a Jira issue tracker under development for Mac (and possibly iOS). The following article provides more details on Rija:

Rija Development: Intro

Get the Swift Dev Journal Newsletter

Subscribe and get exclusive articles, a free guide on moving from tutorials to making your first app, notices of sales on books, and anything I decide to add in the future.

    We won't send you spam. Unsubscribe at any time.