Graph API Timeout while validating endpoint

yorick de Jong 5 Reputation points
2024-05-14T16:48:02.25+00:00

Hi guys!

I have been struggling for the past couple of days with setting up a webhook for outlook via the Graph API of microsoft. Initially, I hosted my application locally and exposed it to the internet with ngrok. Connecting to the Graph API worked well. However, when I moved my application to the cloud. I noticed that each time I tried to setup the Graph API webhook for outlook, I received a timeout error. I checked my Azure endpoint with:

curl -X POST https://db.chainfill.bluewaterfall.nl/email-integration/emails?validationToken=someToken -H "Accept: text/plain" -v

and everything worked fine, yielding a 200 Ok response. I also tracked the incomming headers for my application on Azure. When I make a subscription request, the entire backend seems non responsive, as if the graph API request blocks the entire server.

Tim out error:

Starting new HTTPS connection (1): graph.microsoft.com:443 https://graph.microsoft.com:443 "POST /v1.0/subscriptions HTTP/1.1" 400 None Graph API Error: {'error': {'code': 'ValidationError', 'message': 'Subscription validation request timed out.', 'innerError': {'date': '2024-05-14T16:12:13', 'request-id': 'eb76a2e1-95c7-439c-aaca-6efaf031723d', 'client-request-id': 'eb76a2e1-95c7-439c-aaca-6efaf031723d'}}}

Right afte receiving the time out error:

Response for /email-integration/subscription with headers: {'Content-Type': 'application/json', 'Vary': 'Accept, origin', 'Allow': 'POST, OPTIONS', 'X-Frame-Options': 'DENY', 'Content-Length': '42', 'X-Content-Type-Options': 'nosniff', 'Referrer-Policy': 'same-origin', 'Cross-Origin-Opener-Policy': 'same-origin'}

Response Status Code: 200 Response Content Type: application/json Incoming request at /email-integration/emails with headers: {'Host': 'db.chainfill.bluewaterfall.nl', 'X-Real-Ip': '137.135.11.116', 'X-Forwarded-For': '137.135.11.116', 'X-Forwarded-Proto': 'https', 'Connection': 'close', 'Content-Length': '0', 'Client-Request-Id': 'eb76a2e1-95c7-439c-aaca-6efaf031723d', 'Accept': 'text/plain', 'Content-Type': 'text/plain; charset=utf-8'}

Request Path: /email-integration/emails

Request Method: POST

Request Query Params: <QueryDict: {'validationToken': ['Validation: Testing client application reachability for subscription Request-Id: eb76a2e1-95c7-439c-aaca-6efaf031723d']}>

Accept Headers: text/plain Request URL: https://db.chainfill.bluewaterfall.nl/email-integration/emails?validationToken=Validation%3a+Testing+client+application+reachability+for+subscription+Request-Id%3a+eb76a2e1-95c7-439c-aaca-6efaf031723d

validation_token Validation: Testing client application reachability for subscription Request-Id: eb76a2e1-95c7-439c-aaca-6efaf031723d

Received validation token Response for /email-integration/emails with headers: {'Content-Type': 'text/plain', 'X-Frame-Options': 'DENY', 'Content-Length': '117', 'Vary': 'origin', 'X-Content-Type-Options': 'nosniff', 'Referrer-Policy': 'same-origin', 'Cross-Origin-Opener-Policy': 'same-origin'}

Response Status Code: 200 Response Content Type: text/plain

This is the piece of the code that receives the validation post message from graph api:

@method_decorator(csrf_exempt, name='dispatch')
class GetMailsFromOutlookView(View):
    """Fetches an email from outlook when a new email is received"""
    queryset = UserProfile.objects.all()
    serializer_class = UserProfileSerializer

    # Need to secure this route still

    def post(self, request, *args, **kwargs):
        global last_processed
        debounce_period = 30
        try:
            logging.debug(f'Request URL: {request.build_absolute_uri()}')
            # Retrieve the validation token from the query parameters
            validation_token = request.GET.get('validationToken')
            logging.debug(f'validation_token {validation_token}')
            if validation_token:
                logging.debug('Received validation token')
                # Check the Accept header and adjust the content type of the response accordingly
                accept_header = request.headers.get('Accept', 'text/plain')  # Default to text/plain if not specified
                if 'text/plain' in accept_header:
                    return HttpResponse(validation_token, content_type="text/plain", status=200)
                else:
                    return HttpResponse("Unsupported media type", status=415)

I have also tried hosting on aws, but the same problem arrised. The Vm that I'm running is: Standard B2ms (2 vcpus, 8 GiB memory). If somebody encountered a similar problem or knows some steps forward that I can take, it would be greatly appreciated. Thanks a lot!

Backend:

  • Django

Best regards,

Yorick

Outlook
Outlook
A family of Microsoft email and calendar products.
4,128 questions
Microsoft Graph
Microsoft Graph
A Microsoft programmability model that exposes REST APIs and client libraries to access data on Microsoft 365 services.
12,458 questions
0 comments No comments
{count} votes

1 answer

Sort by: Most helpful
  1. Sourabh Gupta 800 Reputation points Microsoft Vendor
    2024-05-19T10:33:48.26+00:00

    Hi yorick de Jong,

    Thanks for reaching out.

    In case your subscription is getting successfully created in local environment using ngrok, then most likely it is an issue related to Ip addresses whitelisting. when you deploy your code, you need to make sure that you don't have any firewall or application gateway rule that is blocking Microsoft from sending you notification request.

    Refer to the following link to get more details

    https://learn.microsoft.com/en-us/graph/change-notifications-delivery-webhooks?tabs=http#firewall-configuration

    The notifications and validation requests are generated from following IP addresses mentioned at the link below

    https://learn.microsoft.com/en-us/microsoft-365/enterprise/additional-office365-ip-addresses-and-urls?view=o365-worldwide

    Refer to row number 23 of the table mentioned on above page. You can get mentioned Ip addresses whitelisted on your firewall.

    However, the listed IP addresses that are used to deliver change notifications can be updated at any time without notice.

    If the answer is helpful, please click "Accept Answer" and kindly upvote it. If you have extra questions about this answer, please click "Comment".

    0 comments No comments

Your answer

Answers can be marked as Accepted Answers by the question author, which helps users to know the answer solved the author's problem.