AADSTS900144: The request body must contain the following parameter: 'grant_type' with Microsoft Defender for Endpoint API

Question:

I am attempting to authenticate with Microsoft Defender for Endpoint’s API service by following this learn article:

https://learn.microsoft.com/en-us/microsoft-365/security/defender-endpoint/run-advanced-query-sample-python?view=o365-worldwide#get-token

I typically use the "request" library for REST calls, so I didn’t follow the above code snippet exactly. When running my version of the above code:

import json

import requests


MDE_CLIENT_ID = 'XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXX'
MDE_CLIENT_SECRET = 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX'
TENANT_ID = 'XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXX'
AUTHORITY = 'https://login.microsoftonline.com/'
MDE_URI = 'https://api.securitycenter.microsoft.com'


class RESTError(Exception):
    def __init__(self, status_code, message):
        self.status_code = status_code
        self.message = str(self.status_code) + ' ' + json.dumps(message)
        super().__init__(self.message)

def authenticate_mde():
    headers = {
        'content-type': 'application/x-www-form-urlencoded'
    }
    body = {
        'resource': MDE_URI,
        'client_id': MDE_CLIENT_ID,
        'client_secret': MDE_CLIENT_SECRET,
        'grant_type': 'client_credentials'
    }
    response = requests.post(AUTHORITY + TENANT_ID + '/oauth2/token', data = json.dumps(body), headers = headers)
    
    if (response.status_code < 200 or response.status_code > 299):
        raise RESTError(response.status_code, response.json())

    return response.json()['access_token']


def main():
    token = authenticate_mde()
    print(token)

if (__name__ == '__main__'):
    main()

When I run this code I receive a 400 error back from the authentication service complaining about a missing body parameter ‘grant_type’. However, as you can see in the code, I clearly have that included in the same fashion as the code snippet from MSFT.

Traceback (most recent call last):
  File "C:Users24724Documentscodepythonscriptsmde-executor.py", line 42, in <module>
    main()
  File "C:Users24724Documentscodepythonscriptsmde-executor.py", line 38, in main
    token = authenticate_mde()
  File "C:Users24724Documentscodepythonscriptsmde-executor.py", line 32, in authenticate_mde
    raise RESTError(response.status_code, response.json())
__main__.RESTError: 400 {"error": "invalid_request", "error_description": "AADSTS900144: The request body must contain the following parameter: 'grant_type'.rnTrace ID: e4d0d06e-aae6-4b6d-80e2-2b3997f74302rnCorrelation ID: 5788089d-f94e-4e9a-8667-d6e36c183af8rnTimestamp: 2023-01-06 17:00:23Z", "error_codes": [900144], "timestamp": "2023-01-06 17:00:23Z", "trace_id": "e4d0d06e-aae6-4b6d-80e2-2b3997f74302", "correlation_id": "5788089d-f94e-4e9a-8667-d6e36c183af8", "error_uri": "https://login.microsoftonline.com/error?code=900144"}

I also tried copying MSFT’s code snippet exactly and inserting my own global var info but receive the same error. I have tried moving the body to url parameters, headers, splitting it up between body, params, and headers. No luck. I have tried different content-types in the header as well and tried without any headers. None seems to work and I am stumped at this point.

Asked By: Matt M

||

Answers:

I resolved the issue. Passing ‘resource’ into the body was apparently screwing it up, even though their python example here shows that:

import json
import urllib.request
import urllib.parse

tenantId = '00000000-0000-0000-0000-000000000000' # Paste your own tenant ID here
appId = '11111111-1111-1111-1111-111111111111' # Paste your own app ID here
appSecret = '22222222-2222-2222-2222-222222222222' # Paste your own app secret here

url = "https://login.microsoftonline.com/%s/oauth2/token" % (tenantId)

resourceAppIdUri = 'https://api.securitycenter.microsoft.com'

body = {
    'resource' : resourceAppIdUri,
    'client_id' : appId,
    'client_secret' : appSecret,
    'grant_type' : 'client_credentials'
}

data = urllib.parse.urlencode(body).encode("utf-8")

req = urllib.request.Request(url, data)
response = urllib.request.urlopen(req)
jsonResponse = json.loads(response.read())
aadToken = jsonResponse["access_token"]

https://learn.microsoft.com/en-us/microsoft-365/security/defender-endpoint/run-advanced-query-sample-python?view=o365-worldwide#get-token

Following the example they give for cURL here and using the ‘scope’ parameter instead fixed it.

curl -i -X POST -H "Content-Type:application/x-www-form-urlencoded" -d "grant_type=client_credentials" -d "client_id=%CLIENT_ID%" -d "scope=https://securitycenter.onmicrosoft.com/windowsatpservice/.default" -d "client_secret=%CLIENT_SECRET%" "https://login.microsoftonline.com/%TENANT_ID%/oauth2/v2.0/token" -k

https://learn.microsoft.com/en-us/microsoft-365/security/defender-endpoint/exposed-apis-create-app-webapp?view=o365-worldwide#use-curl

Answered By: Matt M
Categories: questions Tags: , , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.