How to implement OAuth 2 (3LO)?

We have been using Jira OAuth 2 (3LO) for a while now and it has been working quite well. Recently, we started looking at the performance side of things and have been studying the docs on OAuth2 (3LO) to understand where we can make improvements to our implementation. While doing so a bunch of questions came up and I am wondering how others do certain things or what’s the intended use.

I am just posting a big list here and would be happy to start a discussion/get some feedback.

1. What data should we store from the authorization flow?

From what I understand, at the very least you should store the refresh_token from the initial request to POST auth.atlassian.com/oauth/token in a persistent store because this will allow you to fetch a new access token later. The access token itself could go e.g. in a Redis cache and expire based on expires_in value. If there is no access token in the cache or an API call returns 401 we could re-try the API call after refreshing the access token using the stored refresh token.

2. How to make API calls efficiently?

The docs suggest to first make a GET request to api.atlassian.com/oauth/token/accessible-resources and then to

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

even more confusing/complicated at the end of section 4 it says

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.

This poses two questions:

  1. Does that mean every API call requires two actual API calls?
  2. How are we meant to “find” the site that we/the customer wants to use?

Here but this is what we did:
After making the initial call to auth.atlassian.com/oauth/token we make a call to api.atlassian.com/oauth/token/accessible-resources. If there is only one site we store it in the persistent store together with the refresh token (see the first question). If there is more then one site we will ask the user in the UI which site he wants to use on the first API call - then update the persistent store with that site. Now we don’t need to make the additional call to get accessible-resources on every API call. Not sure if that’s a good idea but so far it has been working. Curious how others deal with this.

3. Why and when to check site access for the app?

Section 4 in the docs describes a process to check site access when calling a site’s API. From what I understand the idea is to call the accessible-resources endpoint before you make the actual API call. I am not quite sure though what you should do with the response other than getting the id/cloudid. Should we check if the scopes of the site we want to call match the scopes granted in the initial authorization response? And, why should we check this every time we call the API?

4. What do API response payloads look like (in particular error response payloads)?

We’d be interested to know what API responses we would have to expect and what they look like. I think we are quite clear about the success case but wonder what error status codes to expect from different OAuth API endpoints (/oauth/token, /accessible-resources) and what payloads we can expect. E.g. we found that an error response from POST auth.atlassian.com/oauth/token returns a JSON with {error: "...", error_description: "..."} but it would be good to have better docs on what status we should handle and what error payloads are meant to look like.

Any feedback on these points would be highly appreciated :pray:

1 Like

Hi @tbinna,

apologies, just saw this as I was out on holidays. To your questions:

  1. This is generally up to you, but having access tokens in a memory store and refresh tokens in a more persistent store generally makes sense. To further confirm, whenever a access token is not available or returns a 401 you should try getting a new access token via the refresh token. If that fails again, the best practice is to put the user back into the authorization flow.
  2. We recommend caching the cloudId from the accessible-resources response once you received it. For that purpose, we’re looking into adding the FQDN of the site as well to the response to accommodate for custom domain support.
  3. There’s no need to call accessible-resources before doing any API request unless you’re missing the cloudId.
  4. We’re looking into improving both at the moment, improving the errors messages error codes we might issue and also document them publicly.

We apologies for the rough edges you and others might still run into while implementing 3LO, we’re working hard to address them.

Please let me know should you have any further questions.

  • Sascha
2 Likes