Last updated Nov 21, 2024

OAuth 1.0a for REST APIs (Deprecated)

Deprecation Warning

OAuth 1.0a is a deprecated authentication protocol and should not be used.

For new Jira Cloud integrations, you should use one of the recommended alternatives listed below. Additionally, if you have existing services that integrate with Jira Cloud using OAuth 1.0, you should look to replace it with one of the recommended authentication approaches listed:

See Integrating with Jira Software Cloud page for more information on the options for developing with Jira Software Cloud.

Clients authenticate against the Jira REST API using OAuth (1.0a). This tutorial explains how OAuth works with Jira and walks through an example of how to use OAuth to authenticate a Java application (consumer) against the Jira (resource) REST API for a user (resource owner).

Show me the code

Before you begin

To complete this tutorial, you need:

Overview

OAuth is an authentication protocol that enables a user (resource owner) to grant a third-party application (consumer/client) access to their information on another site (resource). Jira uses 3-legged OAuth (3LO), which means that the user is involved in the authentication process by authorizing access to their data on the resource (instead of 2-legged OAuth, where the user is not involved).

Roles

The roles in the OAuth authentication process, and how they relate to authenticating with Jira, are:

  • Resource: when integrating a client application with Jira, Jira is considered to be the resource.
  • Resource owner: As Jira is the resource, the Jira user is considered the resource owner for an authentication request.
  • Consumer: The client application is registered as a consumer by creating an application link (that uses OAuth) in Jira that links to the client.

Process

The authentication process, commonly known as the "OAuth dance," works by the resource owner granting access to their information on the resource by authenticating a request token. The consumer uses the request token to obtain an access token from the resource. Once the client has an access token, it uses it to make authenticated requests to the resource until the token expires or is revoked.

This diagram shows the process in more detail:

Jira OAuth Dance

See it in action

This walkthrough shows how the OAuth authentication process works. First, it configures Jira so that the example client can authenticate against it using OAuth. Then it uses the example client to initiate the "OAuth dance" before making an authenticated request to the Jira REST API.

Step 1: Download the example app

  1. Clone the example Oauth client application code from the atlassian-oauth-examples/ repository
  2. Navigate to the java directory:
    1
    2
    cd atlassian-oauth-examples/java
    
  3. Build the client by running this command in the root of the project:
    1
    2
    mvn clean compile assembly:single
    
  4. Navigate to the target directory in the project and run:
    1
    2
    java -jar OAuthTutorialClient-1.0.jar requestToken
    
    Ignore the exception in the output. This step generates the config.properties file, which is used later.

Step 2. Configure the client application as an OAuth consumer

In Jira, OAuth consumers are represented by application links. Application links use OAuth with RSA-SHA1 signing for authentication. This means that a private key is used to sign requests rather than the OAuth token secret/consumer secret. The following steps generate an RSA public/private key pair and creates a new application link in Jira that uses the key.

Generate an RSA public/private key pair:

  1. In a terminal, run these openssl commands. Do this anywhere in your file system:

    1
    2
    openssl genrsa -out jira_privatekey.pem 1024
    openssl req -newkey rsa:1024 -x509 -key jira_privatekey.pem -out jira_publickey.cer -days 365
    openssl pkcs8 -topk8 -nocrypt -in jira_privatekey.pem -out jira_privatekey.pcks8
    openssl x509 -pubkey -noout -in jira_publickey.cer  > jira_publickey.pem
    

    This generates a 1024 bit private key, creates an X509 certificate, and extracts the private key (PKCS8 format) to the jira_privatekey.pcks8 file. It then extracts the public key from the certificate to the jira_publickey.pem file.

  2. Copy the private key from the jira_privatekey.pcks8 file.

  3. Navigate to the target directory in the example OAuth client project. Edit the config.properties file and make these changes:

    • Paste the private key over the value of private_key field. Remove all line breaks.
    • Change the jira_home to the URL of your Jira development instance, for example, https://example-dev1.atlassian.net
    • Set the consumer_key to OauthKey.
  4. Save the config.properties file.

Configure the client app as a consumer in Jira, using application links:

  1. Choose the Jira icon (icon, icon, icon, icon) > Jira settings > Products. Select Application links in the left menu.
  2. Enter any URL, for example, http://example.com/, and click Create new link. Ignore the warning that No response was received from the URL you entered and click Continue.
  3. On the first screen of the Link applications dialog, enter anything in the fields. However, make sure you tick Create incoming link. Link applications
    Note, in this example, the client application details (such as URL, name, and type) don't matter because it's only retrieving data from Jira. Therefore, the set up uses a one-way (incoming) link from the client to Jira.
  4. In the next screen of the Link applications dialog, enter the consumer details for the example client. Set it to these values:
    • Consumer key = OauthKey
    • Consumer name = Example Jira app
    • Public key = Copy the public key from the jira_publickey.pem file and paste it into this field Copying public keys
  5. Click Continue. The resulting application link typically looks like this: Approve the flow

The example client is now configured as an OAuth consumer in Jira.

Step 3. Do the OAuth dance

The "OAuth dance" is a term used to describe the process of getting an access token from the resource that the consumer can use to access information on the resource. This involves a "dance" where different tokens are passed between the consumer, resource owner, and resource (see OAuth overview above).

  1. In a terminal, navigate to the target directory of the example OAuth client project.

  2. Run this command to request an unauthorized request token from your Jira instance.

    1
    2
    java -jar OAuthTutorialClient-1.0.jar requestToken
    

    The outputs details of the new request token:

    1
    2
    Token:            ec3dj4byySM5ek3XW7gl7f4oc99obAlo
    Token Secret:   OhONj0eF7zhXAMKZLbD2Rd3x7Dmxjy0d
    Retrieved request token. go to https://jira101.atlassian.net/plugins/servlet/oauth/authorize?oauth_token=ec3dj4byySM5ek3XW7gl7f4oc99obAlo to authorize it
    

    The request token persists for 10 minutes. When it expires, you request a new one.

  3. In a browser, go to the URL specified in the output and this dialog shows: Approve the flow

  4. Click Allow. This authorizes the request token and this output shows in the browser:

    1
    2
    Access Approved
    You have successfully authorized 'Example Jira app'. Your verification code is 'qTJkPi'. You will need to enter this exact text when prompted. You should write this value down before closing the browser window.
    

    Make a copy of the verification code.

  5. In a terminal, run this command replacing the example verification code (qTJkPi) with your verification code from the previous step.

    1
    2
    java -jar OAuthTutorialClient-1.0.jar accessToken qTJkPi
    

    Output like this shows in your terminal:

    1
    2
    Access Token:         W1jjOV4sq2iEqxO4ZYZTJVdgbjtKc2ye
    

    Note, OAuth needs the consumer key, request token, verification code, and private key to get the access token. However, in the example client, information such as the consumer key, request token, and private key are stored in the config.properties file when they are generated (check the file when you complete this tutorial and you'll see the new values). You don't want to do this for a production implementation, but this makes the example client easier to use.

The OAuth dance is complete and has provided an access token that is used to make an authenticated request to the Jira REST API.

Step 4. Make an authenticated request to the Jira REST API

An access code is all that is needed to make an authenticated request to the Jira REST API. Requests are made as the user who authorized the initial request token. The access token will persist for 5 years, unless it is revoked.

The example OAuth client only makes GET requests, but it's simple to use. Run this command, substituting <URL for REST method> with the URL of the REST method you want to call.

1
2
java -jar OAuthTutorialClient-1.0.jar request <URL for GET method>

In the code, the example OAuth client stores the access code in the config.properties file. When a request is made, the example client passes the stored access code, rather than you entering it.

Here's an example of a request to get an issue using the example OAuth client, the command gets the issue, JJ-2, using the Jira REST API:

1
2
java -jar OAuthTutorialClient-1.0.jar request https://example-dev1.atlassian.net/rest/api/latest/issue/JJ-2

The command returns an issue like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
{
  "expand": "renderedFields,names,schema,operations,editmeta,changelog,versionedRepresentations",
  "self": "<a href="https://example-dev1.atlassian.net/rest/api/latest/issue/10300">https://example-dev1.atlassian.net/rest/api/latest/issue/10300</a>",
  "id": "10300",
  "fields": {
    "issuetype": {
      "avatarId": 10803,
      "name": "Bug",
      "self": "<a href="https://example-dev1.atlassian.net/rest/api/2/issuetype/1">https://example-dev1.atlassian.net/rest/api/2/issuetype/1</a>",
      "description": "A problem which impairs or prevents the functions of the product.",
      "id": "1",
      "iconUrl": "<a href="https://example-dev1.atlassian.net/secure/viewavatar?size=xsmall&avatarId=10803&avatarType=issuetype">https://example-dev1.atlassian.net/secure/viewavatar?size=xsmall&avatarId=10803&avatarType=issuetype</a>",
      "subtask": false
    },
    "timespent": null,
    "project": {
      "avatarUrls": {
        "48x48": "<a href="https://example-dev1.atlassian.net/secure/projectavatar?pid=10200&avatarId=10700">https://example-dev1.atlassian.net/secure/projectavatar?pid=10200&avatarId=10700</a>",
        "24x24": "<a href="https://example-dev1.atlassian.net/secure/projectavatar?size=small&pid=10200&avatarId=10700">https://example-dev1.atlassian.net/secure/projectavatar?size=small&pid=10200&avatarId=10700</a>",
        "16x16": "<a href="https://example-dev1.atlassian.net/secure/projectavatar?size=xsmall&pid=10200&avatarId=10700">https://example-dev1.atlassian.net/secure/projectavatar?size=xsmall&pid=10200&avatarId=10700</a>",
        "32x32": "<a href="https://example-dev1.atlassian.net/secure/projectavatar?size=medium&pid=10200&avatarId=10700">https://example-dev1.atlassian.net/secure/projectavatar?size=medium&pid=10200&avatarId=10700</a>"
      },
      "name": "Jira Junior",
      "self": "<a href="https://example-dev1.atlassian.net/rest/api/2/project/10200">https://example-dev1.atlassian.net/rest/api/2/project/10200</a>",
      "id": "10200",
      "key": "JJ"
    },
    "fixVersions": [],
    "aggregatetimespent": null,
    "resolution": null,
    "customfield_10500": null,
    "customfield_10700": "com.atlassian.servicedesk.plugins.approvals.internal.customfield.ApprovalsCFValue@c8d588",
    "resolutiondate": null,
    "workratio": -1,
    "lastViewed": "2016-08-01T11:23:39.481+1000",
    "watches": {
      "self": "<a href="https://example-dev1.atlassian.net/rest/api/2/issue/JJ-2/watchers">https://example-dev1.atlassian.net/rest/api/2/issue/JJ-2/watchers</a>",
      "isWatching": true,
      "watchCount": 1
    },
    "created": "2013-05-29T13:56:24.224+1000",
    "customfield_10020": null,
    "customfield_10021": "Not started",
    "priority": {
      "name": "Major",
      "self": "<a href="https://example-dev1.atlassian.net/rest/api/2/priority/3">https://example-dev1.atlassian.net/rest/api/2/priority/3</a>",
      "iconUrl": "<a href="https://example-dev1.atlassian.net/images/icons/priorities/major.svg">https://example-dev1.atlassian.net/images/icons/priorities/major.svg</a>",
      "id": "3"
    },
    "customfield_10300": null,
    "customfield_10102": null,
    "labels": [],
    "customfield_10016": null,
    "customfield_10017": null,
    "customfield_10018": null,
    "customfield_10019": null,
    "timeestimate": null,
    "aggregatetimeoriginalestimate": null,
    "versions": [],
    "issuelinks": [],
    "assignee": {
      "emailAddress": "alana@<a href="http://example.com">example.com</a>",
      "avatarUrls": {
        "48x48": "<a href="https://secure.gravatar.com/avatar/b259e2a7fd37a83b02015192ee247e96?d=mm&s=48">https://secure.gravatar.com/avatar/b259e2a7fd37a83b02015192ee247e96?d=mm&s=48</a>",
        "24x24": "<a href="https://secure.gravatar.com/avatar/b259e2a7fd37a83b02015192ee247e96?d=mm&s=24">https://secure.gravatar.com/avatar/b259e2a7fd37a83b02015192ee247e96?d=mm&s=24</a>",
        "16x16": "<a href="https://secure.gravatar.com/avatar/b259e2a7fd37a83b02015192ee247e96?d=mm&s=16">https://secure.gravatar.com/avatar/b259e2a7fd37a83b02015192ee247e96?d=mm&s=16</a>",
        "32x32": "<a href="https://secure.gravatar.com/avatar/b259e2a7fd37a83b02015192ee247e96?d=mm&s=32">https://secure.gravatar.com/avatar/b259e2a7fd37a83b02015192ee247e96?d=mm&s=32</a>"
      },
      "displayName": "Alana Example",
      "name": "alana",
      "self": "<a href="https://example-dev1.atlassian.net/rest/api/2/user?username=alana">https://example-dev1.atlassian.net/rest/api/2/user?username=alana</a>",
      "active": true,
      "timeZone": "Australia/Sydney",
      "key": "alana"
    },
    "updated": "2016-08-01T11:23:38.022+1000",
    "status": {
      "name": "Open",
      "self": "<a href="https://example-dev1.atlassian.net/rest/api/2/status/1">https://example-dev1.atlassian.net/rest/api/2/status/1</a>",
      "description": "The issue is open and ready for the assignee to start work on it.",
      "iconUrl": "<a href="https://example-dev1.atlassian.net/images/icons/statuses/open.png">https://example-dev1.atlassian.net/images/icons/statuses/open.png</a>",
      "id": "1",
      "statusCategory": {
        "colorName": "blue-gray",
        "name": "To Do",
        "self": "<a href="https://example-dev1.atlassian.net/rest/api/2/statuscategory/2">https://example-dev1.atlassian.net/rest/api/2/statuscategory/2</a>",
        "id": 2,
        "key": "new"
      }
    },
    "components": [],
    "timeoriginalestimate": null,
    "description": "The logo is currently a light cerise. I'd like to see it with a deep pink color.",
    "customfield_10012": null,
    "customfield_10013": null,
    "customfield_10014": null,
    "timetracking": {},
    "customfield_10015": null,
    "customfield_10600": null,
    "customfield_10006": "10",
    "customfield_10601": null,
    "customfield_10007": [
      "com.atlassian.greenhopper.service.sprint.Sprint@dc6300[id=1,rapidViewId=<null>,state=CLOSED,name=Sprint 1,goal=<null>,startDate=2013-07-26T11:31:09.530+10:00,endDate=2013-08-09T11:31:09.530+10:00,completeDate=2013-07-26T11:31:46.489+10:00,sequence=1]",
      "com.atlassian.greenhopper.service.sprint.Sprint@6b3e17[id=2,rapidViewId=<null>,state=ACTIVE,name=Sprint 2,goal=<null>,startDate=2013-08-22T11:35:33.759+10:00,endDate=2013-12-12T11:35:00.000+11:00,completeDate=<null>,sequence=2]"
    ],
    "customfield_10008": null,
    "attachment": [],
    "aggregatetimeestimate": null,
    "summary": "Jira Junior logo is not pink enough",
    "creator": {
      "emailAddress": "admin@<a href="http://example.com">example.com</a>",
      "avatarUrls": {
        "48x48": "<a href="https://secure.gravatar.com/avatar/4beac5df66a475580809e0?d=mm&s=48">https://secure.gravatar.com/avatar/4beac5df66a475580809e0?d=mm&s=48</a>",
        "24x24": "<a href="https://secure.gravatar.com/avatar/4beac5df66a475580809e0?d=mm&s=24">https://secure.gravatar.com/avatar/4beac5df66a475580809e0?d=mm&s=24</a>",
        "16x16": "<a href="https://secure.gravatar.com/avatar/4beac5df66a475580809e0?d=mm&s=16">https://secure.gravatar.com/avatar/4beac5df66a475580809e0?d=mm&s=16</a>",
        "32x32": "<a href="https://secure.gravatar.com/avatar/4beac5df66a475580809e0?d=mm&s=32">https://secure.gravatar.com/avatar/4beac5df66a475580809e0?d=mm&s=32</a>"
      },
      "displayName": "Administrator",
      "name": "admin",
      "self": "<a href="https://example-dev1.atlassian.net/rest/api/2/user?username=admin">https://example-dev1.atlassian.net/rest/api/2/user?username=admin</a>",
      "active": true,
      "timeZone": "Australia/Sydney",
      "key": "admin"
    },
    "subtasks": [],
    "reporter": {
      "emailAddress": "admin@<a href="http://example.com">example.com</a>",
      "avatarUrls": {
        "48x48": "<a href="https://secure.gravatar.com/avatar/4beac5df66a475580809e0?d=mm&s=48">https://secure.gravatar.com/avatar/4beac5df66a475580809e0?d=mm&s=48</a>",
        "24x24": "<a href="https://secure.gravatar.com/avatar/4beac5df66a475580809e0?d=mm&s=24">https://secure.gravatar.com/avatar/4beac5df66a475580809e0?d=mm&s=24</a>",
        "16x16": "<a href="https://secure.gravatar.com/avatar/4beac5df66a475580809e0?d=mm&s=16">https://secure.gravatar.com/avatar/4beac5df66a475580809e0?d=mm&s=16</a>",
        "32x32": "<a href="https://secure.gravatar.com/avatar/4beac5df66a475580809e0?d=mm&s=32">https://secure.gravatar.com/avatar/4beac5df66a475580809e0?d=mm&s=32</a>"
      },
      "displayName": "Administrator",
      "name": "admin",
      "self": "<a href="https://example-dev1.atlassian.net/rest/api/2/user?username=admin">https://example-dev1.atlassian.net/rest/api/2/user?username=admin</a>",
      "active": true,
      "timeZone": "Australia/Sydney",
      "key": "admin"
    },
    "customfield_10000": null,
    "aggregateprogress": {
      "total": 0,
      "progress": 0
    },
    "customfield_10001": null,
    "customfield_10200": "0|10001s:",
    "customfield_10002": null,
    "customfield_10003": null,
    "customfield_10400": null,
    "environment": null,
    "duedate": null,
    "progress": {
      "total": 0,
      "progress": 0
    },
    "comment": {
      "total": 1,
      "comments": [{
        "author": {
          "emailAddress": "admin@<a href="http://example.com">example.com</a>",
          "avatarUrls": {
            "48x48": "<a href="https://secure.gravatar.com/avatar/4beac5df66a475580809e0?d=mm&s=48">https://secure.gravatar.com/avatar/4beac5df66a475580809e0?d=mm&s=48</a>",
            "24x24": "<a href="https://secure.gravatar.com/avatar/4beac5df66a475580809e0?d=mm&s=24">https://secure.gravatar.com/avatar/4beac5df66a475580809e0?d=mm&s=24</a>",
            "16x16": "<a href="https://secure.gravatar.com/avatar/4beac5df66a475580809e0?d=mm&s=16">https://secure.gravatar.com/avatar/4beac5df66a475580809e0?d=mm&s=16</a>",
            "32x32": "<a href="https://secure.gravatar.com/avatar/4beac5df66a475580809e0?d=mm&s=32">https://secure.gravatar.com/avatar/4beac5df66a475580809e0?d=mm&s=32</a>"
          },
          "displayName": "Administrator",
          "name": "admin",
          "self": "<a href="https://example-dev1.atlassian.net/rest/api/2/user?username=admin">https://example-dev1.atlassian.net/rest/api/2/user?username=admin</a>",
          "active": true,
          "timeZone": "Australia/Sydney",
          "key": "admin"
        },
        "created": "2013-06-04T16:11:24.505+1000",
        "updateAuthor": {
          "emailAddress": "admin@<a href="http://example.com">example.com</a>",
          "avatarUrls": {
            "48x48": "<a href="https://secure.gravatar.com/avatar/4beac5df66a475580809e0?d=mm&s=48">https://secure.gravatar.com/avatar/4beac5df66a475580809e0?d=mm&s=48</a>",
            "24x24": "<a href="https://secure.gravatar.com/avatar/4beac5df66a475580809e0?d=mm&s=24">https://secure.gravatar.com/avatar/4beac5df66a475580809e0?d=mm&s=24</a>",
            "16x16": "<a href="https://secure.gravatar.com/avatar/4beac5df66a475580809e0?d=mm&s=16">https://secure.gravatar.com/avatar/4beac5df66a475580809e0?d=mm&s=16</a>",
            "32x32": "<a href="https://secure.gravatar.com/avatar/4beac5df66a475580809e0?d=mm&s=32">https://secure.gravatar.com/avatar/4beac5df66a475580809e0?d=mm&s=32</a>"
          },
          "displayName": "Administrator",
          "name": "admin",
          "self": "<a href="https://example-dev1.atlassian.net/rest/api/2/user?username=admin">https://example-dev1.atlassian.net/rest/api/2/user?username=admin</a>",
          "active": true,
          "timeZone": "Australia/Sydney",
          "key": "admin"
        },
        "self": "<a href="https://example-dev1.atlassian.net/rest/api/2/issue/10300/comment/10100">https://example-dev1.atlassian.net/rest/api/2/issue/10300/comment/10100</a>",
        "id": "10100",
        "body": "Hi [~william] Jira is *super fun* (see JJ-1). Alana is going to fix the logo.",
        "updated": "2013-06-12T21:55:34.882+1000"
      }],
      "maxResults": 1,
      "startAt": 0
    },
    "votes": {
      "hasVoted": false,
      "self": "<a href="https://example-dev1.atlassian.net/rest/api/2/issue/JJ-2/votes">https://example-dev1.atlassian.net/rest/api/2/issue/JJ-2/votes</a>",
      "votes": 0
    }
  },
  "key": "JJ-2"
}

You now know how to use OAuth to make an authenticated request to the Jira REST API.

Tips

Rate this page: