question

$$ANON_USER$$ avatar image
0 Votes"
$$ANON_USER$$ Deactivated asked $$ANON_USER$$ Deactivated edited

Unauthorized (401) listing synchronization jobs for service principal in MS Graph API

I am trying to use MS Graph API to configure Azure AD Connect Cloud Sync from these [instructions][1] but I am having trouble calling this [endpoint][2] in Powershell using client credentials: https://graph.microsoft.com/beta/servicePrincipals/{SERVICE_PRINCIPAL_ID}/synchronization/jobs I can successfully call this using the Graph Explorer, but no luck using Application permission and authentication with a client secret in Powershell. I get 401 Unauthorized error. I can call other endpoints like: https://graph.microsoft.com/beta/servicePrincipals/{SERVICE_PRINCIPAL_ID} # no /synchronization/jobs at the end The application has the API permission: Directory.ReadWrite.All (Application) The permission has been granted by the admin: ![126191-image.png][3] Below is the detail of the code I use to authenticate: $Body = @{ 'tenant' = $TenantId 'client_id' = $ClientId 'scope' = 'https://graph.microsoft.com/.default' 'client_secret' = $ClientSecret 'grant_type' = 'client_credentials' } $Params = @{ 'Uri' = "https://login.microsoftonline.com/$TenantId/oauth2/v2.0/token" 'Method' = 'Post' 'Body' = $Body 'ContentType' = 'application/x-www-form-urlencoded' } $AuthResponse = Invoke-RestMethod @Params And this is how I call the endpoint: $Headers = @{ 'Authorization' = "Bearer $($AuthResponse.access_token)" } $Params = @{ Uri = "https://graph.microsoft.com/beta/servicePrincipals/{SERVICE_PRINCIPAL_ID}/synchronization/jobs" Method = 'Get' ContentType = 'application/json' Headers = $Headers } $res = Invoke-RestMethod @Params And the error: Invoke-RestMethod : The remote server returned an error: (401) Unauthorized If I use the token from the Graph Explorer it works... My token from Powershell decoded contains this "roles" section but no "scp" like in the Graph Explorer token: "roles": [ "Directory.ReadWrite.All" ], Thank you for your help! [1]: https://docs.microsoft.com/en-us/azure/active-directory/cloud-sync/how-to-inbound-synch-ms-graph [2]: https://docs.microsoft.com/en-us/graph/api/synchronization-synchronizationjob-list?view=graph-rest-beta&tabs=http [3]: /answers/storage/attachments/126191-image.png

azure-active-directorywindows-server-powershellmicrosoft-graph-identity
5 |1600 characters needed characters left characters exceeded

Up to 10 attachments (including images) can be used with a maximum of 3.0 MiB each and 30.0 MiB total.

$$ANON_USER$$ avatar image
1 Vote"
$$ANON_USER$$ Deactivated answered $$ANON_USER$$ Deactivated edited

EDIT: The service principal needs the role "Hybrid Identity Administrator" (or Global administrator) for this to work!

Another way with user creds, ROPC flow (username/password) this user also needs Hybrid Identity Admin role:

$Body = @{
'tenant' = $TenantId
'client_id' = $ClientId
'scope' = 'https://graph.microsoft.com/.default'
'username' = $Username
'password' = $Password
'grant_type' = 'password'
}
$Params = @{
'Uri' = "https://login.microsoftonline.com/$TenantId/oauth2/v2.0/token"
'Method' = 'Post'
'Body' = $Body
'ContentType' = 'application/x-www-form-urlencoded'
}
$AuthResponse = Invoke-RestMethod @Params





5 |1600 characters needed characters left characters exceeded

Up to 10 attachments (including images) can be used with a maximum of 3.0 MiB each and 30.0 MiB total.

AhmedUzejnovic-0950 avatar image
0 Votes"
AhmedUzejnovic-0950 answered $$ANON_USER$$ Deactivated commented

Hi,

i think your permissions for the Application are not right.
Try to ad following to your Application Application --> Application.ReadWrite.OwnedBy

126201-grafik.png


The Graph Explorer generates a token that works because you are logged in as User which work like a delegated permission. (Not 100% sure)

I hope this can help you :)



grafik.png (91.6 KiB)
· 2
5 |1600 characters needed characters left characters exceeded

Up to 10 attachments (including images) can be used with a maximum of 3.0 MiB each and 30.0 MiB total.

Hi! I added the new permission but still no luck...

Below is my new token decoded with some obfuscation:
{
"aud": "https://graph.microsoft.com",
"iss": "https://sts.windows.net/{TENANT_ID}/",
"iat": 1629836586,
"nbf": 1629836586,
"exp": 1629840486,
"aio": "{AIO}",
"app_displayname": "AppForAdConnect2",
"appid": "{APPID}",
"appidacr": "1",
"idp": "https://sts.windows.net/{TENANT_ID}/",
"idtyp": "app",
"oid": "{OID}",
"rh": "{RH}",
"roles": [
"Application.ReadWrite.OwnedBy",
"Directory.ReadWrite.All"
],
"sub": "{SUB}",
"tenant_region_scope": "NA",
"tid": "{TENANT_ID}",
"uti": "{UTI}",
"ver": "1.0",
"wids": [
"{WID}"
],
"xms_tcdt": 1584535155
}

0 Votes 0 ·

Any other idea? Is it even possible to perform this guide using Application "client credential" grant?

https://docs.microsoft.com/en-us/azure/active-directory/cloud-sync/how-to-inbound-synch-ms-graph

0 Votes 0 ·
LimitlessTechnology-2700 avatar image
0 Votes"
LimitlessTechnology-2700 answered KymLu-MSFT commented

Hello anonymous user,
Thank you for your question and reaching out.


The application permission of Microsoft Graph cannot be completely replaced by directory role permissions. They cannot replace each other.

Generally speaking, the permissions of the AAD Graph and the directory role permission have a certain overlap. But Microsoft Graph is not.

Please use Application permission

Application.ReadWrite.OwnedBy

or

Directory.ReadWrite.All

in this case.



If the reply was helpful,please don't forget to upvote or accept as answer.

Thanks,

Aradhya C

· 3
5 |1600 characters needed characters left characters exceeded

Up to 10 attachments (including images) can be used with a maximum of 3.0 MiB each and 30.0 MiB total.

I think I use application permission Directory.ReadWrite.All

. My problem seems different than https://stackoverflow.com/questions/68165380/permissions-required-for-graph-beta-synchronization-endpoint

0 Votes 0 ·

Hello anonymous user

Authorization errors can occur as a result of several different issues, most of which generate a 403 error (with a few exceptions). For example, the following can all lead to authorization errors:

Incorrect access token acquisition flows
Poorly configured permission scopes
Lack of consent
Lack of permissions

Make sure that your application is presenting a valid access token to Microsoft Graph as part of the request. This error often means that the access token may be missing in the HTTP authenticate request header or that the token is invalid or has expired. We strongly recommend that you use the Microsoft Authentication Library (MSAL) for access token acquisition. Additionally, this error may occur, if you try to use a delegated access token granted to a personal Microsoft account, to access an API that only supports work or school accounts (organizational accounts).

Hope this answers all your queries, if not please do repost back.
If an Answer is helpful, please click "Accept Answer" and upvote it : )

Regards,

0 Votes 0 ·

Hi @LimitlessTechnology-2700,

On your post that shares information you found on StackOverflow, could you please change the text to quote and include a link to the post? This way the person who wrote the content gets their work attributed. Or if you prefer, you can delete the post since you provided an updated answer.

Thank you,

--Kym
Microsoft Azure

0 Votes 0 ·
sikumars-msft avatar image
3 Votes"
sikumars-msft answered $$ANON_USER$$ Deactivated edited

Hello anonymous user,

Thanks for reaching out and apologize for inconvenient caused by above issue.

I was able to get it to work by adding following Microsoft Graph API permission Application.ReadWrite.All & Directory.ReadWrite.All for application which I used to authenticate and get Access_token using above PowerShell.

Working API permission on my lab:
126872-image.png

If any of these two permission missing then expected to get (401) Unauthorized same as you. Hope this helps.

126893-image.png


Please "Accept the answer" if the information helped you. This will help us and others in the community as well.


image.png (63.6 KiB)
image.png (35.7 KiB)
· 3
5 |1600 characters needed characters left characters exceeded

Up to 10 attachments (including images) can be used with a maximum of 3.0 MiB each and 30.0 MiB total.

Did you use "client credential" to authenticate? Can I see the full powershell code ? Thanks!

0 Votes 0 ·

Yes, I used Client credential flow, here is PS code for your reference:

Get Access_token using client credential flow

  $TenantId = '*******-6560-4d6a-a352-******'
  $ClientId = '8069ed17-****-43ed-***-0a1b9bc32447'
  $ClientSecret = '***_0dgK7**H9-***.y28eRh3~**-I28'
    
  $Body = @{
      'tenant' = $TenantId
      'client_id' = $ClientId
       'scope' = 'https://graph.microsoft.com/.default'
      'client_secret' = $ClientSecret
      'grant_type' = 'client_credentials'
  }
  $Params = @{
      'Uri' = "https://login.microsoftonline.com/$TenantId/oauth2/v2.0/token"
      'Method' = 'Post'
      'Body' = $Body
      'ContentType' = 'application/x-www-form-urlencoded'
   }
  $AuthResponse = Invoke-RestMethod @Params

To call Graph API with Access_token

  $Headers = @{
      'Authorization' = "Bearer $($AuthResponse.access_token)"
  }
  $Params = @{
       Uri = "https://graph.microsoft.com/beta/servicePrincipals/00110cb2-9814-4b5d-b6a3-9e457c589a5d/synchronization/jobs"
       Method = 'Get'
       ContentType =  'application/json'
       Headers     = $Headers
   }
  $res = Invoke-RestMethod @Params
0 Votes 0 ·

Thank you, I used exactly the same code and still the same problem!

Do you create the service principal with https://graph.microsoft.com/beta/applicationTemplates/1a4721b3-e57f-4451-ae87-ef078703ec94/instantiate ? Such as described in https://docs.microsoft.com/en-us/azure/active-directory/cloud-sync/how-to-inbound-synch-ms-graph

Is there anything else I need to change at the subscription level?

edit: I am seeing some inconsistent behavior now... I am trying to call this endpoint with another service principal for example " "appDisplayName": "Office 365 Configure" " and I get a result, but when I try again with my app I get 401, then I try again with the Office 365 id and I get 401 this time!! But waiting 15mn and calling again the Office 365 id work (until I try to call again my app id!)

What is going on here?






0 Votes 0 ·
sikumars-msft avatar image
2 Votes"
sikumars-msft answered shashishailaj rolled back

I guess you are using same service principal in PowerShell that was created using instantiate template, if that's the case then (401) Unauthorized error is expected, because these service principal represent cloud sync Configuration which would be used as service account and only limited for synchronization engine.

Therefore, you should not use them for getting access_token for administration purpose instead create a new App registration or use existing one from Azure AD which should work in client credential flow without any issue.

For an example: I created two service principal (lets say Sync1 & Sync2) using following template ( https://graph.microsoft.com/beta/applicationTemplates/1a4721b3-e57f-4451-ae87-ef078703ec94/instantiate ) as described here which represent cloud sync Configuration and this can be verified by going to Azure AD connect under Azure AD blade as shown below.

127198-image.png

For administration purpose, I created New App registration (lets say myapp) and used them in my PowerShell script to get access_token and perform all API call to https://graph.microsoft.com/beta/servicePrincipals/{SOME_ID}/synchronization/jobs endpoint.

Create App Registration
127252-image.png

Get Access_token

127199-image.png


Get API call to /synchronization/jobs endpoint

127253-image.png

Hope this helps.



image.png (111.1 KiB)
image.png (125.8 KiB)
image.png (33.7 KiB)
image.png (143.1 KiB)
· 1
5 |1600 characters needed characters left characters exceeded

Up to 10 attachments (including images) can be used with a maximum of 3.0 MiB each and 30.0 MiB total.

This is the most detailed answer thank you!

However I really must be missing something because this is exactly what I am doing:
- Create a "myapp" just like you did exactly! with the API permissions and client secret, nothing else changed.
- Using the client secret from "myapp" I instantiate the service principal Sync1
- Using the client secret from "myapp" I try to call /{Sync1_serviceprincipalID}synchronization/jobs --> 401!

So I am not using a secret from Sync1, it didn't even create one, I didn't change anything in this app Sync1.


0 Votes 0 ·