I'm trying to automate purges.
I've created an application, given it the Application right eDiscovery.ReadWrite.All
, and given consent.
I've created an oauth2 access token, and verified that the scope is present in the JWT token.
However, when I call https://graph.microsoft.com/v1.0/security/cases/ediscoveryCases with the Authorization: Bearer {accesstoken}
I get a 401 error.
According to the documentation that I found (https://learn.microsoft.com/en-us/graph/api/security-casesroot-list-ediscoverycases?view=graph-rest-1.0&tabs=http) the application right should be enough to access this endpoint.
Am I missing something? Do I need to give my app additional rights? Or does (despite what the documentation says) application access not work, and do I need to use delegated permissions?
Code (Python):
Get access token using tenant, app and secret (obviously not given here), and create the header:
body = { "client_id": app, "scope": "https://graph.microsoft.com/.default", "grant_type": "client_credentials", "client_secret": secret }
token = requests.post("https://login.microsoftonline.com/{}/oauth2/v2.0/token".format(tenant), data=body).json()['access_token']
header = { 'Authorization': "Bearer {}".format(token) }
Now try a call to users:
users = requests.get('https://graph.microsoft.com/v1.0/users', headers=header).json()['value']
len(users)
This results in 100
. So the token works.
Let's look at the JWT token:
decoded = jwt.decode(token, options={'verify_signature': False})['roles']
pprint.pprint(decoded)
This gives a list of roles:
[
...
'eDiscovery.ReadWrite.All',
...
]
The eDiscovery role is in there.
But when I try to get the ediscovery cases:
requests.get('https://graph.microsoft.com/v1.0/security/cases/ediscoveryCases', headers=header)
<Response [401]>
Joost
Initially, I registered one application and granted eDiscovery.ReadWrite.All
permission of Application type with consent:
When I ran the python code to list eDiscovery cases with token generated with service principal, I too got same error:
To resolve the error, you need make use of ExchangeOnlineManagement
PowerShell module for creating service principal and assigning role to it.
Before creating service principal, get the object ID and application ID of your registered application from Enterprise Applications tab:
Now, install ExchangeOnlineManagement
module and create service principal by connecting to it with below PowerShell commands:
Install-Module ExchangeOnlineManagement
Import-Module ExchangeOnlineManagement
Connect-IPPSSession
New-ServicePrincipal -AppId "spAppId" -ObjectId "spObjectId" -DisplayName "DiscoveryApp"
Get-ServicePrincipal
Response:
Now, run below PowerShell commands to assign eDiscoveryManager and eDiscoveryAdministrator roles to the service principal:
Add-RoleGroupMember -Identity "eDiscoveryManager" -Member "spObjectId"
Get-RoleGroupMember -Identity "eDiscoveryManager"
Add-eDiscoveryCaseAdmin -User "spObjectId"
Get-eDiscoveryCaseAdmin
Response:
When I ran the python code again after assigning roles to service principal, I got the response successfully like this:
import requests
import json
import jwt
TENANT_ID = "tenantId"
CLIENT_ID = "appId"
CLIENT_SECRET = "secret"
TOKEN_URL = f"https://login.microsoftonline.com/{TENANT_ID}/oauth2/v2.0/token"
def get_access_token():
body = {
"client_id": CLIENT_ID,
"scope": "https://graph.microsoft.com/.default",
"grant_type": "client_credentials",
"client_secret": CLIENT_SECRET
}
response = requests.post(TOKEN_URL, data=body)
if response.status_code == 200:
return response.json()["access_token"]
else:
print(f"Error fetching token: {response.status_code} - {response.text}")
return None
def decode_jwt(token):
decoded_token = jwt.decode(token, options={"verify_signature": False})
roles = decoded_token.get("roles", [])
print("\nAssigned Roles in JWT Token:")
print(roles)
print()
return roles
def get_ediscovery_cases(access_token):
url = "https://graph.microsoft.com/v1.0/security/cases/ediscoveryCases"
headers = {
"Authorization": f"Bearer {access_token}",
"Content-Type": "application/json"
}
response = requests.get(url, headers=headers)
print(response)
print(response.text)
if __name__ == "__main__":
access_token = get_access_token()
if access_token:
decode_jwt(access_token)
get_ediscovery_cases(access_token)
Response:
Reference:
Set up app-only access for Microsoft Purview eDiscovery by using Microsoft Graph APIs