Get-AzAccessToken keeps returning the same Token by targeting a different tenant - azure

I have a PowerShell Azure Function that is secured with AAD. In my script I get the id token with the following command:
$assertion = $Request.Headers['x-ms-token-aad-id-token']
I use this assertion in order to get another token to use the Azure Service Management Api with the OBO Flow. Below is the code used:
$contentType = 'application/x-www-form-urlencoded'
$body = #{
grant_type = $grantType
client_id = $clientId
client_secret = $clientSecret
scope = $scope
requested_token_use = $requestedTokenUse
assertion = $assertion
}
$oboResponse = Invoke-RestMethod 'https://login.microsoftonline.com/e005f490-xxxx-4816-xxxx-b0ed7fa9xxxx/oauth2/v2.0/token' -Method 'POST' -body $body -ContentType $contentType
$accessToken = $oboResponse.access_token
Then I use this token to connect to azure with the Connect-AzAccount command. So far, everything is good well and the connection to Azure is working fine.
In my script, I try to have different tokens to connect to other tenants to which I belong. Unfortunately, it does not work as expected. Indeed the command returns the same token for different tenants. In fact, the produced token by the command "Get-AzAccessToken -TenantId $tenant.Id" is always equal to the token produced by the OBO flow.
Below is my PowerShell Azure function. I commented the part where I have a the issue.
using namespace System.Net
# Input bindings are passed in via param block.
param($Request, $TriggerMetadata)
$accountId = $Request.Headers['x-ms-client-principal-name']
$grantType = "urn:ietf:params:oauth:grant-type:jwt-bearer"
$clientId = "xxxx"
$clientSecret = "xxxx"
$scope = "https://management.azure.com/user_impersonation"
$requestedTokenUse = "on_behalf_of"
$assertion = $Request.Headers['x-ms-token-aad-id-token']
$contentType = 'application/x-www-form-urlencoded'
$body = #{
grant_type = $grantType
client_id = $clientId
client_secret = $clientSecret
scope = $scope
requested_token_use = $requestedTokenUse
assertion = $assertion
}
$oboResponse = Invoke-RestMethod 'https://login.microsoftonline.com/e005f490-xxxx-4816-xxxx-b0ed7fa9xxxx/oauth2/v2.0/token' -Method 'POST' -body $body -ContentType $contentType
$accessToken = $oboResponse.access_token
Connect-AzAccount -AccessToken $accessToken -AccountId $accountId
$allTenants = Get-AzTenant
foreach ($tenant in $allTenants) {
# Here I get the same access token for different tenants.
# The token is always equal to the token produced by the OBO flow.
$accessToken_ = Get-AzAccessToken -TenantId $tenant.Id
....
....
}
...
...
I don't understand why the token produced by the OBO flow is always equal to the token produced by Get-AzAccessToken. I hope I have described my problem correctly, thank you for your help.
Thank you.

I think the problem is due to your Invoke-RestMethod call using a static tenant ID/GUID:
$oboResponse = Invoke-RestMethod 'https://login.microsoftonline.com/e005f490-xxxx-4816-xxxx-b0ed7fa9xxxx/oauth2/v2.0/token' -Method 'POST' -body $body -ContentType $contentType
https://login.microsoftonline.com/{**tenant**}/oauth2/v2.0/token

Related

Unable to create Azure B2C User Flow via powershell with b2cIdentityUserFlow

I am trying to create a user flow with PowerShell, but I receiveThe remote server returned an error: (403) Forbidden.. I was reading the documentation from Microsoft but with no success.
Connect-AzAccount -Tenant "myorg.onmicrosoft.com"
$managementAccessToken = Get-AzAccessToken -TenantId "$tenantId" -ResourceTypeName MSGraph
$DefinitionFilePath = "C:\azdeploy\flows\b2csignin.json"
$signinFlowContent = Get-Content $DefinitionFilePath
Invoke-WebRequest -Uri "https://graph.microsoft.com/beta/identity/b2cUserFlows" `
-Method "POST" `
-Headers #{
"Content-Type" = "application/json"
"Authorization" = "Bearer $($managementAccessToken.Token)";
} `
-Body $signinFlowContent
JSON Content(Default From Microsoft Docs):
{
"id": "Customer",
"userFlowType": "signUpOrSignIn",
"userFlowTypeVersion": 3
}
Connect-AzAccount is made with a user who is Global Administrator, also tried with Lifecycle Workflows Administrator permissions. I don't know what to do, trying the old API but it is deprecated. I need to create a few User Flows with а few Application Claims. How can I achieve this?
Thanks!
I tried to reproduce the same in my environment and got below results:
I created one json file with same parameters as you like below:
I have one user named Sritest having Global Administrator role like below:
When I ran the same code as you by signing in with above user, I got same error as below:
Connect-AzAccount -Tenant "myorg.onmicrosoft.com"
$managementAccessToken = Get-AzAccessToken -TenantId "$tenantId" -ResourceTypeName MSGraph
$DefinitionFilePath = "C:\test\b2csignin.json"
$signinFlowContent = Get-Content $DefinitionFilePath
Invoke-WebRequest -Uri "https://graph.microsoft.com/beta/identity/b2cUserFlows" `
-Method "POST" `
-Headers #{
"Content-Type" = "application/json"
"Authorization" = "Bearer $($managementAccessToken.Token)";
} `
-Body $signinFlowContent
Response:
You need to have IdentityUserFlow.ReadWrite.All permission to create userflow.
To resolve the error, I registered one Azure AD application and added that API permission like below:
Make sure to grant admin consent after adding API permissions in application. Now, I created one client secret and added all these details in getting access token by modifying PowerShell code.
When I ran below modified code, userflow created successfully as below:
Connect-AzureAD -TenantId "c6d99123-0cf9-4b64-bde3-xxxxxxxxx"
$graphtokenBody = #{
grant_type = "client_credentials"
scope = "https://graph.microsoft.com/.default"
client_id = "appID"
client_secret = "secret"
}
$graphToken = Invoke-RestMethod -Uri "https://login.microsoftonline.com/c6d99123-0cf9-4b64-bde3-xxxxxxxxx/oauth2/v2.0/token" -Method POST -Body $graphtokenBody
$token = $graphToken.access_token
$DefinitionFilePath = "C:\test\b2csignin.json"
$signinFlowContent = Get-Content $DefinitionFilePath
Invoke-WebRequest -Uri "https://graph.microsoft.com/beta/identity/b2cUserFlows" `
-Method "POST" `
-Headers #{
"Content-Type" = "application/json"
"Authorization" = "Bearer $($token)";
} `
-Body $signinFlowContent
Response:
To confirm that, I checked the same in Portal where B2C_1_Customer userflow is present like below:

Unable to retrieve token from Azure API

I am trying to get a token to be able to retrieve Groups information in Azure AD
This is my powershell script against the API:
Invoke-RestMethod -Method POST -Uri 'https://login.microsoftonline.com/<tenantID>/oauth2/v2.0/token?api-version=1.6'
-Header #{'Content-type' = 'application/x-www-form-urlencoded'}
-Body '{grant_type=client_credentials&client_id=<clientID>&scope=https://graph.microsoft.com/.default&client_secret=<clientSecret> }'
I keep getting this error: {"error":"invalid_request","error_description":"AADSTS900144: The request body must contain the following parameter: 'grant_type'.\r\nTrace ID:
Your body ia malformed, just do:
$body = #{
Grant_Type = 'client_credentials'
Scope = 'https://graph.microsoft.com/.default'
Client_Id = $ClientID
Client_Secret = $Secret
}
$con = Invoke-RestMethod -Uri "https://login.microsoftonline.com/$tenantid/oauth2/v2.0/token" -Method 'Post' -Body $body
$token = $con.access_token

Using token from Connect-AZAccount on Intune Graph calls

So, after years consuming valuable information from StackOverflow this is my first question!
I used part of a code I found when researching as I start to move away from App Secrets in Graph API and prepare to use Managed Identities.
It successfully authenticate and generate the token when running locally(haven't tested anything on Azure Automation yet as this would be the PoC) but it will not work for everything in Graph, it works when querying AD Users and Groups but not on anything else, it seems it is not getting all the scopes the accounts has access to although the account is global admin.
This part will check if you are using Managed Identity or prompt for login
#Requires -Modules #{ ModuleName="Az.Accounts"; ModuleVersion="2.7.0" } , #{ ModuleName="Az.Resources"; ModuleVersion="5.1.0" }
Param(
[Switch]$nonInteractive
)
[void] [System.Reflection.Assembly]::LoadWithPartialName("System.Web")
$res = [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls -bor [Net.SecurityProtocolType]::Tls11 -bor [Net.SecurityProtocolType]::Tls12
if(!$Global:AZConnection){
try{
if($nonInteractive){
Write-Output "Logging in with MI"
$Null = Connect-AzAccount -Identity -ErrorAction Stop
Write-Output "Logged in as MI"
}else{
Write-Output "Logging in to Azure AD"
$Global:AZConnection = Connect-AzAccount -Force -ErrorAction Stop
Write-Output "Logged in to Azure AD with $($Global:AZConnection.Context.Account.id)"
}
}catch{
Throw $_
}
}
After this it will get the context and authenticate against graph.microsoft.com & graph.windows.net to get both tokens.
It will also prepare the headers to be used on WebRequests
$context = [Microsoft.Azure.Commands.Common.Authentication.Abstractions.AzureRmProfileProvider]::Instance.Profile.DefaultContext
$graph = ([Microsoft.Azure.Commands.Common.Authentication.AzureSession]::Instance.AuthenticationFactory.Authenticate($context.Account, $context.Environment, $context.Tenant.Id.ToString(), $null, [Microsoft.Azure.Commands.Common.Authentication.ShowDialog]::Always, $null, "https://graph.microsoft.com"))
$graphToken = $graph.AccessToken
$AAD = ([Microsoft.Azure.Commands.Common.Authentication.AzureSession]::Instance.AuthenticationFactory.Authenticate($context.Account, $context.Environment, $context.Tenant.Id.ToString(), $null, [Microsoft.Azure.Commands.Common.Authentication.ShowDialog]::Never, $null, "https://graph.windows.net"))
$aadToken = $AAD.AccessToken
$HeadersGraph = #{
'Content-Type' = "application\json"
'Authorization' = "Bearer $graphToken"
}
$HeadersAAD = #{
'Content-Type' = "application\json"
'Authorization' = "Bearer $aadToken"
}
Trying to use the token to query user or group information will work
$UserData = Invoke-RestMethod -Uri "https://graph.microsoft.com/beta/Users" -Method GET -Headers $HeadersGraph
$GroupData = Invoke-RestMethod -Uri "https://graph.microsoft.com/beta/Users" -Method GET -Headers $HeadersGraph
But if I try to query any Intune or SharePoint URI it will give me:
Invoke-RestMethod : The remote server returned an error: (403) Forbidden.
$deviceData = Invoke-RestMethod -Uri "https://graph.microsoft.com/beta/deviceManagement/managedDevices" -Method GET -Headers $HeadersGraph
$SharepointData = Invoke-RestMethod -Uri "https://graph.microsoft.com/beta/sites" -Method GET -Headers $HeadersGraph
I can use these Tokens to authenticate on other modules but I was trying to avoid it and keep everything on Graph web requests
#This also works
$Intune = Connect-MgGraph -AccessToken $graphToken
$AzureAD = Connect-AzureAD -AadAccessToken $aadToken -AccountId $context.Account.Id
Does anyone know of any resource principal that I can authenticate that would give me a valid token to make calls to SharePoint and Intune?
I assume that you're aware that this is a permission issue and not a token issue.
I have no solution for your problem. I just want to let you know, that nowadays there is an easier way to get Graph Access tokens (since you are already using the Az.Accounts module).
https://learn.microsoft.com/en-us/powershell/module/az.accounts/get-azaccesstoken

Call azure ad-protected API from powershell with username/password

I have an Azure AD-protected web api.
My javascript sends a callback-url where the users access-token is sent to
https://login.microsoftonline.com/{my_tenant}/oauth2/v2.0/token/?redirect_uri={my_url}&...
Now I want to access my api using powershell and for that reason I would like to just have the token as a response. I have tried this resource owner password credential-flow with the code below, but it just says that the username or password is incorrect...(I use same for manual login)
$creds = #{
client_id = $clientId
username = $username
password = $password
grant_type = "password"
scope="User.Read"
}
$headers = #{
"Content-Type"="application/x-www-form-urlencoded"
}
Invoke-RestMethod $authUrl -Method Post -Body $creds -Headers $headers;
So, in short, I want to login using username and password with powershell, is it possible?
I could not reproduce your issue, please double check your username and password.
Or you can try my working sample, and if you want to get the toekn from your own api, the scope should be {Application ID URI or Application ID}/User.Read, if you use scope="User.Read", it represents the MS Graph API by default.
Sample:
$authUrl = "https://login.microsoftonline.com/{tenant-id}/oauth2/v2.0/token/"
$clientId = "xxxxxxxxxxxxxxxxxxx"
$username = "xxxxxx#xxx.onmicrosoft.com"
$password = "xxxxxx"
$creds = #{
client_id = $clientId
username = $username
password = $password
grant_type = "password"
scope="{Application ID URI or Application ID}/User.Read"
}
$headers = #{
"Content-Type"="application/x-www-form-urlencoded"
}
Invoke-RestMethod $authUrl -Method Post -Body $creds -Headers $headers
Update:
When using resource owner password credential flow, please note the important things below.

Azure AD Access Token Without ADAL

I am trying to edit a user's AD profile on the server side which requires an access token. I can do this with the native app example which uses only a client id and secret. The docs mention that this is possible with only http requests (see Get Access Token), but I can't find any example or way to do this.
You can use OAuth2 to request an access token. Here is an example on how you use OAuth2 with Azure AD. It's written in PowerShell Script.
$tenantID = "<the Tenant ID of your Azure AD>"
$loginEndpoint = "https://login.windows.net/"
$graphResourceURI = "https://graph.windows.net/"
$clientId = "<the client id of your AD application>"
$key = "<the client secret of your AD application>"
# the URL for requesting access token.
$tokenURL = $loginEndpoint+$tenantID+"/oauth2/token"
# the token request body.
$body = "grant_type=client_credentials&client_id="+$clientId `
+"&client_secret="+[system.uri]::EscapeDataString($key) `
+"&resource="+[system.uri]::EscapeDataString($graphResourceURI)
# the token request headers.
$headers = #{"Content-Type"="application/x-www-form-urlencoded"}
$authenticationResult = Invoke-RestMethod -Method POST `
-Uri $tokenURL -Headers $headers -Body $body
$authenticationResult.access_token
The AD application you use here must be "Web application and/or web API", because a Native AD application does not have client secret.
I got this to work using Jack's answer but with a couple of changes. Here is the complete working example:
$tenantID = "<the name of your tenant of your Azure AD>"
$loginEndpoint = "https://login.windows.net/"
$graphResourceURI = "https://graph.windows.net/"
$clientId = "<the client id of your AD application>"
$key = "<the client secret of your AD application>"
# the URL for requesting access token.
$tokenURL = $loginEndpoint+$tenantID+"/oauth2/token"
# the token request body.
$body = "grant_type=client_credentials&client_id="+$clientId `
+"&client_secret="+[system.uri]::EscapeDataString($key) `
+"&resource="+[system.uri]::EscapeDataString($graphResourceURI)
# the token request headers.
$headers = #{"Content-Type"="application/x-www-form-urlencoded"}
$authenticationResult = Invoke-RestMethod -Method POST `
-Uri $tokenURL -Headers $headers -Body $body
$authenticationResult.access_token

Resources