1
votes

I am able to get a token using the default scope for powerbi ( "scope" : "https://analysis.windows.net/powerbi/api/.default"] )

With that token, I am able to read the workspaces my user has access to, ( "https://api.powerbi.com/v1.0/myorg/groups") and the reports information inside each of those workspaces ( "https://api.powerbi.com/v1.0/myorg/reports/")

But it does not matter if I reuse the same token or just acquire a brand new, if I try to export a specific report, I got a 401 error code. This is the way I am issuing the requests.get

 token_ = <new token or reused from previous get requests> 
 reports = requests.get(  # Use token to call downstream service
      config['reports']+report_id+'/Export',
      headers={'Authorization': 'Bearer ' + token_ },)

Now, if I go to https://docs.microsoft.com/en-us/rest/api/power-bi/reports/getreportsingroup
and sign in (with the same user I am using on my python script). Get the token from that page and use it on my script. It works, If I use it in postman, it works I I try to use the token acquired by my script in Postman, I also get a 401 error. So, yes, my script is not getting the correct token for this particular entry point but it is good enough for the groups and reports entry point.

Is there anything I need to add to the request for the token on this particular entry point?

Thank you very much, Andres

Here is the full script I am using, there is also a params.json that looks like this:

{
    "authority": "https://login.microsoftonline.com/1abcdefg-abcd-48b6-9b3c-bd5123456",
    "client_id": "5d2545-abcd-4765-8fbb-53555f2fa91",
    "username":"myusername@tenant",
    "password": "mypass",
    "scope" : ["https://analysis.windows.net/powerbi/api/.default"],
    "workspaces" : "https://api.powerbi.com/v1.0/myorg/groups",
    "reports": "https://api.powerbi.com/v1.0/myorg/reports/"

}


#script based on msal github library sample
import sys  # For simplicity, we'll read config file from 1st CLI param sys.argv[1]
import json
import logging
import requests
import msal

def exportReport(report_id,token_):
        result = app.acquire_token_by_username_password( config["username"], config["password"], scopes=config["scope"])
        token_ = result['access_token']
        print(f'Using token: {token_}')
        reports = requests.get(  # Use token to call downstream service
          config['reports']+report_id+'/Export',
          headers={'Authorization': 'Bearer ' + token_ },)
        print(f'-reports: {reports.status_code}')

def list_reports(workspace_id,ws_id,ws_name,token_):
    print(f'reports id for workspace {ws_name}')
    for rp in workspace_id['value']:
        if rp["id"] == "1d509119-76a1-42ce-8afd-bd3c420dd62d":
          exportReport("1d509119-76a1-42ce-8afd-bd0c420dd62d",token_)

def list_workspaces(workspaces_dict):
    for ws in workspaces_dict['value']:
        yield (ws['id'],ws['name'])

config = json.load(open('params.json'))

app = msal.PublicClientApplication(
    config["client_id"], authority=config["authority"],
    )

result = None

if not result:
    logging.info("No suitable token exists in cache. Let's get a new one from AAD.")
    result = app.acquire_token_by_username_password(
        config["username"], config["password"], scopes=config["scope"])

if "access_token" in result:
    workspaces = requests.get(  # Use token to call downstream service
        config['workspaces'],
        headers={'Authorization': 'Bearer ' + result['access_token']},).json()
    ids=list_workspaces(workspaces) #prepare workspace generator
    headers = {'Authorization': 'Bearer ' + result['access_token']}
    while True:
      try:
        ws_id,ws_name=next(ids)
        reports = requests.get(  # Use token to call downstream service
          config['workspaces']+'/'+ws_id+'/reports',
          headers={'Authorization': 'Bearer ' + result['access_token']},).json()
        list_reports(reports,ws_id,ws_name,result['access_token'])
      except StopIteration: 
        exit(0)
else:
    print(result.get("error"))
    print(result.get("error_description"))
    print(result.get("correlation_id"))  # You may need this when reporting a bug
    if 65001 in result.get("error_codes", []): 
        # AAD requires user consent for U/P flow
        print("Visit this to consent:", app.get_authorization_request_url(config["scope"]))
1
Please show the code you used to get the token. - Joy Wang-MSFT

1 Answers

0
votes

From your description, I suppose you didn't grant the correct permission for your AD App used to login your user account in the code, please follow the steps below.

Navigate to the Azure portal -> Azure Active Directory -> App registrations -> find your AD App used in the code(filter with All applications) -> API permissions -> add the Report.Read.All Delegated permission in Power BI Service API(this permission is just for read action, if you need some further write operation, choose Report.ReadWrite.All) -> click the Grant admin consent for xxx button at last.

enter image description here

enter image description here

enter image description here

Update:

Use the application id of the access token got from Get-PowerBIAccessToken solve the issue.