Microsoft Graph API and Azure Active Directory Provider

Hello,
I am trying to connect to the Microsoft Graph API. (For reference I am using this doc: How to build insightful M365 Analytics Dashboards with SquaredUp and Microsoft Graph API (Part 1) | SquaredUp and How to build insightful M365 Analytics Dashboards with SquaredUp and Microsoft Graph API (Part 2) | SquaredUp)

I have created an app in Azure with the following permissions:

I have also added the Azure Active Directory provider which is showing as green.

The problem is when I try to configure the web api (line graph)tile I am receiving:
The API returned HTTP 403 status code

{

“error”: {

"code": "UnknownError",

"message": "{\"error\":{\"code\":\"S2SUnauthorized\",\"message\":\"Invalid permission.\"}}" S2SUnauthorized

For the life me I can’t figure out what permission is missing.

Any thoughts?

Hey Terry, what data are you trying to pull back?

Ah it could be that you’ve got a delegated permission mixed in with your others. As we use authorization code flow to grant access to Azure AD, any delegated permission will take precedence over the application permissions.

Your app is likely being authorised with just that basic “User.Read” permission and ignoring the higher application scopes.

Could you try dropping that delegated permission so that only application permissions exist, and then reauthorise the provider in SquaredUp?

OR, give the necessary permissions via the delegated route.

No joy, I removed the User.read (I believe that one was there by default).
I also tried adding User.Read.All with Type Application.

Still seeing the same error.

Hi @tingerson ,

one good step in trouble shooting here is to use PowerShell to see if you can get the information expected.

Please try:

$clientId = “YOUR Client ID”
$tenantId = “YOUR Tenant ID”
$clientSecret = ‘YOUR Client Secret’

$uri = “Sign in to your account”

$body = @{
client_id = $clientId
scope = “https://graph.microsoft.com/.default”
client_secret = $clientSecret
grant_type = “client_credentials”
}

$tokenRequest = Invoke-WebRequest -Method Post -Uri $uri -ContentType >“application/x-www-form-urlencoded” -Body $body -UseBasicParsing

$token = ($tokenRequest.Content | ConvertFrom-Json).access_token

$uri = “https://graph.microsoft.com/beta/reports/getSharePointSiteUsageSiteCounts(period=‘D30’)?`$format=application/json”

$query = Invoke-RestMethod -Method Get -Uri $uri -ContentType “application/json” -Headers @{Authorization = “Bearer $token”} -ErrorAction Stop

$query.value

Please share if this returns any result in your desired query.

This does work as expected:
$clientSecret = ‘My app secret’
$clientId = ‘my client id’
$tenantId = ‘my tenant id’

Construct URI

$uri = “Sign in to your account”

Construct Body

$body = @{
client_id = $clientId
client_secret = $clientSecret
scope = ‘https://graph.microsoft.com/.default’
grant_type = ‘client_credentials’
}

Get OAuth 2.0 Token

$tokenRequest = Invoke-WebRequest -Method Post -Uri $uri -ContentType ‘application/x-www-form-urlencoded’ -Body $body -UseBasicParsing

Access Token

$token = ($tokenRequest.Content | ConvertFrom-Json).access_token
$uri = “https://graph.microsoft.com/beta/reports/getMailboxUsageMailboxCounts(period=‘D30’)?$format=application/json”
$query = Invoke-RestMethod -Method Get -Uri $uri -ContentType “application/json” -Headers @{Authorization = “Bearer $token”} -ErrorAction Stop
$query

It almost is if the web api isn’t passing on the provider info and/or not sending the authorization. Should there be a header with the web api request?

Also for anyone following this the “Sign into your account” is https://login.microsoftonline.com/$tenantId/oauth2/v2.0/token it gets converted in this forum…

Hey Terry,

I’m not 100% sure what the underlying issue is, yet, but I’ve only been able to successfully access the Graph /reports endpoint using Reports.Read.All as a delegated permission. And I tried with both the Azure AD provider type, and also the standard WebAPI > Oauth type. In all cases I was getting the same S2SUnauthorized error message.

I did come across a few bug reports on GitHub for application permissions in this context but no fixes or workarounds.

Note that the PS sample above is using the client_credentials Oauth flow, not the authorization_code flow that our Azure AD provider uses, so that test above isn’t strictly a like-for-like comparison. That being said, using the WebAPI > Oauth provider with either of those flows gives the same outcome so there’s likely some additional investigation needed on our side.

That script above can be used with our new PowerShell tiles though, so if app permissions are a must, try using the PS tiles as follows…

Create a profile under System > PowerShell containing

$clientSecret = ‘client-secret’
$clientId = ‘client-id’
$tenantId = ‘tenant-id’
$uri = "https://login.microsoftonline.com/$tenantId/oauth2/v2.0/token"
$body = @{
client_id = $clientId
client_secret = $clientSecret
scope = ‘https://graph.microsoft.com/.default’
grant_type = ‘client_credentials’
}
$tokenRequest = Invoke-WebRequest -Method Post -Uri $uri -ContentType ‘application/x-www-form-urlencoded’ -Body $body -UseBasicParsing
$token = ($tokenRequest.Content | ConvertFrom-Json).access_token

Then add the last part to the tile of your choice to pull back data.

$uri = “https://graph.microsoft.com/beta/reports/getMailboxUsageMailboxCounts(period='D30')?$format=application/json”
$query = Invoke-RestMethod -Method Get -Uri $uri -ContentType “application/json” -Headers @{Authorization = “Bearer $token”} -ErrorAction Stop
$query

Note you’ll likely need to add some additional manipulation to your script to format the contents of $query in a useful way i.e. to create readable timestamps for line graphs, multiple columns or groupings for grids/donuts etc.

Delegated permissions will solve this for you, but if that’s not acceptable, PowerShell for the :trophy:

Thanks Adam,

Changing it to Delegated fixed the permission issue. So I assume anyone who looks at the dashboard in squaredup would need the appropriate rights in azure?

No its purely based on you and your rights in this context. The provider is always using the scope and permissions you granted it when authorizing. The subsequent use of that provider doesn’t take the dashboard viewer’s identity into account.

SquaredUp for Azure’s native Azure Monitor tiles take the logged in user into account, in the same way your SCOM role is taken into account when viewing SCOM data in SquaredUp for SCOM, but WebAPI providers are authorised and then act based on the user who authorized them.

OK, So the provider is using the credentials I used when granting. So going forward it will continue to use those creds unless I re-authorize the provider with a different account?

In effect, yes. The provider is always acting on your behalf using its own client ID and secret to gain an access token with the scope it’s allowed. The provider can only be used to take the actions granted in the Azure Portal though i.e. Reports.Read.All, User.Read.All etc, it doesn’t magically gain your full set of rights because you clicked the Authorize button. It can never act outside of the allowed/authorized scope, and it can never act with higher priviledge than the user who authorized it.

You’ve basically said it’s OK for it to do what it needs cause you say so :slight_smile:

Note that that last part is where the “Grant admin consent for…” option in the Azure Portal comes in. Delegated permissions always need admin consent to be given first, but once that’s given, any other user with suitable permissions can authorize the provider in SquaredUp and it would then act on their behalf.

(In this specfic case, with admin consent granted, any Azure AD user could now authorize this provider, but it’ll only pull back data from the /reports endpoint if that user is either an admin, or has the Reports Reader role in Azure AD. Even though the provider has the Reports.Read.All scope, if the authorizing user isn’t allowed to read reports, the provider acting on their behalf will be stuck too)

@tingerson Could you post an updated screenshot of your new permissions? Just like you had at the top of this question. Thank you!

Sure thing, you can ignore the one for auditlog.read.all for this example. I’m testing out pulling some sign-in data for a different report:

@tingerson Thank you! Would you mind showing me the ad role you needed as well

If I understand Adam above correctly, once consent is granted in Azure, any user that has at least the Reports Reader Role in AzureAD can authorize the provider in SquaredUP.

  • User delegated authorization - Allows an app to read all service usage reports on behalf of the signed-in user. In addition to the app having been granted the required permissions, the user must be a member of an Azure AD limited administrator role. This can be one of the following roles: company administrator, Exchange administrator, SharePoint administrator, Lync administrator, Teams Service Administrator, Teams Communications Administrator, global reader, or reports reader.
1 Like

Just for completeness for any future readers, in the updated screenshot from Terry showing the app registration’s permissions, the delegated permissions always take precendence over application permissions when using our Azure AD provider (due to the underlying Oauth flow in use), so the effective scope when authorizing in this case would simply be Reports.Read.All even though there are a bunch of other permissions mixed in there.