Last updatedJul 17, 2019

OAuth 2.0 (3LO) for apps

This functionality is currently in developer preview. During the developer preview, grants can only be authorized from the developer's account (that is, by the owner of the app). We are now in beta for the public distribution of apps - see ACJIRA-1588 for updates.

This page shows you how to configure your app to use OAuth 2.0 (3LO) (also known as "three-legged OAuth" or "authorization code grants"). Note, you cannot use OAuth 2.0 (3LO) with Atlassian Connect apps.

OAuth 2.0 (3LO) is used to allow external applications and services to access Atlassian product APIs on a user's behalf. For example, if a user has granted a Gmail app access to Jira Cloud, via OAuth 2.0 (3LO), then that app can interact with Jira issues (for example, view, transition, comment, watch, etc).

Which authentication method should I use?

Overview

OAuth 2.0 (3LO) involves three parties:

  • An Atlassian site (resource)
  • A user (resource owner)
  • An external application/service (client).

For example, a Jira site (resource), an Atlassian user (resource owner), and Gmail (client). Underlying the authorization interactions between these three parties is an authorization server.

To the user, the authorization process looks like this:

authorization process for user

  1. The app directs the user to an Atlassian screen that prompts them to grant access to their data on the Atlassian site. The screen displays the access being requested in the Atlassian product. For example, the screenshot below shows the scopes in Jira that the app wants access to. 3lo consent screen
  2. The user grants (or denies) access to their data on the Atlassian site, via the screen.
  3. The user is directed back to the external service. If the user granted access, the external service can now access data (within the specified scopes) from the Atlassian site on the user's behalf.

Underlying this process are a number of interactions between the external service, the app, and the authorization server. The full process is described in more detail below.

authorization process for app

Note, this process assumes that the external service has registered an app with Atlassian that can use OAuth 2.0 (3LO).

  1. The user, in the external service, uses a feature that requires data from an Atlassian product.
  2. The external service seeks authorization to access the product's APIs, within the specified scopes, on behalf of the user.
  3. The user is directed to the Atlassian account login screen, if they are not already logged in, and prompted to log in.
  4. The user is directed to the authorization URL for the authorization server. This displays a screen that prompts the user to grant access to their data.
  5. If the user grants access, the user is directed to the callback URL with an authorization code.
  6. The app makes a POST to the token URL for the authorization server, exchanging the authorization code for an access token.
  7. The access token can now be used to access the APIs for the authorized Atlassian site on behalf of the user. This can be used until the token expires or is revoked.

Enabling OAuth 2.0 (3LO)

Before you can implement OAuth 2.0 (3LO) for your app, you need to enable it for your app in app management:

  1. In app management, navigate to your app (or create one if you don't already have one).
  2. In the APIS AND FEATURES section in the side navigation, click +Add.
  3. In the Features section of the APIs and features page, click Add for OAuth 2.0 (3LO) then click Configure.
  4. Enter the Callback URL. Set this to any URL that is accessible by the app. When you implement OAuth 2.0 (3LO) in your app (see next section), the redirect_uri must match this URL.
  5. Click Save changes.

Your screen should look something like this: 3lo app management

Note, if you haven't already added the Jira platform REST API to your app, you should do this now:

  1. In the APIS AND FEATURES section in the side navigation, click +Add.
  2. In the APIs section of the APIs and features page, click Add for Jira platform REST API then click Configure.
  3. Add the desired scopes for your app.

Implementing OAuth 2.0 (3LO)

Once you have enabled OAuth 2.0 (3LO) for your app, you can implement it in your app's code. There are a number of key parts to this:

  1. Direct the user to the authorization URL to get an authorization code
  2. Exchange the authorization code for an access token
  3. Authorize any calls to the product APIs using the access token
  4. Check site access for the app

1. Direct the user to the authorization URL to get an authorization code

As described in the Overview above, your app should start the authorization flow by directing the user to the authorization URL:

1
2
3
4
5
6
7
8
https://auth.atlassian.com/authorize?
  audience=api.atlassian.com&
  client_id=YOUR_CLIENT_ID&
  scope=REQUESTED_SCOPE_ONE%20REQUESTED_SCOPE_TWO&
  redirect_uri=https://YOUR_APP_CALLBACK_URL&
  state=YOUR_USER_BOUND_VALUE&
  response_type=code&
  prompt=consent

Use the authorization URL in a GET request. You can copy this URL from the OAuth 2.0 authorization code grants (3LO) for apps page for your app in app management. Alternatively, you can construct the URL manually (for example, if you want to specify scopes from multiple products).

The query parameters for the authorization URL are described below:

  • audience: (required) Set this to api.atlassian.com.
  • client_id: (required) Set this to the Client ID for your app. Find this in App details for your app in app management.
  • scope: (required) Set this to the desired scopes:
    • Separate multiple scopes with a space.
    • Only choose from the scopes that you have already added to the APIs for your app in app management.
    • You may specify scopes from multiple products, in addition to Jira platform REST API scopes.
  • redirect_uri: (required) Set this to the Callback URL configured for Authorization code grants for your app in app management.
  • state: (required for security) Set this to a value that is associated with the user you are directing to the authorization URL, e.g., a hash of the user's session ID. Make sure that this is a value that cannot be guessed. You may be able to generate and validate this value automatically, if you are using an OAuth 2.0 client library or an authentication library with OAuth 2.0 support. For more information, including why this parameter is required for security, see What is the state parameter used for? below.
  • response_type: (required) Set to code as you are requesting an authorization code (not a token).
  • prompt: (required) Set to consent so that the screen prompting the user to grant access will display.

If successful, the user will be redirected to the app's callback URL, with an authorization code provided as a query parameter called code. This code can be exchanged for an access token, as described in step 2.

Jira scopes for OAuth 2.0 (3LO)

The Jira Cloud REST API supports the following scopes. These scopes are specific to OAuth 2.0 (3LO) (i.e., different to Connect scopes).

Scope definitionScope nameDescription
read:jira-userView user profilesView user information in Jira that the user has access to, including usernames, email addresses, and avatars.
read:jira-workView Jira issue dataRead Jira project and issue data, search for issues and objects associated with issues like attachments and worklogs.
write:jira-workCreate and manage issuesCreate and edit issues in Jira, post comments as the user, create worklogs, and delete issues.
manage:jira-projectManage project settingsCreate and edit project settings and create new project-level objects (for example, versions and components).
manage:jira-configurationManage Jira global settingsTake Jira administration actions (for example, create projects and custom fields, view workflows, and manage issue link types).

To find out which scope is required for an operation, check the OAuth scopes required field for the operation in the Jira platform REST API documentation. If the operation has the statement Apps cannot access this REST resource, you cannot use it with OAuth 2.0 (3LO).

Note that an app will always be constrained by the permissions of the user that an app is acting for, regardless of its scopes. For example, if an app has the manage:jira-configuration scope but user does not have the Administer Jira permission, then the app will not be able to take Jira administration actions.

2. Exchange authorization code for access token

1
2
3
4
curl --request POST \
  --url 'https://auth.atlassian.com/oauth/token' \
  --header 'Content-Type: application/json' \
  --data '{"grant_type": "authorization_code","client_id": "YOUR_CLIENT_ID","client_secret": "YOUR_CLIENT_SECRET","code": "YOUR_AUTHORIZATION_CODE","redirect_uri": "https://YOUR_APP_CALLBACK_URL"}'
  • client_id: (required) Set this to the Client ID for your app. Find this in App details for your app in app management.
  • client_secret: (required) Set this to the Secret for your app. Find this in App details for your app in app management.
  • code: (required) Set this to the authorization code received from the initial authorize call (described above).
  • redirect_uri: (required) Set this to the callback URL configured for your app in app management.

If successful, this call will return an access token. This access token can be used to make API calls, as described below.

3. Make calls to the API using the access token

Your app now has an access token that it can use to authorize requests to the APIs for the Atlassian site. To make requests, do the following:

  1. Get the cloudid for your site.
  2. Construct the request URL using the cloudid.
  3. Call the API, using the access token and request URL.

3.1 Get the cloudid for your site

Make a GET request to https://api.atlassian.com/oauth/token/accessible-resources passing the access token as a bearer token in the header of the request. For example:

1
2
3
4
curl --request GET \
  --url https://api.atlassian.com/oauth/token/accessible-resources \
  --header 'Authorization: Bearer ACCESS_TOKEN' \
  --header 'Accept: application/json'

This will retrieve the sites that have scopes granted by the token (see Check site access for the app below for details). Here's an example of a response with a single Jira site:

1
2
3
4
5
6
7
8
9
10
11
12
[
  {
    "id": "1324a887-45db-1bf4-1e99-ef0ff456d421",
    "name": "your-domain1",
    "scopes": [
      "write:jira-work",
      "read:jira-user",
      "manage:jira-configuration"
    ],
    "avatarUrl": "https:\/\/site-admin-avatar-cdn.prod.public.atl-paas.net\/avatars\/240\/flag.png"
  }
]

Find your site in the response and copy the id. This is the cloudid for your site.

3.2 Construct the request URL

Requests that use OAuth 2.0 (3LO) are made via api.atlassian.com (not https://your-domain.atlassian.net). Construct your request URL using the following structure:

https://api.atlassian.com/ex/jira/{cloudid}/{api}

where:

  • {cloudid} is the cloudid for your site that you obtained in the previous step. For example, 11223344-a1b2-3b33-c444-def123456789.
  • {api} is the base path and name of the API. For example, /rest/api/2/project for the project endpoint in the Jira REST API.

Your request URL should look something like this (using the examples above):

https://api.atlassian.com/ex/jira/11223344-a1b2-3b33-c444-def123456789/rest/api/2/project

Note that if you are copying the examples in the Jira REST API documentation, you will need to amend the example URLs as they currently use https://your-domain.atlassian.net/{api} not api.atlassian.com/ex/jira/{cloudid}/{api}.

3.3 Call the API

Make the API call passing the access token as a bearer token in the header of the request. This will authorize the request on the user's behalf.

1
2
3
4
curl --request GET \
  --url https://api.atlassian.com/ex/jira/{cloudid}/{api} \
  --header 'Authorization: Bearer ACCESS_TOKEN' \
  --header 'Accept: application/json'

For example:

1
2
3
4
curl --request GET \
  --url https://api.atlassian.com/ex/jira/11223344-a1b2-3b33-c444-def123456789/rest/api/2/project \
  --header 'Authorization: Bearer aBCxYz654123' \
  --header 'Accept: application/json'

4. Check site access for the app

An authorization grant is when a user consents to your app accessing a specific site and APIs within that site (via scopes). This can change when either of the following occur:

  • The user revokes the grant for the site.
  • The user consents to a new grant for the site. The scopes in the new grant override the scopes in the existing grant.

Therefore, since a grant can change over time, it's important that you check your app's access to a site and its APIs when calling the site's APIs. To check this, call the accessible-resources endpoint on https://auth.atlassian.com (you used this endpoint in a previous step to get the cloudid for your site). The endpoint is described in detail below:

Get list of resources

GET /oauth/token/accessible-resources

Request

Request parameters: None

Example:

1
2
curl --header 'Authorization: Bearer <access_token>' \
  --url 'https://api.atlassian.com/oauth/token/accessible-resources'
Response

200 OK example:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
[
  {
    "id": "8594f221-9797-5f78-1fa4-485e198d7cd0",
    "name": "your-domain2",
    "scopes": [
      "write:jira-work",
      "read:jira-user"
    ],
    "avatarUrl": "https:\/\/site-admin-avatar-cdn.prod.public.atl-paas.net\/avatars\/240\/koala.png"
  },
  {
    "id": "1324a887-45db-1bf4-1e99-ef0ff456d421",
    "name": "your-domain1",
    "scopes": [
      "write:jira-work",
      "read:jira-user",
      "manage:jira-configuration"
    ],
    "avatarUrl": "https:\/\/site-admin-avatar-cdn.prod.public.atl-paas.net\/avatars\/240\/flag.png"
  }
]

Each item in the response describes a container (e.g., a Jira site) that your app has access to, the scopes associated with that access, and metadata such as the name and avatar URL (if any). It's important to understand that this endpoint won't tell you anything about the user's permissions, which may limit the resources that your app can access via the site's APIs.

Note, the id is not unique across containers (i.e., two entries in the results can have the same id), so you may need to infer the type of container from its scopes.

Publishing your OAuth 2.0 (3LO) app

When you create an OAuth 2.0 (3LO) app, it is private by default. This means that only you can install and use it. If you want to distribute your app to other people, you must make it public.

  1. In the Developer Service Desk, go to 3LO Apps - Beta > Distribute apps using Cloud APIs (3LO) - Beta and create a ticket.
  2. Wait until the ticket is approved.
  3. Go to app management, find your app and click it.
  4. Click Distribute in the left menu.
  5. Enable the toggle switch in the Make your app public section.

Your app is now public and can be used by others.

Note that making your app public does not make it available on the Atlassian Marketplace. Listing a OAuth 2.0 (3LO) app on the Atlassian Marketplace is currently not supported.

Known issues

We are aware of the following issues with OAuth 2.0 (3LO). Some of the issues have workarounds, which are described below. Others do not have workarounds, but are listed so that you are aware of them. If you discover an issue that is not listed below, raise a ticket at https://ecosystem.atlassian.net/projects/ACJIRA.

Implicit grant flow not supported

OAuth 2.0 (3LO) currently supports the code grant flow only. It does not support the implicit grant flow. We understand that this is preventing people from using OAuth 2.0 (3LO) for standalone mobile apps and web/JavaScript (Chrome, Electron) apps and we are investigating ways to address this.

Site-scoped grants limitations

The current implementation of OAuth 2.0 (3LO) uses site-scoped grants, which means that the user only grants access to a single site each time they complete the consent flow. Be aware that there are a few limitations to this:

  • If your integration needs information from multiple sites at one time, then the user will be required to go through multiple consent flows (one for each site).
  • The consent screen currently requires the user to select the site that they want to grant access to. This can be confusing for users if there are multiple sites.
  • With site-scoped grants, an access token can have access to multiple sites. This means that an app can't delete an access token to revoke access. For example, an access token could grant access to site A, then delete it to remove access. However, if the user grants the app access to site C later, the app will be issued with an access token with access to sites A and B. The only way access can be removed is for the user to revoke access via the Connect apps tab in their account settings at https://{subdomain}.atlassian.net/people/{account_id}/settings/apps.

Accessible resources API does not return the base URL

The accessible resources API is used to retrieve the sites that have scopes granted by the token. However, this API does not return the base URL of the sites in the response. Note that the name property in the response does not always correspond to the base URL of the site, even though it looks similar. It is only suitable for display to users.

To get the base URL of the site, call Get Jira instance info using the access token.

Apps cannot declare searchable entity properties

Apps can store and read the values of entity properties (issue properties and project properties) using the REST API. However, in the current implementation of OAuth 2.0 (3LO), apps cannot declare searchable entity properties. This means that if your app uses OAuth 2.0 (3LO), it won't be able to refer to entity properties in JQL queries.

Frequently asked questions

How do I get a new access token, if my access token expires or is revoked?

You have two options:

  • Initiate the entire authorization flow from the beginning again.
  • Use a refresh token to get another access token.

A refresh token can be returned with the access token in your initial authorization flow. To do this, add the offline_access scope to the scope parameter of the authorization URL.

Once you have the refresh token, exchange it for an access token by calling the token URL:

1
2
3
4
curl --request POST \
  --url 'https://auth.atlassian.com/oauth/token' \
  --header 'Content-Type: application/json' \
  --data '{ "grant_type": "refresh_token", "client_id": "YOUR_CLIENT_ID", "client_secret": "YOUR_CLIENT_SECRET", "refresh_token": "YOUR_REFRESH_TOKEN" }'
  • grant_type: Set to refresh_token.
  • client_id: (required) Set this to the Client ID for your app. Find this in App details for your app in app management.
  • client_secret: (required) Set this to the Secret for your app. Find this in App details for your app in app management.
  • refresh_token: The refresh token that you obtained with your original access token.

If successful, a new access token will be returned that you can use to make calls to the product API.

What happens if a user grants access to more than one Atlassian site for an app?

Only one grant exists per app for a given Atlassian account. If a user grants access to more than one Atlassian site for this app, then the additional sites are added to the same grant. This means that existing access tokens will give you access to all sites and scopes that a user has granted your app access to.

What is the state parameter used for?

The primary use for the state parameter is to associate a user with an authorization flow. This makes the authorization flow more secure, as the authorization flow cannot be hijacked to associate a user's account with another user's token. Consider the following example scenario using Jira:

  1. An application, named Incidents_Application, has a Jira integration that implements OAuth 2.0 authorization code grants but does not specify a state parameter.
  2. A malicious actor, Mallory, initiates a Jira authorization flow for herself. This could be via the Incidents_Application or by crafting an authorization URL that includes the Incidents_Application's client_id.
  3. Mallory blocks the request to the Incidents_Application's callback URL during the authorization flow. She records the URL, including the code parameter.
  4. Mallory tricks another user, Edward, into visiting the callback URL in his browser.
  5. The Incidents_Application handles the callback and exchanges Mallory's code for an access token to Jira. Edward is logged into the Incidents_Application and the callback request came from Edward's browser, so Mallory's token is now linked to Edward's account.
  6. Mallory now has access to information sent to Edward by the Incidents_Application via the Jira integration. For example, the Incidents_Application may create a Jira ticket about a confidential incident, where the ticket is intended to be restricted to Edward but is restricted to Mallory instead.

If the Incidents_Application integration had used a state parameter, the Incidents_Application would have known that the callback URL belonged to Mallory and ignored the request.

Other uses for the state parameter include:

  • Acting as a key for keeping track of specific details about the flow.
  • Returning the user to the right step in their workflow after sending them through the authorization flow.