I want to do upload some files from local to my OneDrive. This case I'm using MSAL(Microsoft Authentication Library) library. There is a little issue in ms_graph.py file. The flow user_code doesn't responding back correctly. How can I fix this issue? In the normal case code needs to print user_code to access OneDrive account.
ms_graph.py:
import webbrowser
from datetime import datetime
import json
import os
import msal
GRAPH_API_ENDPOINT = 'https://graph.microsoft.com/v1.0'
def generate_access_token(app_id, scopes):
# Save Session Token as a token file
access_token_cache = msal.SerializableTokenCache()
# read the token file
if os.path.exists('ms_graph_api_token.json'):
access_token_cache.deserialize(open("ms_graph_api_token.json", "r").read())
token_detail = json.load(open('ms_graph_api_token.json', ))
token_detail_key = list(token_detail['AccessToken'].keys())[0]
token_expiration = datetime.fromtimestamp(int(token_detail['AccessToken'][token_detail_key]['expires_on']))
if datetime.now() > token_expiration:
os.remove('ms_graph_api_token.json')
access_token_cache = msal.SerializableTokenCache()
# assign a SerializableTokenCache object to the client instance
client = msal.PublicClientApplication(client_id=app_id, token_cache=access_token_cache)
accounts = client.get_accounts()
if accounts:
# load the session
token_response = client.acquire_token_silent(scopes, accounts[0])
else:
# authetnicate your accoutn as usual
flow = client.initiate_device_flow(scopes=scopes)
print('user_code: ' + flow['user_code'])
webbrowser.open('https://microsoft.com/devicelogin')
token_response = client.acquire_token_by_device_flow(flow)
with open('ms_graph_api_token.json', 'w') as _f:
_f.write(access_token_cache.serialize())
return token_response
if __name__ == '__main__':
...
main.py:
import os
import requests
from ms_graph import generate_access_token, GRAPH_API_ENDPOINT
APP_ID = 'my_client_id'
SCOPES = ['Files.ReadWrite']
access_token = generate_access_token(APP_ID, SCOPES)
headers = {
'Authorization': 'Bearer' + access_token['access_token']
}
file_path = r'C:\Users\user\OneDrive\Desktop\Fiyat-2\example_file.txt'
file_name = os.path.basename(file_path)
with open(file_path, 'rb') as upload:
content = upload.read()
response = requests.put(
GRAPH_API_ENDPOINT + f'me/drive/items/root:/{file_path}:/content',
headers=headers,
data=content
)
print(response.json())
Output:
C:\Users\user\OneDrive\Desktop\Fiyat-2\venv\Scripts\python.exe C:\Users\user\OneDrive\Desktop\Fiyat-2\main.py
Traceback (most recent call last):
File "C:\Users\user\OneDrive\Desktop\Fiyat-2\main.py", line 8, in <module>
access_token = generate_access_token(APP_ID, SCOPES)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Users\user\OneDrive\Desktop\Fiyat-2\ms_graph.py", line 34, in generate_access_token
print('user_code: ' + flow['user_code'])
~~~~^^^^^^^^^^^^^
KeyError: 'user_code'
Process finished with exit code 1
Note that: Device code flow requires web browser interaction, the user must enter the code generated by the flow and the login with the user account.
To upload the file to the OneDrive you can make use of below code:
# Microsoft Graph API endpoint
GRAPH_API_ENDPOINT = 'https://graph.microsoft.com/v1.0/'
# Configuration for Microsoft Authentication
config = {
"client_id": "ClientID",
"authority": "https://login.microsoftonline.com/TenantID",
"scope": ["Files.ReadWrite"],
}
access_token_cache = msal.SerializableTokenCache()
# Load cached token if available
if os.path.exists('ms_graph_api_token.json'):
access_token_cache.deserialize(open("ms_graph_api_token.json", "r").read())
token_detail = json.load(open('ms_graph_api_token.json', ))
token_detail_key = next(iter(token_detail.keys()), None) # Get the first key in the dictionary
if token_detail_key == 'AccessToken':
token_expiration = datetime.fromtimestamp(int(token_detail['AccessToken']['expires_on']))
if datetime.now() > token_expiration:
os.remove('ms_graph_api_token.json')
access_token_cache = msal.SerializableTokenCache()
app = msal.PublicClientApplication(
config["client_id"],
authority=config["authority"],
token_cache=access_token_cache,
)
result = None
# First, try to get a token from the cache.
accounts = app.get_accounts()
if accounts:
# Use the first account to acquire a token silently.
result = app.acquire_token_silent(config["scope"], account=accounts[0])
if not result:
# If a token is not available in the cache, use the device flow to acquire a new token.
flow = app.initiate_device_flow(scopes=config["scope"])
print(flow["message"])
# Open the browser automatically
webbrowser.open(flow["verification_uri"])
result = app.acquire_token_by_device_flow(flow)
# Use the access token to call the Microsoft Graph API.
if "access_token" in result:
access_token = result["access_token"]
headers = {'Authorization': 'Bearer ' + access_token}
# File upload parameters
file_path = r'C:\Users\rukmini\Downloads\RukRoleAssignmentPs.txt'
file_name = os.path.basename(file_path)
with open(file_path, 'rb') as upload:
content = upload.read()
response = requests.put(
GRAPH_API_ENDPOINT + f'me/drive/root:/{file_name}:/content',
headers=headers,
data=content
)
# Output response
print(response.json())
else:
error = result.get("error")
if error == "invalid_client":
print("Invalid client ID. Please check your Azure AD application configuration")
else:
print(error)
The above code opens the web browser, and the user must enter the code and login:
The file uploaded successfully to OneDrive:
OneDrive Portal:
To resolve the issue, modify the ms_graph.py
like below:
if accounts:
# load the session
token_response = client.acquire_token_silent(scopes, accounts[0])
else:
# authetnicate your accoutn as usual
flow = client.initiate_device_flow(scopes=scopes)
webbrowser.open('https://microsoft.com/devicelogin')
token_response = client.acquire_token_by_device_flow(flow)
with open('ms_graph_api_token.json', 'w') as _f:
_f.write(access_token_cache.serialize())
return token_response
if __name__ == '__main__':
...