Search code examples
azureazure-active-directorymicrosoft-graph-api

Python Graph API Send Email No suitable token exists in cache and Access Denied


I am using below code to send email using microsoft graph api credentials getting below mentioned errors , from postman email is being sent successfully. Not sure what is missing.

import json
import msal
import requests

client_id = '6c762**************'
client_secret = 'F-S**************************'
tenant_id = 'e*****************************'
authority = f"https://login.microsoftonline.com/{tenant_id}"

app = msal.ConfidentialClientApplication(
    client_id=client_id,
    client_credential=client_secret,
    authority=authority)

scopes = ["https://graph.microsoft.com/.default"]

result = None
result = app.acquire_token_silent(scopes, account=None)

if not result:
    print(
        "No suitable token exists in cache. Let's get a new one from Azure Active Directory.")
    result = app.acquire_token_for_client(scopes=scopes)

# if "access_token" in result:
#     print("Access token is " + result["access_token"])


if "access_token" in result:
    userId = "support@****.com"
    endpoint = f'https://graph.microsoft.com/v1.0/users/{userId}/sendMail'
    toUserEmail = "hi****@gmail.com"

    email_msg = {'Message': {'Subject': "Test Sending Email from Python",
                             'Body': {'ContentType': 'Text', 'Content': "This is a test email."},
                             'ToRecipients': [{'EmailAddress': {'Address': toUserEmail}}]
                             },
                 'SaveToSentItems': 'true'}
    r = requests.post(endpoint,
                      headers={'Authorization': 'Bearer ' + result['access_token']}, json=email_msg)
    if r.ok:
        print('Sent email successfully')
    else:
        print(r.json())
else:
    print(result.get("error"))
    print(result.get("error_description"))
    print(result.get("correlation_id"))

Below Are Error Detail :-

No suitable token exists in cache. Let's get a new one from Azure Active Directory. {'error': {'code': 'ErrorAccessDenied', 'message': 'Access is denied. Check credentials and try again.'}}


Solution

  • Initially, I too got same error when I ran your code in my environment by granting Mail.Send permission of Delegated type:

    enter image description here

    To resolve the error, make sure to grant Mail.Send permission of Application type as you are using client credentials flow that generates app-only token.

    enter image description here

    When I ran the same code again after granting permission of Application type, I got the response like this:

    import json
    import msal
    import requests
    
    client_id = 'appID'
    client_secret = 'secret'
    tenant_id = 'tenantId'
    authority = f"https://login.microsoftonline.com/{tenant_id}"
    
    app = msal.ConfidentialClientApplication(
        client_id=client_id,
        client_credential=client_secret,
        authority=authority)
    
    scopes = ["https://graph.microsoft.com/.default"]
    
    result = None
    result = app.acquire_token_silent(scopes, account=None)
    
    if not result:
        print(
            "No suitable token exists in cache. Let's get a new one from Azure Active Directory.")
        result = app.acquire_token_for_client(scopes=scopes)
    
    # if "access_token" in result:
    #     print("Access token is " + result["access_token"])
    
    
    if "access_token" in result:
        userId = "[email protected]"
        endpoint = f'https://graph.microsoft.com/v1.0/users/{userId}/sendMail'
        toUserEmail = "[email protected]"
    
        email_msg = {'Message': {'Subject': "Test Sending Email from Python",
                                 'Body': {'ContentType': 'Text', 'Content': "This is a test email."},
                                 'ToRecipients': [{'EmailAddress': {'Address': toUserEmail}}]
                                 },
                     'SaveToSentItems': 'true'}
        r = requests.post(endpoint,
                          headers={'Authorization': 'Bearer ' + result['access_token']}, json=email_msg)
        if r.ok:
            print('Sent email successfully')
        else:
            print(r.json())
    else:
        print(result.get("error"))
        print(result.get("error_description"))
        print(result.get("correlation_id"))
    

    Response:

    enter image description here

    To confirm that, I checked the same in Sent Items of users where email sent successfully as below:

    enter image description here