I'm using Microsoft Graph Python SDK to access user license information. Here is the code line:
license_details = await graph_client.users.by_user_id(user_id).license_details.get()
However, this keep throwing Authorization Error with following response:
APIError
Code: 403
message: None
error: MainError(additional_data={}, code='Authorization_RequestDenied', details=None, inner_error=InnerError(additional_data={}, client_request_id='a0dc5684-d627-477b-af2c-1d264039e6de', date=DateTime(2024, 11, 13, 5, 59, 28, tzinfo=Timezone('UTC')), odata_type=None, request_id='f503re17-615b-46f4-afce-fc02bda244a2'), message='Insufficient privileges to complete the operation.', target=None)
My application has all necessary permissions like User.Read.All and Directory.Read.All with admin consent granted and correct credentials are used. Despite this, the error still occcurs.
The goal is to retrieve user’s license details, but instead, the 403 authorization error is being returned. Are there additional permissions required to access license details? Could there be an issue with Azure AD app configuration?
Thanks in advance for any help!
Note that, licenseDetails
API endpoint does not support permissions of Application type. You can refer this MS Document.
Initially, I too got same error when I tried to access user's license information with client credentials flow granting Application type permissions:
To resolve the error, switch to delegated flows like interactive or authorization code flow by granting LicenseAssignment.ReadWrite.All
permission of Delegated type as below:
In my case, I used interactive flow for which redirect URI should be http://localhost in Mobile & Desktop applications platform with public client flow enabled:
When I ran below sample code and signed in with admin account, I got response with user's license information successfully as below:
import asyncio
from azure.identity import InteractiveBrowserCredential
from msgraph import GraphServiceClient
CLIENT_ID = 'appID'
TENANT_ID = 'tenantID'
SCOPES = ['LicenseAssignment.ReadWrite.All']
credential = InteractiveBrowserCredential(
client_id=CLIENT_ID,
tenant_id=TENANT_ID
)
graph_client = GraphServiceClient(credential)
async def get_user_license_details(user_id):
try:
license_details_response = await graph_client.users.by_user_id(user_id).license_details.get()
license_details = license_details_response.value
if license_details:
print(f"License details for user {user_id}:")
for license in license_details:
print(f"License SKU ID: {license.sku_id}")
print(f"SKU Part Number: {license.sku_part_number}")
if license.service_plans:
print("Assigned Service Plans:")
for plan in license.service_plans:
print(f" Service Plan ID: {plan.service_plan_id}")
print(f" Service Plan Name: {plan.service_plan_name}")
print(f" Provisioning Status: {plan.provisioning_status}")
print(f" Applies to: {plan.applies_to}")
else:
print("No assigned service plans found.")
else:
print(f"No license details found for user {user_id}.")
except Exception as e:
print(f"Error retrieving license details: {e}")
async def main():
user_id = 'userID'
await get_user_license_details(user_id)
asyncio.run(main())
Response: