How to solve "Unauthorized; scope does not match"?

Hello,

I’m developing a Slack Bot and it uses an Jira OAuth App to login in to the user jira account.

Here is a snippet of my code (python):

@app.route('/login', methods=['POST'])
def login():
    data = request.form
    user_id = data.get('user_id')
    channel_id = data.get('channel_id')
    
    jira_authorize_url = "https://auth.atlassian.com/authorize"
    params = {
        'audience': 'api.atlassian.com',
        'client_id': os.environ['JIRA_CLIENT_ID'],
        'scope': 'read:jira-user read:jira-work manage:jira-project manage:jira-configuration read:issue:jira write:issue:jira',
        'redirect_uri': os.environ['JIRA_REDIRECT_URI'],
        'state': user_id,
        'response_type': 'code',
        'prompt': 'consent'
    }
    login_url = f"{jira_authorize_url}?{requests.compat.urlencode(params)}"
    client.chat_postMessage(channel=channel_id, text=f"Please log in to Jira: {login_url}")


@app.route('/listprojects', methods=['POST'])
def list_projects():
    data = request.form
    user_id = data.get('user_id')
    
    # Fetch the access token from the database
    c.execute('SELECT access_token FROM tokens WHERE user_id = ?', (user_id,))
    result = c.fetchone()
    if result is None:
        return Response("You need to log in first. Use /login.", status=200)

    access_token = result[0]

    # Fetch cloud ID
    try:
        cloud_id = get_cloud_id(access_token)
    except Exception as e:
        logging.error(f"Failed to get cloud ID: {e}")
        return Response("Failed to get cloud ID. Please try again.", status=200)

    # Use the access token to fetch projects from Jira
    headers = {
        "Authorization": f"Bearer {access_token}",
        "Accept": "application/json"
    }
    response = requests.get(f"https://api.atlassian.com/ex/jira/{cloud_id}/rest/api/3/project", headers=headers)
    if response.status_code != 200:
        logging.error(f"Failed to fetch projects: {response.text}")
        return Response("Failed to fetch projects. Please try again.", status=200)

    projects = response.json()
    project_list = "\n".join([f"{project['key']}: {project['name']}" for project in projects])
    
    client.chat_postMessage(channel=user_id, text=f"Here are your projects:\n{project_list}")
    return Response(), 200



@app.route('/report', methods=['POST'])
def generate_report():
    data = request.form
    user_id = data.get('user_id')

    # Fetch the selected project from the database
    c.execute('SELECT project_key FROM selected_project WHERE user_id = ?', (user_id,))
    result = c.fetchone()
    if result is None:
        return Response("You need to select a project first. Use /selectProject <project_key>.", status=200)

    project_key = result[0]

    # Fetch the access token from the database
    c.execute('SELECT access_token FROM tokens WHERE user_id = ?', (user_id,))
    result = c.fetchone()
    if result is None:
        return Response("You need to log in first. Use /login.", status=200)

    access_token = result[0]

    # Fetch cloud ID
    try:
        cloud_id = get_cloud_id(access_token)
    except Exception as e:
        logging.error(f"Failed to get cloud ID: {e}")
        return Response("Failed to get cloud ID. Please try again.", status=200)

    # Use the access token to fetch sprint information from Jira
    headers = {
        "Authorization": f"Bearer {access_token}",
        "Accept": "application/json"
    }

    # Get board ID for the project
    response = requests.get(f"https://api.atlassian.com/ex/jira/{cloud_id}/rest/agile/1.0/board?projectKeyOrId={project_key}", headers=headers)
    if response.status_code != 200:
        logging.error(f"Failed to fetch boards: {response.text}")
        if response.status_code == 401:
            return Response("Unauthorized access. Please re-authenticate using /login.", status=200)
        return Response("Failed to fetch boards. Please try again.", status=200)

    boards = response.json().get('values', [])
    if not boards:
        return Response(f"No boards found for project {project_key}.", status=200)

    board_id = boards[0]['id']

    # Get current sprint ID
    response = requests.get(f"https://api.atlassian.com/ex/jira/{cloud_id}/rest/agile/1.0/board/{board_id}/sprint?state=active", headers=headers)
    if response.status_code != 200:
        logging.error(f"Failed to fetch sprints: {response.text}")
        if response.status_code == 401:
            return Response("Unauthorized access. Please re-authenticate using /login.", status=200)
        return Response("Failed to fetch sprints. Please try again.", status=200)

    sprints = response.json().get('values', [])
    if not sprints:
        return Response(f"No active sprints found for board {board_id}.", status=200)

    sprint_id = sprints[0]['id']

    # Get issues in the current sprint
    response = requests.get(f"https://api.atlassian.com/ex/jira/{cloud_id}/rest/agile/1.0/sprint/{sprint_id}/issue", headers=headers)
    if response.status_code != 200:
        logging.error(f"Failed to fetch issues: {response.text}")
        if response.status_code == 401:
            return Response("Unauthorized access. Please re-authenticate using /login.", status=200)
        return Response("Failed to fetch issues. Please try again.", status=200)

    issues = response.json().get('issues', [])

    # Generate report
    report = f"Daily Report for Sprint {sprint_id} in Project {project_key}:\n"
    for issue in issues:
        report += f"- {issue['key']}: {issue['fields']['summary']} (Status: {issue['fields']['status']['name']})\n"

    client.chat_postMessage(channel=user_id, text=report)
    return Response(), 200

the route /login and /listprojects work perfectly. But the /report doesn’t.

DEBUG:urllib3.connectionpool:Starting new HTTPS connection (1): api.atlassian.com:443
DEBUG:urllib3.connectionpool:https://api.atlassian.com:443 "GET /ex/jira/2e1b18bd-d8f4-4086-9538-2850e79aa295/rest/agile/1.0/board?projectKeyOrId=PMAAS HTTP/1.1" 401 59
ERROR:root:Failed to fetch boards: {"code":401,"message":"Unauthorized; scope does not match"}

How can I solve that?

@KnowCodeCompany,

You can find the required scopes for get all boards in the Jira Software Cloud REST API docs (not with the Jira Platform REST APIs). In this case, you would need read:board-scope:jira-software and read:project:jira.

Thank you very much, @ibuchanan!