Obtaining JWT token from Azure AD using Python - azure

I am working on an application that needs to obtain a bearer token form Azure AD. Right now I have everything set in Azure and I can generate/obtain the token using Postman (see picture)
However, and this is my struggle, I am having problems obtaining the token programmatically using Python. The code below is what I have tried, with some variations of what to iinclude as fields in the request_payload, without any luck
import json
import requests
#TOKEN_URL = "https://login.microsoftonline.com/organizations/oauth2/v2.0/token"
TOKEN_URL = "https://login.microsoftonline.com/41ff26dc-250f/oauth2/token?resource=https://graph.windows.net"
RESOURCE_URL = "https://login.microsoftonline.com/41ff26dc-739be8610c21/oauth2/authorize?resource=https://graph.windows.net"
def authenticate():
request_payload = {
"callback_url" : "https://localhost",
"auth_url" : "https://login.microsoftonline.com/41ff26dc-250fc21/oauth2/authorize?resource=https://graph.windows.net",
"access_token_url" : "https://login.microsoftonline.com/41ff26dc-739be8610c21/oauth2/token?resource=https://graph.windows.net",
"username": MY USER NAME,
"password": MY PASSWORD,
"resource": RESOURCE_URL,
"grant_type": "Authorization_Code",
"client_id": 'e0d00a8e-b799-4285-be3f-eb5822aaa86e',
"client_secret": '-n24Y2is~p5Jk7~6kYcp4~q2lrmnRCXoW_'}
response = requests.post(url=TOKEN_URL, data=request_payload).json()
print(response)
bearer_token = response["access_token"]
print(bearer_token)
return bearer_token
print(authenticate())
This is the error I get when I fill in my credentials (MY USER NAME and MY PASSWORD) appropriately
{'error': 'invalid_request', 'error_description': "AADSTS900144: The request body must contain the following parameter: 'code'.\r\nTrace ID: d84b06a7-1c45-4657-bb3f-085248de5d01\r\nCorrelation ID: e431a09a-07dc-4c12-bf19-3b8ff7e7c358\r\nTimestamp: 2020-06-25 21:55:25Z", 'error_codes': [900144], 'timestamp': '2020-06-25 21:55:25Z', 'trace_id': 'd84b06a7-1c45-4657-bb3f-085248de5d01', 'correlation_id': 'e431a09a-07dc-4c12-bf19-3b8ff7e7c358', 'error_uri': 'https://login.microsoftonline.com/error?code=900144'}
Any thoughts, suggestions or leads are very much appreciated. Like I said before, I am not sure what fields should I put in the payload, I am not even sure what are all possible fields to try there either.

Please follow this document for auth code flow
you need to send below
https://login.microsoftonline.com/{tenant}/oauth2/v2.0/authorize?
client_id=6731de76-14a6-49ae-97bc-6eba6914391e
&response_type=code
&redirect_uri=http%3A%2F%2Flocalhost%2Fmyapp%2F
&response_mode=query
&scope=openid%20offline_access%20https%3A%2F%2Fgraph.microsoft.com%2Fmail.read
&state=12345
please refer to this python sample

Related

PowerBI Permissions

I am trying to access the API to download usage statistics from PowerBI and integrate with other reports. When I run the below code
import requests
import json
base_url = "https://api.powerbi.com/v1.0/myorg/admin/"
url = "https://login.microsoftonline.com/my_tenant_id_goes_here/oauth2/token"
scope = "https://analysis.windows.net/powerbi/api/.default"
grant_type = "client_credentials"
client_id = "my_client_id"
client_secret = "my_client_secret"
resource = "https://analysis.windows.net/powerbi/api"
header = {
"scope": scope,
"grant_type": grant_type,
"client_id": client_id,
"client_secret": client_secret,
"resource": resource
}
r = requests.post(url, data = header)
login_result = r.json()
print(login_result)
token_type = login_result.get("token_type")
access_token = login_result.get("access_token")
authorization_key = token_type + " " + access_token
print('Authentication Key Generated')
headers = {'Content-Type':'application/json','Authorization': authorization_key}
print('Consuming Dashboards Rest End Point')
data = requests.get(base_url + 'dashboards', headers=headers)
print(data)
json_data = data.content
print(json_data)
I get the following result after print(login_result)
{'token_type': 'Bearer', 'expires_in': '3599', 'ext_expires_in': '3599', 'expires_on': '1667296164', 'not_before': '1667292264', 'resource': 'https://analysis.windows.net/powerbi/api', 'access_token': 'longlongalphanumericstring'}
It seems to have found the access token correctly. However when I print data I get a <401> error, and the json_data reads
b'{"error":{"code":"PowerBINotAuthorizedException","pbi.error":{"code":"PowerBINotAuthorizedException","parameters":{},"details":[],"exceptionCulprit":1}}}'
I have checked in Azure for the permissions. I have the Dashboard.Read.All permission.
When I remove the "admin" from
base_url = "https://api.powerbi.com/v1.0/myorg/admin/"
I get
'{"Message":"API is not accessible for application"}'
It looks to me like a permissions error, but I cannot seem to navigate the Azure interface to fix it.
I tried to reproduce the same in my environment via Postman and got the same error as below:
To resolve the error, try the below:
I created an Azure AD Application and granted permissions:
I created an Azure Security Group and added the Service Principal as a Member like below:
In the PowerBi Admin Portal, Enable Allow service principals to use read-only admin APIs add the security group created above:
It takes around 15 mins to reflect the settings. Now I generated the token via Client Credentials flow like below:
I used v2.0 token endpoint:
https://login.microsoftonline.com/TenantID/oauth2/v2.0/token
client_id:clientID
client_secret:ClientSecret
grant_type:client_credentials
scope:https://analysis.windows.net/powerbi/api/.default
I am able to access PowerBi successfully like below:
https://api.powerbi.com/v1.0/myorg/admin/groups/ID
If still the issue persists, try using Authorization Code flow as Tenant.ReadWrite.All is an Application permission.

Query on Microsoft Graph API Python

I want to pull emails by Graph API from client inbox using python.
I started with a tutorial and successfully experimented over my personal inbox.
My problem,
Every time my code generates an authorization URL.
I have to browse through it (using web browser library) , sign in using my credentials and copy paste the authorization code for generating access token.
Which is a lot of manual work every time.
Question :
Is there a way to automate the whole process of token generation ?
Such that my client only shares his application id and client secret, and email is pulled without his sign in credentials ?
My code is attached below -
import msal
from msal import PublicClientApplication
import webbrowser
import requests
import pandas as pd
APPLICATION_ID="app id"
CLIENT_SECRET="client secret"
authority_url='https://login.microsoftonline.com/common/'
base_url = 'https://graph.microsoft.com/v1.0/'
endpoint_url = base_url+'me'
SCOPES = ['Mail.Read','Mail.ReadBasic']
client_instance = msal.ConfidentialClientApplication(client_id = APPLICATION_ID,client_credential = CLIENT_SECRET,authority = authority_url)
authorization_request_url=client_instance.get_authorization_request_url(SCOPES)
#print(authorization_request_url)
# browsing authorization request URL for retrieving authorization code.
webbrowser.open(authorization_request_url,new=True)
# Manually pasting authorization code.
authorization_code='authorization code from authorization URL'
access_token = client_instance.acquire_token_by_authorization_code(code=authorization_code,scopes=SCOPES)
access_token_id=access_token['access_token']
# Rest of the codes are for hitting the end point and retrieving the messages
Any help with code suggestions will be much appreciated.
Thanks in advance
If you would like to authenticate only with a clientId and clientSecret, without any user context, you should leverage a client credentials flow.
You can check this official MS sample that uses the same MSAL library to handle the client credentials flow. It is quite straightforward, as you can see below:
import sys # For simplicity, we'll read config file from 1st CLI param sys.argv[1]
import json
import logging
import requests
import msal
# Optional logging
# logging.basicConfig(level=logging.DEBUG)
config = json.load(open(sys.argv[1]))
# Create a preferably long-lived app instance which maintains a token cache.
app = msal.ConfidentialClientApplication(
config["client_id"], authority=config["authority"],
client_credential=config["secret"],
# token_cache=... # Default cache is in memory only.
# You can learn how to use SerializableTokenCache from
# https://msal-python.rtfd.io/en/latest/#msal.SerializableTokenCache
)
# The pattern to acquire a token looks like this.
result = None
# Firstly, looks up a token from cache
# Since we are looking for token for the current app, NOT for an end user,
# notice we give account parameter as None.
result = app.acquire_token_silent(config["scope"], account=None)
if not result:
logging.info("No suitable token exists in cache. Let's get a new one from AAD.")
result = app.acquire_token_for_client(scopes=config["scope"])
if "access_token" in result:
# Calling graph using the access token
graph_data = requests.get( # Use token to call downstream service
config["endpoint"],
headers={'Authorization': 'Bearer ' + result['access_token']}, ).json()
print("Graph API call result: ")
print(json.dumps(graph_data, indent=2))
else:
print(result.get("error"))
print(result.get("error_description"))
print(result.get("correlation_id")) # You may need this when reporting a bug
The sample is retrieving a list of users from MS Graph, but it should be just a matter of adapting it to retrieve the list of emails of a specific user by changing the "endpoint" parameter in the parameters.json file to:
"endpoint": "https://graph.microsoft.com/v1.0/users//users/{id | userPrincipalName}/messages"
You can check here more information regarding the MS Graph request to list emails.
register your app
get your tenant id from azure portal and disable mfa
application_id = "xxxxxxxxxx"
client_secret = "xxxxxxxxxxxxx"
#authority_url = "xxxxxxxxxxx"
authority_url = 'xxxxxxxxxxxxxxxxxxxx'
base_url = "https://graph.microsoft.com/v1.0/"
endpoint = base_url+"me"
scopes = ["User.Read"]
tenant_id = "xxxxxxxxxxxx"
token_url = 'https://login.microsoftonline.com/'+tenant_id+'/oauth2/token'
token_data = {
'grant_type': 'password',
'client_id': application_id,
'client_secret': client_secret,
'resource': 'https://graph.microsoft.com',
'scope':'https://graph.microsoft.com',
'username':'xxxxxxxxxxxxxxxx', # Account with no 2MFA
'password':'xxxxxxxxxxxxxxxx',
}
token_r = requests.post(token_url, data=token_data)
token = token_r.json().get('access_token')
print(token)

401 Error - Google API with service account

Hello fellow User.
I'm trying to write to a Google sheets document via a Google service account. The account has writing permission the the document, so that's not the issue.
The reason I'm using a service account is to avoid having to constantly refresh access tokens. I think there lies the issue. Because this is the response from the API:
{
"error": {
"code": 401,
"message": "Request had invalid authentication credentials. Expected OAuth 2 access token, login cookie or other valid authentication credential.
See https://developers.google.com/identity/sign-in/web/devconsole-project.",
"status": "UNAUTHENTICATED"
}
}
This occurs when I try sending a put request to the spreadsheet to edit data. I previously connected the service account to the sheets API, aquired the credentials from there and used them in the request.
If anyone knows, where the error might be, I'd be very thankful.
Here is the Python code that triggered the error:
iat = time.time()
exp = iat + 3600
payload = {'iss': <SERVICE ACCOUNT EMAIL>,
'sub': <SERVICE ACCOUNT EMAIL>,
'aud': f'https://sheets.googleapis.com/v4/spreadsheets/<SHEETID>/values/Sheet1!A2:B5',
'iat': iat,
'exp': exp}
additional_headers = {'kid': <PRIVATE KEY ID>}
signed_jwt = jwt.encode(payload, key=<PRIVATE KEY>,
headers=additional_headers, algorithm='RS256')
headers = {'authorization': f'Bearer {signed_jwt}'}
params = {'range': f'Sheet1!A2B5',
'majorDimension':'ROWS',
'values':[
<LISTS OF ENTRIES>
]}
spreadsheetId = <SHEETID>
resp = requests.put(url=f'https://sheets.googleapis.com/v4/spreadsheets/{spreadsheetId}/
values/Sheet1!A2:B5?valueInputOption=USER_ENTERED',
data = params, headers = headers)
For anyone interested, I was following these guides:
How to use service account for authentication: https://developers.google.com/identity/protocols/oauth2/service-account#python_1
How to issue correct put request: https://developers.google.com/sheets/api/samples/writing
How to get correct endpoint for jwt-encoded credentials: https://developers.google.com/sheets/api/reference/rest
Expected OAuth 2 access token, login cookie or other valid authentication credential.
The coded used for accessing APIs with a service account is not the same as the code used for Oauth2. The code you are using appears to be trying to authenticate using Oauth2.
You should be using something like this.
credentials = ServiceAccountCredentials.from_json_keyfile_name(
KEY_FILE_LOCATION, SCOPES)
service = build('calendar', 'v3', credentials=credentials)
Reference:
oauth2client.service_account module

Google my business service account - Requested entity not found using python requests

I'm trying to retrieve data from the google business api, I've set up a service account and written code that successfully generates a bearer authorization token.
I've then passed this as a header through a get request but I receive this response:
{'error': {'code': 404, 'message': 'Requested entity was not found.', 'status': 'NOT_FOUND'}}
Using postman and sending an oauth request returns the desired information, but as this is meant to be set for automated unattended running, I wanted to explore the service account approach. Myscript is here:
'''
import jwt
import requests
import time
import json
iat = time.time()
exp = iat + 3600
payload = {'iss': 'xxxxx.iam.gserviceaccount.com',
'sub': 'xxxxx.iam.gserviceaccount.com',
'aud': 'https://mybusiness.googleapis.com/',
'iat': iat,
'exp': exp}
additional_headers = {'kid': 'xxxxxx'}
signed_jwt = jwt.encode(payload, "-----BEGIN PRIVATE KEY-----xxxxxxx-----END PRIVATE KEY-----\n", headers=additional_headers,
algorithm='RS256')
print(signed_jwt.decode())
response = requests.get('https://mybusiness.googleapis.com/v4/accounts/xxxxxx/locations', headers = {'Authorization':'Bearer '+signed_jwt.decode()})
response = response.json()
print(response)
'''
any help would be greatly appreciated
In 2020, retrieving data from GMB API using Service Accounts is still not possible (It s*cks!).
Here is the reason: https://support.google.com/business/thread/1856166?msgid=1856214

Partner Center rest api 401 error

I tried to use partnercenter api to get the billing details. But API call return 401 error code. the details provided on partnercpi are correct one and its working fine If I give it as input for Subscription API.
URL : https://api.partnercenter.microsoft.com/v1/customers/<Customer-tenant-ID>/subscriptions/<Subscription-ID>/usagerecords/resources
Can you suggest what can be the reason for the error.
Reference link : https://msftstack.wordpress.com/2016/01/05/azure-resource-manager-authentication-with-python/
Here is my python code.
import adal
import requests
application_secret = 'asdq23Y+tRgEspFxQDPasd'
application_id = '523129-1267b-123'
authentication_endpoint = 'https://login.microsoftonline.com/'
resource = 'https://management.core.windows.net/'
context = adal.AuthenticationContext(authentication_endpoint + tenant_id)
token_response = context.acquire_token_with_client_credentials(resource, application_id, application_secret)
access_token = token_response.get('accessToken')
endpoint = "https://api.partnercenter.microsoft.com/v1/customers/<customer tenant ID>/subscriptions/<sub-id>/usagerecords/resources"
headers = {"Authorization": 'Bearer ' + access_token}
json_output = requests.get(endpoint,headers=headers)
print json_output
Output Response:
<Response [401]>
Is this method not fit for partnerapi usage collection ?. If not pls suggest alternate option. Thanks in advance.
401 is because partner center does not work with just the token from Azure AD.
The actual Auth workflow
Get a Token from Azure AD.
Use that token to get an access token from PartnerCenter.
Use this token to communicate with the partner center REST APIs.
More Info Here

Resources