Trying to perform User Impersonation with OAuth2, but I can only access public pages

Hello, I’m trying to create an app that impersonates a user and can search confluence with the same permissions as that user. I followed the guides for impersonation with Oauth2, but the responses from the api don’t include items that the impersonated user should have permissions to see.

I post here in the hopes someone can point me in the right direction. Implementing Oauth2 is new to me and maybe I’m missing something obvious.

Context:

  • My goal is to create a service in our company servers that searches confluence, and shows only the results that the user has permission to read. When using our company service, the user will be prompted to login to confluence, and the company app will use the token to ensure that the permissions are respected.
  • However, after configuring all this, the app can only read pages and spaces that are marked as public
  • I’m following these guides: OAuth 2.0 (3LO) apps / Confluence scopes for OAuth 2.0 (3LO) and Forge apps

Our progress so far:

  • We created an app (type:OAuth2 integration) in the developer console, using the account of an admin in confluence.

  • Get the client_ID and client_secret associated with this integration

  • Set the scopes to “read:content-details:confluence”, “read:content:confluence”, “search:confluence” by adding them to the AuthorizationURL

  • Set the callback to an URL in our company, including our internal ID in place of ${YOUR_USER_BOUND_VALUE} (let’s call it companyID)

  • Tried presenting this authorization URL to a user, and after they accepted, we receive the token and the companyId

  • POST tohttps://auth.atlassian.com/oauth/token using the token that arrived at the callbackURL + the client_id + client_secret of our app

  • We receive a response with access_token, scope, expires_in

  • We check that the access_token works by calling: https://api.atlassian.com/oauth/token/accessible-resources as the guide suggests. The URL of our confluence is correct and the scopes are those requested

  • Tried the token (as Bearer token) to GET https://api.atlassian.com/me → the name and information of the user that accepted the link is correct. OK

The problem:

  • Tried to use the access_token at https://COMPANY.atlassian.net/wiki/api/v2/spaces, but the response contains only spaces visible by everyone, and none of the spaces that the user should be able to read. If https://api.atlassian.com/me knows the impersonated user info, why does this endpoint not know?

So the question:

When impersonating a user, where (as in, which URL/endpoint) am I supposed to use the access_token if i want to search pages?

  • I tried using it in the V1 api https://COMPANY.atlassian.net/wiki/rest/api/searchcql=type=page&limit=25, but it responds with

    <status>
        <status-code>404</status-code>
        <message>null for uri: https://COMPANY.atlassian.net/wiki/rest/api/searchcql=type=page&amp;limit=25</message>
    </status>
    
  • I tries using it in the V2 api (https://{your-domain}/wiki/api/v2/pages) but it only returns public pages, and not pages the user should be able to see

  • Maybe there’s a third api I don’t know about?

The access token that you get you can use to call the Atlassian APIs but you need to use the https://api.atlassian.com/ex/confluence/{cloudid}/{api} endpoints instead of https://COMPANY.atlassian.net/wiki/api/v2/spaces

The cloud id can be retrieved from the /accessible-resources endpoint.

It is also described in the documentation here how to construct a request URL https://developer.atlassian.com/cloud/confluence/oauth-2-3lo-apps/#3-2-construct-the-request-url

I hope it helps.

Hi Andi.Muka, thanks for your answer.

I managed to get my cloud_id. However, when building the URL as indicated in the guide linked, (https://api.atlassian.com/ex/jira/{cloudID}/rest/api/spaces/ for confluence) I receive “401 Unauthorized; scope does not match”. The same happens when querying other endpoints (pages, users, etc)

Is read:content:confluence and read:content-details:confluence not enough to see the pages?

Edit: the OAuth2 App (as configured in the Developer Console) has more scopes than the token, can this be the cause of the error?

@PabloDominguezFrojan I think you are building the wrong URL because you are trying to access Jira APIs with Confluence Scopes.

You should use https://api.atlassian.com/ex/confluence/{cloudid}/{api} for accessing Confluence APIs

Nice catch! however after fixing the URL I still get the same error.

{
    "code": 401,
    "message": "Unauthorized; scope does not match"
}

I added some more scopes to both the Oauth2 app and the authorization URL, to make sure that wasn’t the problem. Now I have

// https://api.atlassian.com/oauth/token/accessible-resources
[
    {
        "id": "{cloudID}",
        "url": "https://COMPANY.atlassian.net",
        "name": "COMPANY",
        "scopes": [
            "readonly:content.attachment:confluence",
            "read:confluence-content.permission",
            "read:confluence-props",
            "read:confluence-content.all",
            "read:confluence-groups",
            "read:confluence-content.summary",
            "read:confluence-user",
            "search:confluence",
            "read:confluence-space.summary"
        ],
        "avatarUrl": "https://site-admin-avatar-cdn.prod.public.atl-paas.net/avatars/240/koala.png"
    }
]

The URLs I tried to call:

  • https://api.atlassian.com/ex/confluence/{cloudID}/rest/api/space (as per the link you sent about URL construction) → “410 com.atlassian.confluence.api.service.exceptions.pagesmodes.GoneException: This deprecated endpoint has been removed.”

  • https://api.atlassian.com/ex/confluence/{cloudID}/wiki/api/v2/pages (from the Rest api v2 examples) → “Scopes don’t match”

  • https://api.atlassian.com/ex/confluence/{cloudID}/wiki/rest/api/pages (from the Rest api v1 examples) → “Scopes don’t match”

I’m finding the different documentations and api versions a bit confusing. Does anybody have an example of a complete URL (host and endpoint) that can be used with the access_token to get a page contents?

You can find the relevant needed scopes in the Rest API documentation. E.g https://developer.atlassian.com/cloud/confluence/rest/v2/api-group-page/#api-pages-get

So if you want to get pages you would need to use the read:page:confluence scope as documented in the “OAuth 2.0 scopes required” section of the API documentation

I think I managed to solve my issue.

A recap of what I did, for future visitors:

Created the Oauth2 app in the developer console.

I enabled the following scopes in the Oauth2 app (not sure which are neccessary, but these are the scopes I had on the first success, and I’m not touching anything now)

        "scopes": [
            "readonly:content.attachment:confluence",
            "read:confluence-content.permission",
            "read:page:confluence",
            "read:confluence-props",
            "read:space:confluence",
            "read:confluence-groups",
            "read:confluence-content.summary",
            "read:confluence-user",
            "search:confluence",
            "read:confluence-space.summary",
            "read:confluence-content.all",
            "read:attachment:confluence"
        ],

Created an authorization link with those same scopes encoded in the URL:

https://auth.atlassian.com/authorize?audience=api.atlassian.com&client_id={yourAppClientID}&scope=read%3Aattachment%3Aconfluence%20read%3Apage%3Aconfluence%20read%3Aspace%3Aconfluence%20read%3Ame%20read%3Aaccount%20read%3Aconfluence-space.summary%20read%3Aconfluence-props%20read%3Aconfluence-content.all%20read%3Aconfluence-content.summary%20search%3Aconfluence%20read%3Aconfluence-content.permission%20read%3Aconfluence-user%20read%3Aconfluence-groups%20readonly%3Acontent.attachment%3Aconfluence&redirect_uri={yourCallbackURL}&state=${userBoundValue}&response_type=code&prompt=consent

Sent and made the test user accept the link.

Received the callback at my $yourCallbackURL, and extracted the $Code from the response body

Used $Code to call https://auth.atlassian.com/oauth/token (+ the clientID, clientSecret, and callbackURL for my app, the parameters needed are in the guide)

The response contained $accessToken.

Then to get a list of the pages I called (with $accessToken as bearer token):

https://api.atlassian.com/ex/confluence/{cloud-id}/wiki/rest/api/search?cql=type=page&limit=25

And I could see pages that the test user was allowed to see.