cancel
Showing results for 
Search instead for 
Did you mean: 
Reply
bdfox Regular Visitor
Regular Visitor

Can't use any Power BI APIs, get 403 even with token

I'm trying to test the APIs (get a list of datasets in my workspace) before progressing on to doing some dataflow work, but anything I do with the APIs after getting my token returns a 403 error.

 

I'm using the example website sample code from the adal python github page (https://github.com/AzureAD/azure-activedirectory-library-for-python/tree/dev/sample)

 

I added an extra requests.get call hitting up https://api.powerbi.com/v1.0/myorg/datasets to get a list of datasets and then just printing it out to the console, which gives me: 

Running API
API Result: 403

 

The resource in the parameters.json file passed into the python script is "00000009-0000-0000-c000-000000000000" which I believe is correct. When I run the token through jwt.io to check it out, I see my username and scps of:

Capacity.Read.All Capacity.ReadWrite.All Content.Create Dashboard.Read.All Dashboard.ReadWrite.All Dataflow.Read.All Dataflow.ReadWrite.All Dataset.ReadWrite.All Group.Read Report.ReadWrite.All Workspace.Read.All Workspace.ReadWrite.All

 

Any help would be appreciated.

 

try:
    from http import server as httpserver
    from http import cookies as Cookie
except ImportError:
    import SimpleHTTPServer as httpserver
    import Cookie as Cookie

try:
    import socketserver
except ImportError:
    import SocketServer as socketserver

try:
    from urllib.parse import urlparse, parse_qs
except ImportError:
    from urlparse import urlparse, parse_qs

import json
import os
import random
import string
import sys
import requests
import urllib.request

import adal

# You can provide account information by using a JSON file. Either
# through a command line argument, 'python sample.py parameters.json', or
# specifying in an environment variable of ADAL_SAMPLE_PARAMETERS_FILE.
#
# The information inside such file can be obtained via app registration.
# See https://github.com/AzureAD/azure-activedirectory-library-for-python/wiki/Register-your-application-with-Azure-Active-Directory
#
# {
#    "resource": "your_resource",
#    "tenant" : "rrandallaad1.onmicrosoft.com",
#    "authorityHostUrl" : "https://login.microsoftonline.com",
#    "clientId" : "624ac9bd-4c1c-4687-aec8-b56a8991cfb3",
#    "clientSecret" : "verySecret=""
# }

parameters_file = (sys.argv[1] if len(sys.argv) == 2 else
                   os.environ.get('ADAL_SAMPLE_PARAMETERS_FILE'))

if parameters_file:
    with open(parameters_file, 'r') as f:
        parameters = f.read()
    sample_parameters = json.loads(parameters)
else:
    raise ValueError('Please provide parameter file with account information.')

PORT = 8088
TEMPLATE_AUTHZ_URL = ('https://login.windows.net/{}/oauth2/authorize?'+
                      'response_type=code&client_id={}&redirect_uri={}&'+
                      'state={}&resource={}')
PBI_RESOURCE = '00000009-0000-0000-c000-000000000000'
RESOURCE = sample_parameters.get('resource', PBI_RESOURCE)
REDIRECT_URI = 'http://localhost:{}/getAToken'.format(PORT)

authority_url = (sample_parameters['authorityHostUrl'] + '/' +
                 sample_parameters['tenant'])

class OAuth2RequestHandler(httpserver.SimpleHTTPRequestHandler):
    def do_GET(self):
        if self.path == '/':
            self.send_response(307)
            login_url = 'http://localhost:{}/login'.format(PORT)
            self.send_header('Location', login_url)
            self.end_headers()
        elif self.path == '/login':
            auth_state = (''.join(random.SystemRandom()
                                  .choice(string.ascii_uppercase + string.digits)
                                  for _ in range(48)))
            cookie = Cookie.SimpleCookie()
            cookie['auth_state'] = auth_state
            authorization_url = TEMPLATE_AUTHZ_URL.format(
                sample_parameters['tenant'],
                sample_parameters['clientId'],
                REDIRECT_URI,
                auth_state,
                RESOURCE)
            self.send_response(307)
            self.send_header('Set-Cookie', cookie.output(header=''))
            self.send_header('Location', authorization_url)
            self.end_headers()
        elif self.path.startswith('/getAToken'):
            is_ok = True
            try:
                token_response = self._acquire_token()
                access_token = token_response['accessToken']
                #Later, if the access token is expired it can be refreshed.
                #auth_context = adal.AuthenticationContext(authority_url)
                #token_response_refresh = auth_context.acquire_token_with_refresh_token(
                #    token_response['refreshToken'],
                #    sample_parameters['clientId'],
                #    RESOURCE,
                #    sample_parameters['clientSecret'])
                ################OK We now have a token, lets do stuff with it to see if that works#########################
                #refresh_token = token_response_refresh['accessToken']
                print("Running API")
                headers = {'Authorization': 'Bearer {}'.format(access_token), 'Content-Type': 'application/json'}
                api = requests.get('https://api.powerbi.com/v1.0/myorg/datasets', headers=headers)
                print("API Result: {}\n{}\n".format(api.status_code, api.text))
                message = "Full Token Response: {}<br><br>token: {}<br><br>Status Code: {}<br>Returned: {}".format(token_response, access_token, api.status_code, api.text)
            except ValueError as exp:
                message = str(exp)
                is_ok = False
            self._send_response(message, is_ok)

    def _acquire_token(self):
        parsed = urlparse(self.path)
        code = parse_qs(parsed.query)['code'][0]
        state = parse_qs(parsed.query)['state'][0]
        cookie = Cookie.SimpleCookie(self.headers["Cookie"])
        if state != cookie['auth_state'].value:
            raise ValueError('state does not match')
        ### Main logic begins
        auth_context = adal.AuthenticationContext(authority_url)
        return auth_context.acquire_token_with_authorization_code(
            code,
            REDIRECT_URI,
            RESOURCE,
            sample_parameters['clientId'],
            sample_parameters['clientSecret'])
        ### Main logic ends

    def _send_response(self, message, is_ok=True):
        self.send_response(200 if is_ok else 400)
        self.send_header('Content-type', 'text/html')
        self.end_headers()

        if is_ok:
            #todo, pretty format token response in json
            message_template = ('<html><head><title>Succeeded</title></head>'
                                '<body><p>{}</p></body></html>')
        else:
            message_template = ('<html><head><title>Failed</title></head>'
                                '<body><p>{}</p></body></html>')

        output = message_template.format(message)
        self.wfile.write(output.encode())

httpd = socketserver.TCPServer(('', PORT), OAuth2RequestHandler)

print('serving at port', PORT)
httpd.serve_forever()
1 ACCEPTED SOLUTION

Accepted Solutions
bdfox Regular Visitor
Regular Visitor

Re: Can't use any Power BI APIs, get 403 even with token

Finally figured it out. The resource in the parameters.json needs to be https://analysis.windows.net/powerbi/api or it won't work.

1 REPLY 1
bdfox Regular Visitor
Regular Visitor

Re: Can't use any Power BI APIs, get 403 even with token

Finally figured it out. The resource in the parameters.json needs to be https://analysis.windows.net/powerbi/api or it won't work.