Microsoft Graph API - app in powershell - forbidden response - powershell

I am attempting to make a Microsoft Graph API call using powershell 5.1. I have Registred the app. I use the AppID and secret to obtain an Authorization Bearer token. I then construct my API request. The security/events endpoint only returns a Forbidden response.
API Permissions Granted
My script to make the call looks like this:
# Define AppId, secret and scope, your tenant name and endpoint URL
$AppId = [myAPPID]
$AppSecret = [myAPPSecret]
$Scope = "https://graph.microsoft.com/.default"
$TenantName = "[mytenant].onmicrosoft.com"
$Url = "https://login.microsoftonline.com/$TenantName/oauth2/v2.0/token"
# Add System.Web for urlencode
Add-Type -AssemblyName System.Web
$Body = #{
client_id = $AppId
client_secret = $AppSecret
scope = $Scope
grant_type = 'client_credentials'
}
# Splat the parameters for Invoke-Restmethod for cleaner code
$PostSplat = #{
ContentType = 'application/x-www-form-urlencoded'
Method = 'POST'
# Create string by joining bodylist with '&'
Body = $Body
Uri = $Url
}
$Request = Invoke-RestMethod #PostSplat
$Header = #{
Authorization = "$($Request.token_type) $($Request.access_token)"
}
$Uri = "https://graph.microsoft.com/v1.0/security/events"
$SecurityAlertsRequest = Invoke-RestMethod -Uri $Uri -Headers $Header -Method Get -ContentType "application/json"
Just looking to see If I have missed something obvious. Is there any other reason this would response with Forbidden for this configuration?

You have granted delegated permissions to the app.
Those only apply when there is a user signed in to your app and you acquire an access token to act on their behalf.
You need to grant application permissions to your app in Azure AD.

Related

How to retrieve a specific SharePoint online site using graph api and PowerShell

Am currently creating a PowerShell script to retrieve a specific SharePoint online site using Microsoft graph api. The goal is, once i retrieve the site, then i can grab the siteid. My script fails on the api call. Have tried different search and filter combination, but it's not working. All of my api calls just retrieves all the sites. I got all required permissions assigned to the app registration.
Below is the entire script.
$siteUrl = "https://bernardcomms.sharepoint.com/sites/Project1"
$tenantId = ""
$clientId = ""
$clientSecret = ""
# Get an access token for the Microsoft Graph API
$tokenAuthUri = "https://login.microsoftonline.com/$tenantId/oauth2/v2.0/token"
$tokenRequestBody = #{
grant_type = "client_credentials"
client_id = $clientId
client_secret = $clientSecret
scope = "https://graph.microsoft.com/.default"
}
$tokenResponse = Invoke-RestMethod -Method Post -Uri $tokenAuthUri -Body $tokenRequestBody
$accessToken = $tokenResponse.access_token
# Get the ID of the SharePoint site
$requesturi = "https://graph.microsoft.com/v1.0/sites?$search=$siteUrl"
$siteIdResponse = Invoke-RestMethod -Method Get -Uri $requesturi -Headers #{Authorization=("bearer {0}" -f $accessToken)}
$siteId = $siteIdResponse.value.id
$siteId
Have tried to use below calls but it just returns all the sites.
$requesturi = "https://graph.microsoft.com/v1.0/sites?$search=weburl eq '$siteUrl'"
$requesturi = "https://graph.microsoft.com/v1.0/sites?$filter=weburl eq '$siteUrl'"
How can I retrieve a site that has a matching specific siteurl.
permissions assigned.
I agree with #Cpt.Whale, you need to change your graph query to https://graph.microsoft.com/v1.0/sites/bernardcomms.sharepoint.com:/sites/Project1
I tried to reproduce the same in my environment and got below results:
I registered one Azure AD application and granted same API permissions like below:
I have one SharePoint site named sritestsite like below:
When I ran below PowerShell script by making few changes, I got siteID successfully like below:
$hostName = "mytenant.sharepoint.com"
$siteName = "sitename"
$tenantId = <tenantID>
$clientId = <appID>
$clientSecret = <secret>
# Get an access token for the Microsoft Graph API
$tokenAuthUri = "https://login.microsoftonline.com/$tenantId/oauth2/v2.0/token"
$tokenRequestBody = #{
grant_type = "client_credentials"
client_id = $clientId
client_secret = $clientSecret
scope = "https://graph.microsoft.com/.default"
}
$tokenResponse = Invoke-RestMethod -Method Post -Uri $tokenAuthUri -Body $tokenRequestBody
$accessToken = $tokenResponse.access_token
# Get the ID of the SharePoint site
$requesturi = "https://graph.microsoft.com/v1.0/sites/$hostName`:/sites/$siteName"
$siteIdResponse = Invoke-RestMethod -Method Get -Uri $requesturi -Headers #{Authorization=("bearer {0}" -f $accessToken)}
$siteId = $siteIdResponse.id
$siteId
Response:
To get full response, you can run $siteIdResponse like below:

Microsoft Graph API Bearer Token expires after an hour, can I extend it?

I have a PowerShell script that uploads data to a SharePoint site, however, the script needs more than an hour to upload all the data. Is there anyway to increase the tokens life span. Below is the call I use to get the Bearer token
$TenantId = "T123"
$ClientId = "C123"
$Secret = "S123"
$uri = "https://login.microsoftonline.com/$TenantId/oauth2/v2.0/token "
$body = #{
grant_type = "client_credentials"
client_id = $ClientId
client_secret = $Secret
scope = "https://graph.microsoft.com/.default"
}
#call to get access token
$resp = Invoke-RestMethod -Method Post -Uri $uri -Body $body -ContentType "application/x-www-form-urlencoded"

Mocking Auth Token function in Pester

I have written a PowerShell function that generates an auth token in azure using REST API call. I am using the tenant id, Service principal client id and client secret as input param to generate this token. The SP client id and client secret are being read from the azure keyvault.
I wish to write a unit test to:
Read secret from keyvault
Mock the REST API call to generate dummy auth token
I am planning to use PESTER for unit testing and need some assistance here.
Here is my function:
function string GetAuthToken() {
$AuthTokenUri = "https://login.microsoftonline.com/$TenantId/oauth2/token"
$Body = #{
'resource' = $resourcename
'client_id' = $ID
'grant_type' = 'client_credentials'
'client_secret' = $Secret
}
$Token = Invoke-RestMethod -Method Post -Uri $AuthTokenUri -Body $Body -ContentType application/x-www-form-urlencoded'
$AuthToken = ($Token.access_token).ToString()
return $AuthToken
}

Permissions to access to MS Graph API via PowerShell

Question
I am trying to write a PowerShell script to get report data via the MS Graph API /reports/credentialUserRegistrationDetails.
When I use Graph Explorer it works just fine, as long as I enable Reports.Read.All on the Modify permissions (Preview) tab.
But, when I try to do it with my script, I just get the error "Calling principal does not have required MSGraph permissions Reports.Read.All"
In all my searches, I can only find how to assign permissions to apps.
Is there some way to make it so I can do it from my script?
My Script
$azContext = Get-AzContext
$token = [Microsoft.Azure.Commands.Common.Authentication.AzureSession]::Instance.AuthenticationFactory.Authenticate(
$azContext.Account,
$azContext.Environment,
$azContext.Tenant.Id,
$null,
[Microsoft.Azure.Commands.Common.Authentication.ShowDialog]::Never,
$null,
"https://graph.microsoft.com"
)
$params = #{
Method = "GET"
Uri = "https://graph.microsoft.com/beta/reports/credentialUserRegistrationDetails"
Headers = #{
Authorization = "Bearer $($token.AccessToken)"
"Content-Type" = "application/json"
}
}
Invoke-RestMethod #params
Response
{
"error": {
"code":"Authentication_MSGraphPermissionMissing",
"message":"Calling principal does not have required MSGraph permissions Reports.Read.All",
"innerError": {
"date":"2021-10-19T01:18:36",
"request-id":"xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
"client-request-id":"6b8cc3a3-b93b-44bb-b1d4-190c618aa52a"
}
}
}
When I use Graph Explorer it works just fine, as long as I enable
Reports.Read.All on the Modify permissions (Preview) tab.
Its because Microsoft Graph Explorer is a Enterprise Application of Microsoft which is present on every Azure AD tenant just you need to sign in and use it by providing the required permissions.
But when you are writing running your Powershell script it uses Microsoft Azure Powershell . You can verify it by checking the access_token received in JWT Token.So, you need to provide the Reports.Read.All API Permission to the same app in your tenant with appid : 1950a258-227b-4e31-a9cf-717495945fc2 in Enterprise Application >> Permissions and grant the admin consent .After providing the required permissions only it will work.
Another Way will be to create a App registration ,Create a client secret for it and then provide the Reports.Read.All API permission and use the below script:
$TenantName = "tenantname.onmicrosoft.com"
$clientID = "d344e3xxx-xxx-xxxx-xxxx-9c861d363244" # app registration clientId
$clientSecret = "fNc7Q~UNHBgv_xxxxxxxxxxxxxxxxxxxxxx-PD"
$Scope = "https://graph.microsoft.com/.default"
$Body = #{
Grant_Type = "client_credentials"
Scope = $Scope
client_Id = $clientID
Client_Secret = $clientSecret
}
$authUri = "https://login.microsoftonline.com/$TenantName/oauth2/v2.0/token"
$TokenResponse = Invoke-RestMethod -Uri $authUri -Method POST -Body $Body
$Headers = #{
"Authorization" = "Bearer $($TokenResponse.access_token)"
"Content-type" = "application/json"
}
$apiUri = "https://graph.microsoft.com/beta/reports/credentialUserRegistrationDetails"
$response = Invoke-RestMethod -Headers $Headers -Uri $apiUri -Method GET
$response.value
Output:
Note: In Some Tenants Microsoft Azure PowerShell might not be visible from portal , so in that case please use the above solution it will be easier.
For Authorization code flow, try something like this -
#region Auth1
#With User Interaction for Delegated Permission
Add-Type -AssemblyName System.Web
Function Get-AuthCode {
Add-Type -AssemblyName System.Windows.Forms
$form = New-Object -TypeName System.Windows.Forms.Form -Property #{Width = 440; Height = 640 }
$web = New-Object -TypeName System.Windows.Forms.WebBrowser -Property #{Width = 420; Height = 600; Url = ($url -f ($Scope -join "%20")) }
$DocComp = {
$Global:uri = $web.Url.AbsoluteUri
if ($Global:uri -match "error=[^&]*|code=[^&]*") { $form.Close() }
}
$web.ScriptErrorsSuppressed = $true
$web.Add_DocumentCompleted($DocComp)
$form.Controls.Add($web)
$form.Add_Shown( { $form.Activate() })
$form.ShowDialog() | Out-Null
$queryOutput = [System.Web.HttpUtility]::ParseQueryString($web.Url.Query)
$output = #{}
foreach ($key in $queryOutput.Keys) {
$output["$key"] = $queryOutput[$key]
}
#$output
}
Get-AuthCode
#Extract Access token from the returned URI
$regex = '(?<=code=)(.*)(?=&)'
$authCode = ($uri | Select-string -pattern $regex).Matches[0].Value
Write-output "Received an authCode, $authCode"
$tokenBody = #{
Grant_Type = "authorization_code"
Scope = "https://graph.microsoft.com/.default"
Client_Id = $clientId
Client_Secret = $clientSecret
redirect_uri = $redirectUri
code = $authCode
ressource = $resource
}
$tokenResponse = Invoke-RestMethod https://login.microsoftonline.com/common/oauth2/token -Method Post -ContentType "application/x-www-form-urlencoded" -Body $tokenBody -ErrorAction STOP
#endregion Auth1
For delegated permissions use something like below -
$tokenBody = #{
Grant_Type = "password"
Scope = "user.read%20openid%20profile%20offline_access"
Client_Id = $clientId
username = $User
password = $pw
resource = $resource
}
$tokenResponse = Invoke-RestMethod https://login.microsoftonline.com/common/oauth2/token -Method Post -ContentType "application/x-www-form-urlencoded" -Body $tokenBody -ErrorAction STOP
#endregion Auth2
For Application permissions (using client credential flow) use something like this
$tokenBody = #{
Grant_Type = "client_credentials"
Scope = "https://graph.microsoft.com/.default"
Client_Id = $clientId
Client_Secret = $clientSecret
}
$tokenResponse = Invoke-RestMethod -Uri "https://login.microsoftonline.com/$Tenantid/oauth2/v2.0/token" -Method POST -Body $tokenBody
#endregion Auth3
Despite what Method you have chosen, the tokenRepsonse Variable is holding our Key to Query against the Microsoft GRAPH API.
We want a list of all Teams in our Tenant, so this require propriate Application Permission. So for example- our Powershell to get a Full List of all Teams look like this -
$headers = #{
"Authorization" = "Bearer $($tokenResponse.access_token)"
"Content-type" = "application/json"
}
$URL = "https://graph.microsoft.com/beta/groups?`$filter=resourceProvisioningOptions/Any(x:x eq 'Team')"
$AllTeams = (Invoke-RestMethod -Headers $headers -Uri $URL -Method GET).value
Thanks.
I finally gave up on using the REST APIs and started using Microsoft.Graph PowerShell Modules. I find the documentation pretty sparse, but at least it works for what I need. :)
Import-Module "Microsoft.Graph.Identity.Signins"
Import-Module "Microsoft.Graph.Users"
Import-Module "Microsoft.Graph.Groups"
Connect-MgGraph -TenantId $TenantId -Scopes "Directory.Read.All", "UserAuthenticationMethod.Read.All" -ForceRefresh
Select-MgProfile -Name "beta"
$report = Get-MgReportCredentialUserRegistrationDetail

How to change a user password using Microsoft Graph API

I'm writing a kind of passwd command line app to change an Azure account's password. Something to work like this:
> passwd someuser Passw*rd1
After reading all the documentations, I have my application created in Azure portal with all the permissions needed and I'm about to get the app authorization, just before getting the access token.
Problem is instead of getting the authorization code what the call for https://login.microsoftonline.com/<tenantName>/oauth2/v2.0/authorize returns to me is an HTML page.
As far as I understand, this page is supposed to give the signed-in user the chance to delegate the required permissions, but this is a command to be used by an administrator user. In fact, there is no signed-in user at this moment.
What am I missing?
There is a code sample about resetting user password.
$tennantid = ''
$SubscriptionId = ''
$ApplicationID = ''
$ApplicationKey = ''
$TokenEndpoint = {https://login.windows.net/{0}/oauth2/token} -f $tennantid
$ARMResource = "https://graph.microsoft.com";
$Body = #{
'resource'= $ARMResource
'client_id' = $ApplicationID
'grant_type' = 'client_credentials'
'client_secret' = $ApplicationKey
'scope' = 'https%3A%2F%2Fgraph.microsoft.com%2FDirectory.AccessAsUser.All'
}
$params = #{
ContentType = 'application/x-www-form-urlencoded'
Headers = #{'accept'='application/json'}
Body = $Body
Method = 'Post'
URI = $TokenEndpoint
}
$token = Invoke-RestMethod #params
$headers = #{}
$headers.Add("authorization","Bearer $($Token.access_token)")
$ResetPwd = #{
"passwordProfile" = #{
"forceChangePasswordNextSignIn" = "false"
"password" = "Test123456!"
}
} | ConvertTo-Json
Invoke-RestMethod -Headers $headers -Method Patch -Uri "https://graph.microsoft.com/beta/users/$($respons.id)" -ContentType "application/json" -Body $ResetPwd
Then used this and the code above works.
$servicePrincipal = Get-MsolServicePrincipal -ServicePrincipalName ServicePrincipalName
$roleId = (Get-MsolRole -RoleName "Company Administrator").ObjectId
Add-MsolRoleMember -RoleObjectId $roleId -RoleMemberObjectId $servicePrincipal.ObjectId -RoleMemberType servicePrincipal
Hope this help.
Where are you call the /authorize endpoint?
You could call it like this in a browser and log in with your account:
https://login.microsoftonline.com/{tenant}/oauth2/v2.0/authorize?
client_id=6731de76-14a6-49ae-97bc-6eba6914391e
&response_type=code
&redirect_uri=http%3A%2F%2Flocalhost%2Fmyapp%2F
&response_mode=query
&scope=openid%20offline_access%20https%3A%2F%2Fgraph.microsoft.com%2Fuser.read
&state=12345
Then you will get a "code" in the address bar.
Use this code to request access token:
POST /{tenant}/oauth2/v2.0/token HTTP/1.1
client_id=6731de76-14a6-49ae-97bc-6eba6914391e
&scope=https%3A%2F%2Fgraph.microsoft.com%2Fuser.read
&code=OAAABAAAAiL9Kn2Z27UubvWFPbm0gLWQJVzCTE9UkP3pSx1aXxUjq3n8b2JRLk4OxVXr...
&redirect_uri=http%3A%2F%2Flocalhost%2Fmyapp%2F
&grant_type=authorization_code
&client_secret=JqQX2PNo9bpM0uEihUPzyrh
See details from Request an authorization code and Request an access token.