Pranav
Pranav

Reputation: 1

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

Upvotes: 0

Views: 2112

Answers (2)

Gaurav Lokhande
Gaurav Lokhande

Reputation: 21

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)

Upvotes: 0

Sérgio Correia
Sérgio Correia

Reputation: 586

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.

Upvotes: 1

Related Questions