azuremicrosoft-graph-api

Get eDiscovery cases as application


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


Solution

  • Initially, I registered one application and granted eDiscovery.ReadWrite.All permission of Application type with consent:

    enter image description here

    When I ran the python code to list eDiscovery cases with token generated with service principal, I too got same error:

    enter image description here

    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:

    enter image description here

    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:

    enter image description here

    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:

    enter image description here

    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:

    enter image description here

    Reference:

    Set up app-only access for Microsoft Purview eDiscovery by using Microsoft Graph APIs