Using Personal Access Token to access issue in Jira Cloud (from CLI util)

Jira Personal Access Token in Jira Cloud

I am trying to create some basic automation to be used with personal access tokens. For the begining I want to fetch an issue by ID.

When I have a token created with “Create API token without scopes” and use it with basic auth it works just fine:

curl -D-    -u myusername@mycompany.com:MY_TOKEN_WITHOUT_SCOPE    -X GET    -H "Content-Type: application/json"    https://mycompany.atlassian.net/rest/api/3/issue/UU-42

Equivalent in javascript:

    // Construct the full API URL
    const apiUrl = `https://${mycompany}.atlassian.net/rest/api/3/issue/${jiraKey}`;
  
    // Encode credentials for Basic Authentication header
    const credentials = `${username}:${token}`;
    const encodedCredentials = Buffer.from(credentials).toString('base64');
    const authHeader = `Basic ${encodedCredentials}`;
  
    // Define request headers
    const headers = {
      'Authorization': authHeader,
      'Accept': 'application/json', // Tell the server we expect JSON back
      // 'Content-Type': 'application/json' // Usually not needed for GET requests
    };

    display(`Fetching Jira issue: ${apiUrl}`);

    const response = await fetch(apiUrl, {
        method: 'GET',
        headers: headers,
    });

Using it with Bearer does not work.

However, if I create a token with “Create API token with scopes” nothing seems to work.

curl -D- -H "Authorization: Bearer MYTOKENWITHSCOPE" -X GET    -H "Content-Type: application/json"    https://mycompany.atlassian.net/rest/api/3/issue/UU-42

Equivalent in javascript:

    // Construct the full API URL
    const apiUrl = `https://${mycompany}.atlassian.net/rest/api/3/issue/${jiraKey}`;

    const authHeader = `Bearer ${myTokenWithScope}`;
  
    // Define request headers
    const headers = {
      'Authorization': authHeader,
      'Accept': 'application/json', // Tell the server we expect JSON back
      // 'Content-Type': 'application/json' // Usually not needed for GET requests
    };

    display(`Fetching Jira issue: ${apiUrl}`);

    const response = await fetch(apiUrl, {
        method: 'GET',
        headers: headers,
    });

It responds 403 {"error": "Failed to parse Connect Session Auth Token"}

Attempt to use PAT with scope the old way is a bit fruitless:

curl -D-    -u myusername@mycompany.com:MY_TOKEN_WITH_SCOPE    -X GET    -H "Content-Type: application/json"    https://mycompany.atlassian.net/rest/api/3/issue/UU-42

Responds 404 {“errorMessages”:[“Issue does not exist or you do not have permission to see it.”],“errors”:{}}

I first tried setting up Granular read:issue:jira

I thought I maybe didn’t add enough so I added quite a lot: read:issue:jira
read:field:jira, read:issue-meta:jira, read:issue-field-values:jira, read:user.columns:jira, read:comment.property:jira, read:jql:jira, read:screen-field:jira, read:issue-link:jira, read:jira-expressions:jira, read:issue:jira-software, read:issue-type-transition:jira, read:issue-type:jira, read:project.feature:jira, read:label:jira, read:board-scope:jira-software, read:issue-event:jira, read:issue-details:jira, read:priority:jira, read:issue.transition:jira, read:epic:jira-software, read:issue-type.property:jira, read:design:jira, read:screen:jira, read:issue.remote-link:jira, read:customer.detail-field:jira-service-management, read:dashboard:jira, read:sprint:jira-software, read:project:jira, read:project-type:jira, read:issue.property:jira and it didn’t help either.

I thought this may be an issue with granular scope, so I tried setting all classic scopes read:jira-work and so on and it didn’t help as well.

Then I tried setting up all possible granular scopes and nothing seems to work.

Please help with a working out example to do basic issue read with scoped PAT.

Hi @AndreiKondratev

Thanks a lot for sharing this.

API Tokens with scopes has a different request URL that is similar to 3LO Integrations, please follow the following link for the request URL construction https://developer.atlassian.com/cloud/confluence/oauth-2-3lo-apps/#3-2-construct-the-request-url

API Token with scopes is using Basic Auth not bearer.

At the end of the API Tokens with scope creation there is a hint about the URL construction but please let us know if its not clear enough

2 Likes

Yes, that worked! I really appreciate your help @IbraheemOsama !

Here’s a working JavaScript (NodeJS) snippet to help other devs if they went astray like myself:

    console.log('Fetching issue with Scoped PAT (Personal Access Token) ');
    // Jira Cloud ID can be found by authenticated user at https://company.atlassian.net/_edge/tenant_info

    // From that point the URL should be constructed according to https://developer.atlassian.com/cloud/confluence/oauth-2-3lo-apps/#implementing-oauth-2-0--3lo-

    // According to doc https://developer.atlassian.com/cloud/jira/platform/rest/v3/api-group-issues/#api-rest-api-3-issue-issueidorkey-get permissions to read this resource:
    // either Classic (RECOMMENDED) read:jira-work
    // or Granular read:issue-meta:jira, read:issue-security-level:jira, read:issue.vote:jira, read:issue.changelog:jira, read:avatar:jira, read:issue:jira, read:status:jira, read:user:jira, read:field-configuration:jira
    const apiUrl = `https://api.atlassian.com/ex/jira/${cloudId}/rest/api/3/issue/${jiraKey}`;

    // Encode credentials for Basic Authentication header
    const credentials = `${username}:${token}`;
    const encodedCredentials = Buffer.from(credentials).toString('base64');
    const authHeader = `Basic ${encodedCredentials}`;

    // Define request headers
    const headers = {
        'Authorization': authHeader,
        'Accept': 'application/json; charset=utf-8',
        'Accept-Language': 'en-US,en;q=0.9' // without this I was getting errors in Chinese
    };

    console.log(`Loading Jira issue: ${jiraKey} from ${apiUrl}`);

    try {
        const response = await fetch(apiUrl, {
            method: 'GET',
            headers: headers,
        });

        if (response.ok) {
            // Assuming successful responses are JSON
            console.log(await response.json());
        } else {
            try {
                // Read the body as text first. This is robust for any content type.
                console.error(await response.json());
            } catch (e) {
                console.error("Response was unsuccessful, failed to parse error response.", e);
            }
        }
    } catch (e) {
        console.error("Failed to perform fetch.", e);
    }
1 Like