Python对接Microsoft Graph API遇授权码获取失败及代码报错求助
问题描述
我在华盛顿特区的实习项目中,用Python和Microsoft Graph API开发程序,用来检查公司员工邮箱是否新增了其他授权方式——一旦检测到新增,可能意味着有恶意人员试图访问信息。我参考了Jie Jenn的视频《Getting Started With Microsoft Graph API For Python Development (Set Up & Authentication)》,现在程序能生成设备代码和链接,但无法获取授权码。另外还遇到两个报错:
- demo2.py第31行:
TypeError: 'dict' object is not callable - main.py第12行和第100行:
TypeError: Graph() takes no arguments
错误详情
demo2.py报错
Traceback (most recent call last): File "C:\Users\S.Soundararajan\Documents\PE Project for Azure\demo2.py", line 31, in webbrowser.open(flow('verification_uri')) TypeError: 'dict' object is not callable
main.py报错
Traceback (most recent call last): File "C:\Users\S.Soundararajan\Documents\PE Project for Azure\main.py", line 100, in main() File "C:\Users\S.Soundararajan\Documents\PE Project for Azure\main.py", line 12, in main graph: Graph = Graph(azure_settings) TypeError: Graph() takes no arguments Python Graph Tutorial
相关代码
demo2.py
#import account as account import webbrowser from xmlrpc.client import APPLICATION_ERROR import requests import msal from msal import PublicClientApplication CLIENT_ID = '' CLIENT_SECRET = '' authority_url = '' base_url = 'https://graph.microsoft.com/v1.0/' endpoint = base_url + 'me' SCOPES = ['User.Read', 'Mail.Read', 'Mail.Send'] # Method 2. Login to acquire access_token app = PublicClientApplication( CLIENT_ID, authority = authority_url ) #accounts = app.get_accounts() #if accounts: #app.acquire_token_silent(scopes=SCOPES, account=account[0]) flow = app.initiate_device_flow(scopes=SCOPES) print(flow) print(flow['message']) #app_code = flow['message'] webbrowser.open(flow('verification_uri')) result = app.acquire_token_by_device_flow(flow) access_token_id = result['access_token'] headers = {'Authorization': 'Bearer' + access_token_id} response = requests.get(endpoint, headers=headers) print(response) print(response.json())
main.py
import configparser from graph import Graph from msal import PublicClientApplication def main(): print('Python Graph Tutorial\n') # Load settings config = configparser.ConfigParser() config.read(['config.cfg', 'config.dev.cfg']) azure_settings = config['azure'] graph: Graph = Graph(azure_settings) greet_user(graph) choice = -1 while choice != 0: print('Please choose one of the following options:') print('0. Exit') print('1. Display access token') print('2. List my inbox') print('3. Send mail') print('4. List users (requires app-only)') print('5. Make a Graph call') try: choice = int(input()) except ValueError: choice = -1 if choice == 0: print('Goodbye...') elif choice == 1: display_access_token(graph) elif choice == 2: list_inbox(graph) elif choice == 3: send_mail(graph) elif choice == 4: list_users(graph) elif choice == 5: make_graph_call(graph) else: print('Invalid choice!\n') def greet_user(graph: Graph): user = graph.get_user() print('Hello,', user['displayName']) # For Work/school accounts, email is in mail property # Personal accounts, email is in userPrincipalName print('Email:', user['mail'] or user['userPrincipalName'], '\n') def display_access_token(graph: Graph): token = graph.get_user_token() print('User token:', token, '\n') return 1 def list_users(graph: Graph): users_page = graph.get_users() # Output each users's details for user in users_page['value']: print('User:', user['displayName']) print(' ID:', user['id']) print(' Email:', user['mail']) # If @odata.nextLink is present more_available = '@odata.nextLink' in users_page print('\nMore users available?', more_available, '\n') def list_inbox(graph: Graph): message_page = graph.get_inbox() # Output each message's details for message in message_page['value']: print('Message:', message['subject']) print(' From:', message['from']['emailAddress']['name']) print(' Status:', 'Read' if message['isRead'] else 'Unread') print(' Received:', message['receivedDateTime']) # If @odata.nextLink is present more_available = '@odata.nextLink' in message_page print('\nMore messages available?', more_available, '\n') def send_mail(graph: Graph): # Send mail to the signed-in user # Get the user for their email address user = graph.get_user() user_email = user['mail'] or user['userPrincipalName'] graph.send_mail('Testing Microsoft Graph', 'Hello world!', user_email) print('Mail sent.\n') def make_graph_call(graph: Graph): graph.make_graph_call() # Run main main()
解决方案
1. 修复demo2.py的dict is not callable错误
app.initiate_device_flow()返回的是字典类型,你误用了函数调用语法flow('verification_uri'),正确写法是字典取值的flow['verification_uri']。修改第31行:
webbrowser.open(flow['verification_uri'])
额外建议:在调用acquire_token_by_device_flow前先检查设备流是否初始化成功,避免后续报错:
if 'error' in flow: print(f"设备流初始化失败: {flow['error']}") exit()
2. 修复main.py的Graph() takes no arguments错误
该错误说明你导入的Graph类没有定义接收参数的构造方法。需要修改graph.py中的Graph类,添加__init__方法来接收azure_settings参数,示例如下:
class Graph: def __init__(self, azure_settings): self.settings = azure_settings # 在这里初始化MSAL客户端或其他需要的资源 # 保留get_user、get_inbox等原有方法的实现逻辑
如果是参考教程中的Graph类,请确保它的构造方法支持传入配置参数,或者检查你是否正确实现了该类。
3. 其他需要修正的问题
- device flow属于公共客户端认证,不需要
CLIENT_SECRET,可以移除demo2.py中的该变量。 - 请求头中Bearer和token之间需要加空格,否则会导致授权失败:
headers={'Authorization': f'Bearer {access_token_id}'} - main.py中
list_users和list_inbox函数内的for循环存在缩进错误,需要调整缩进对齐,否则会触发语法报错。
内容的提问来源于stack exchange,提问作者Sairam Soundararajan
相关产品推荐
相关产品推荐

