oAuth is a framework that allows applications and services to request and provide access to one another without requiring they exchange a user’s username and password(basic authentication). It is a more secure mechanism for authorizing API access because user credentials don’t need to be passed in the API call in clear text. A simple way to think of it is like providing a service access alarm code to your house. It is not the master code you use, you can set an expiration date, and you can cancel access at any time.
I had been trying to implement oAuth authentication within the Apps Scripts of my various Jira tools that call the Jira REST API for a while with little success. The process didn’t seem that difficult, but there was no place I was able to find that documented the entire process start to finish for an Apps Scripts implementation. Disparate sources all over the Web covered portions of the process, but they varied in detail and at times had conflicting information that just added to my confusion and frustration.
Through a lot of digging, and trial and error, I was finally able to piece together everything that needs to be done and successfully implemented oAuth authentication with the Jira REST API. I thought I would share what I had learned so others wanting to do the same don’t experience the same frustration for what should be a relatively easy task.
I created an example Google Apps Script that demonstrates how to make a Jira REST API call using oAuth 1.0. The example uses the Google Apps Script oAuth 1.0 library which does most of the heavy lifting. Here is a diagram that explains the script’s fundamental authentication flow called the oAuth dance.
The high level steps to try my example are as follows:
- Obtain or generate your public/private key pair files
- Copy my Apps Script example and set the global variables
- Create a new Jira Application Link
- Run the updatePrivateKey() function
- Run the callAPI() function, authorize the API call, and then callAPI() again
First, a few background notes:
- I was using a Windows 10 laptop and running my terminal sessions as admin when I generated my keys
- The private key file was saved to my Google MyDrive. The example Google Script cannot read from a Google Shared Drive
- I used a Jira Cloud Software instance. There may be differences in configuration of Application Links for other types of Jira platforms such as Jira Server(on-prem)
- To create the Jira Application Link you will need to have admin permissions on your Jira instance
- This implementation uses oAuth 1.0A. That is the only version that is supported by the Jira REST API as of this writing
- If you are doing this from w/in an environment that uses SSO the callAPI() function may or may not work. It will depend on how your SSO firewalls have been configured for the Jira REST API. You should check with your network admin if you have trouble getting through
1 Obtain or generate Your Public/Private Key Pair Files
oAuth 1.0A uses RSA-SHA1 public/private key pairs for signature generation and verification when making calls. The details of the cryptography are well beyond the scope of this post. If you are implementing this within a company you should check with your InfoSec group about any specific requirements they have regarding cryptography key creation and management. They may want to provide keys for you. I have provided basic instructions on how to create keys, but you should only consider this a simple example.
If you don’t already have an application for generating RSA public/private key pairs you will need to install one. The app must be able to generate the keys in PCKS#8 format. There are a number of apps available. I used Win64 OpenSSL-Win64(non-light version). You can download it for free from several locations. I used Shinning Light Productions’ offering.
If you install it you should also add the path of the OpenSSL-Win64/bin folder to your PATH environment variable if you want the CreateKeyPair.bat file discussed in the next step to work as is.
You will need to create an RSA-SHA1 public/private key pair. I have created a batch file called CreatKeyPair.bat will execute the necessary OpenSSL commands for generating the keys. The batch file does the following:
- Creates a 2048 bit private key that expires in 365 days
- Creates an X509 certificate used for generating the public key
- Extracts the private key in PKCS#8 format from the private key .pem file
- Extracts the public key from the certificate
The batch file code is below
openssl genrsa -out %2.pem 2048openssl req -newkey rsa:2048 -x509 -key %2.pem -out %1.cer -days 365openssl pkcs8 -topk8 -nocrypt -in %2.pem -out %2.pcks8openssl x509 -pubkey -noout -in %1.cer > %1.pem
Copy the batch to the folder in which you wish to generate your keys and then execute the command as follows:
createkeypair <PublicKeyFilename> <PrivateKeyFilename>
Your filenames should not have extensions. When the code executes you will be prompted for several pieces of information:
- Org name
- Org unit Name
- Your name
- Email address to associate with the key
Your execution should look similar to the image below
The batch file will generate 4 files only two of which you need: <PrivateKeyFileName>.pcks8 and <PublicKeyFileName>.pem
You will use the files in later steps
2 Create Your Google Apps Script
I have created a Google Apps Script that implements the necessary code to make a simple call to the Jira REST API. The majority of this code came from the Jira example in the Google apps-script-oauth1 library (see below), but I enhanced it to make dealing with the private key easier. More on this in a bit. You can either make a local copy of that script or you can create an empty script and write your own implementation using mine as an example.
If you create your own script you will need link the Google apps-script-oauth1 library to the script. To link the library, go to the code section of the script and click on add button in the Libraries bar. This will bring up a dialog box. Enter the following script id:
Click on the Look up button and choose version 18. Leave the Identifier as OAuth1.
Technically, this library has been deprecated given oAuth 2.0 is what mos people are implementing now, but since Jira still uses oAuth 1.0 we don’t have an option unless we want to do everything from scratch…not!.
If you are using my code then you need to set the values of 4 global variables at the top of the script:
- SITE — This is the URL for your Jira implementation
- CONSUMER_KEY — This will be the key the API call uses to find the Application Link we will have created in Jira. This key can be anything you want. Just make sure whatever name you put here is the same name you enter in the Consumer Key field when you create the Application Link
- PRIVATE_KEY_FOLDER_NAME — The name of the Google folder where your private key is located. This cannot be a Google Shared drive. The Apps Scripts DriveApp class used in the script to handle file operations does not support Google Shared Drives
- PRIVATE_KEY_FILENAME — The file name of your private key. Same rules apply here with shared drives as above
Here is an example of the variables:
Once you have set the values for the global variables the script is ready to go.
3Create New Jira Application Link
Next we need to create an Application Link in Jira. This was one of the areas I found very confusing when I was first attempting to implement oAuth. oAuth requires that you register the oAuth consumer application(our Apps Script) with the oAuth provider(Jira). It was not clear that using Application Links was the way to do that. At first I thought it was the OAuth credentials under the Apps configuration that I needed to set up
But no, that is far too obvious a place for Atlassian! Then I thought I needed to create an App using the Atlassian Developer portal. Strike two! Whenever you hear or read Atlassian documentation talking about Apps they are really talking about plug-ins, not external applications. I did eventually find Jira documentation that explained that what I needed was an Application Link, but you will need to trust me that it was not as easy as you would think to find. Or maybe I’m just slow. Either way I pulled out a lot of hair trying to figure it out. Hopefully this will save some hair on your head or whatever you take your frustrations out on!
The Application Links configuration is found in the Products section of Jira administration
As mentioned before you will need to have Jira admin permissions to view, create, and manage an application link. If you don’t have admin permissions then your Jira admin should be able to do it for you. I would suggest if that is the case that you walk through the setup together so you know that the link was configured exactly as you need. One typo can cost hours of going back and forth trying to diagnose where the problem is.
First step in creating the link is entering a URL for the application and hitting the Create Link button. You can enter anything you want. The application link will not actually be using the URL we enter here so it does not need to be valid. When you hit the Create Button it will bring up the following dialog:
The dialog will tell you there is a problem with the URL and to enter a new URL. Just hit the Continue button. The next dialog will prompt you for a number of different fields. Only three of the fields are required.
- Application Name : Enter anything you like
- Application Type: Select Generic Application
- Create incoming link: Check
The other fields don’t need to be filled out because those are related to an outgoing request. In other words Jira would be calling back to our app. The Consumer Key you see in this dialog is not the consumer key we need to be concerned with. If you want to fill in the fields it will not harm anything. The format for the URL fields is below. Click the Continue button.
The final dialog is the most important one. This is where you will enter the Consumer Key you entered in the script and where you will enter the public key you generated or obtained earlier. The Consumer Name should match what you entered in you App Scripts for the CONSUMER_KEY global variable. For the public key, open your public key .pem file and copy the entire contents of the file. It is best to do a select all just to be sure you get the entire thing. It needs to contain the — — -BEGIN PUBLIC KEY — — — and — — -END PUBLIC KEY — — -. If you do not have this Jira will tell you the key is invalid. Hit the Continue button.
If all goes according to plan you should now see a check mark that the link was created successfully and your application link listed below
The Application Link is ready to use
4 Run the updatePrivateKey() Function
We are ready to begin executing the code. The first step is to run the updatePrivateKey() function. This was one of the enhancements I made to the Jira example from the oAuth library because the private key really tripped me up. When I first tried the example from the oAuth1 library I just copy and pasted the private key from the file directly into my script and used the JS line continuation character to make it one big string like below:
This didn’t work. I kept getting an error from a Google hash function saying Invalid Argument: key. It made no sense because the private key was in the standard RSA format. After more hair pulling and a lot of trial and error I finally came to discover that the Google oAuth library won’t work with private keys containing \n characters within the key even though that is part of the RSA standard. I don’t know if that is just an issue with this library or a broader issue with the hash function it is using. The Jira documentation also mentions removing all line breaks as well which leads me to believe it isn’t necessarily limited to the Google oAuth1 library. So, the key must be the following format for it to work:
-----BEGIN PUBLIC KEY-----\n<private key>\n-----END PUBLIC KEY-----\n
Where the <private key> has had all the newline(/n) characters removed. Leaving the private key hard coded in the script was never the intent. That was just a first step to get something to work. Rather than having to manually manipulate the copy and pasted key I decided to read it from the private key file, programmatically reformat it, and then store it in UserProperties to be used when needed.
The updatePrivateKey() function merely passes the private key folder and file name to the function that does all the work called storeKey(). Run updatePrivateKey(). The private key should now be stored in the UserProperties where it can be used by the getService() function which is called by the callAPI() function.
5 Run the callAPI() Function, Authorize Access, and run the callAPI() function again
Here is the basic flow:
- The getService() function is called which returns an object containing a hasAccess() function indicting whether theAPI call has been granted access
- If access has been granted then it will make the call to the Jira REST API using the access token that is stored in UserProperties and then display the result of the call in the console (Image A)
- If access hasn’t been granted then the service.authorize() function will be called which returns a URL to the Jira authorization along with the unauthorized request token
- The authorization URL will be displayed in the console (Image B). You then copy and paste the URL into a browser tab which will bring up the Jira authorization dialog(or Jira login if you haven’t logged into Jira yet). Select Allow to grant access to API calls going forward (Image C). The access token is returned and then stored in UserProperties. Whenever an API call is made going forward it will retrieve the access token from UserProperties to use to authenticate the call
- If you wish to delete the access token, call the reset() function. That will delete the token from the user properties and force you to reauthenticate the next time you run the callAPI() function
That wraps it up. I hope you found the post informative and it makes it easier for you to use oAuth with Jira REST API calls. Feel free to post any questions or comments you may have.