"""Add Mail.Send permission to the app registration and grant admin consent.""" import json import sys import urllib.request import urllib.parse import urllib.error import ssl TENANT_ID = "5c53ae9f-7071-4248-b834-8685b646450f" APP_ID = "fabb3421-8b34-484b-bc17-e46de9703418" APP_SECRET = "~QJ8Q~NyQSs4OcGqHZyPrA2CVnq9KBfKiimntbMO" GRAPH_APP_ID = "00000003-0000-0000-c000-000000000000" # Microsoft Graph ctx = ssl.create_default_context() def get_token(): data = urllib.parse.urlencode({ "client_id": APP_ID, "scope": "https://graph.microsoft.com/.default", "client_secret": APP_SECRET, "grant_type": "client_credentials", }).encode() req = urllib.request.Request( f"https://login.microsoftonline.com/{TENANT_ID}/oauth2/v2.0/token", data=data, method="POST" ) with urllib.request.urlopen(req, context=ctx) as resp: return json.loads(resp.read())["access_token"] def graph_get(token, url): req = urllib.request.Request(url, headers={"Authorization": f"Bearer {token}"}) with urllib.request.urlopen(req, context=ctx) as resp: return json.loads(resp.read()) def graph_post(token, url, payload=None): headers = {"Authorization": f"Bearer {token}"} body = None if payload: headers["Content-Type"] = "application/json" body = json.dumps(payload).encode() else: body = b"" req = urllib.request.Request(url, data=body, headers=headers, method="POST") try: with urllib.request.urlopen(req, context=ctx) as resp: content = resp.read() return json.loads(content) if content else {} except urllib.error.HTTPError as e: body = e.read().decode() try: return json.loads(body) except: return {"error": {"message": body, "code": str(e.code)}} token = get_token() print("[OK] Token acquired") # Find our app's service principal filter_val = urllib.parse.quote(f"appId eq '{APP_ID}'") url = f"https://graph.microsoft.com/v1.0/servicePrincipals?$filter={filter_val}" data = graph_get(token, url) if not data.get("value"): print("[ERROR] Could not find our service principal") print(json.dumps(data, indent=2)[:1000]) sys.exit(1) our_sp_id = data["value"][0]["id"] print(f"[OK] Our SP ID: {our_sp_id}") # Find Microsoft Graph service principal filter_val2 = urllib.parse.quote(f"appId eq '{GRAPH_APP_ID}'") url2 = f"https://graph.microsoft.com/v1.0/servicePrincipals?$filter={filter_val2}" data2 = graph_get(token, url2) graph_sp_id = data2["value"][0]["id"] print(f"[OK] Graph SP ID: {graph_sp_id}") # Find Mail.Send role app_roles = data2["value"][0].get("appRoles", []) mail_send_id = None for role in app_roles: if role.get("value") == "Mail.Send": mail_send_id = role["id"] break if not mail_send_id: print("[ERROR] Mail.Send role not found") sys.exit(1) print(f"[OK] Mail.Send role ID: {mail_send_id}") # Grant the appRoleAssignment (admin consent) payload = { "principalId": our_sp_id, "resourceId": graph_sp_id, "appRoleId": mail_send_id } url3 = f"https://graph.microsoft.com/v1.0/servicePrincipals/{our_sp_id}/appRoleAssignments" result = graph_post(token, url3, payload) if result.get("id"): print(f"[SUCCESS] Mail.Send permission granted! Assignment ID: {result['id']}") elif "already exists" in str(result.get("error", {}).get("message", "")): print("[INFO] Mail.Send permission already assigned") else: print(f"[RESULT] Response: {json.dumps(result, indent=2)[:1000]}")