Powershell: Get-MsalToken error AADSTS7000218 - powershell

Running the following Powershell command
$tokenresponse = Get-MsalToken -ClientId $clientID -TenantId $tenantID -Interactive -RedirectUri "http://localhost"
gives me the error:
AADSTS7000218: The request body must contain the following parameter: 'client_assertion' or 'client_secret'.
All solutions I found are pointing to the direction, that in the Azure Protal I should enable "Allow public client flows" but this setting is enabled. Any idea how I can get the token (I would need to get a token for delegated permissions)

I know this is an old question, but my answer below might help someone in the future. I ran into this same problem today and the way to fix it was by correcting the configuration on the App Registration. The solution was quite simple. I just had to set up Authentication to the right platform: "Mobile and desktop applications". Instead of Web or SPA.
To test it, get your tenant id and application (client) id, and pass it to the Get-MsalToken commandlet like below. The login page should pop-up, including any MFA dialogs (if MFA is configured).
$tenant_id = "00000000-0000-0000-0000-0000000000000"
$client_id = "00000000-0000-0000-0000-0000000000000"
$authParams = #{
ClientId = $client_id
TenantId = $tenant_id
Interactive = $true
}
$auth = Get-MsalToken #authParams
$auth

Related

Cannot call Graph API using delegated permissions in PowerShell

I have an Azure App Registration setup as below:
Not shown: two redirect URIs for https://login.microsoftonline.com/common/oauth2/nativeclient (which I believe is needed for auth code flows if using PowerShell?) and https://MYDEVSERVER
We have a web API that can correctly access Graph against this App Registration by using a hybrid on-behalf-of flow and IConfidentialClientApplication, along with a bootstrap token provided by an Office add-in.
The creation of this App Registration is actually done by a PowerShell script, and I want to add a feature that will try to login as the script user to make a test Graph API call (against the /me endpoint) to make sure it is setup correctly. The script can ensure that the App Registration is granted admin consent for the Graph API permissions so the script user does not have to provide consent.
From what I understand, the authorization code flow can be used to test these delegated permissions. However, I've tried two approaches with different results as below using the MSAL module to get the token and then use that token in the authorization header for a Graph call to simply return details about the logged in user via the /me endpoint:
If I don't pass a client secret to the Get-MsalToken cmdlet, I do get a login prompt but Get-MsalToken always returns the error 'AADSTS7000218: The request body must contain the following parameter: 'client_assertion' or 'client_secret'
If I do provide a client_secret, I do NOT get the login prompt and can successfully get a token, but the Graph call fails saying that the /me request is only valid with delegated authentication flow. Which makes sense because we need a user context.
So I'm stuck - I can't use the user context with the interactive login without it expecting a client secret that if I do use is using a different flow that's not supporting delegated permissions. What am I doing wrong? Or do I need to use a completely different authentication flow?
Does the fact that the App Registration is designed to handle SSO with Office web add-ins have anything to do with it? Meaning, I have a scope (e.g. api://MYWEBSERVER/XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX/access_as_user, as used by the add-in manifests for purposes of issuing a bootstrap token from the Office JS library to our Web API) and a list of authorized client applications (Office apps) in the 'Expose an API' section. I'm wondering if only those applications are authorized, and not the PowerShell script...
I've also seen some suggestions about using the device code flow and I'm wondering if I'm missing a step to get a refresh token (as demonstrated here but I can't get that example to work either using a client secret (I can't figure out the proper Get-MsalToken parameter combination).
$graphEndPointUrl = "https://graph.microsoft.com/v1.0/me"
$connectionDetails = #{
'TenantId' = $tenantId #Set variable to your own value
'ClientId' = $clientId #Set variable to your own value
'Scopes' = 'https://graph.microsoft.com/.default'
}
#Using the above information, it throws this error on Invoke-RestMethod:
#ERROR: "{"error":"invalid_client","error_description":"AADSTS7000218: The request body must contain the following parameter: 'client_assertion' or 'client_secret'.
#"error_codes":[7000218]
#"error_uri":"https://login.microsoftonline.com/error?code=7000218"}"
$connectionDetails = #{
'TenantId' = $tenantId #Set variable to your own value
'ClientId' = $clientId #Set variable to your own value
'ClientSecret' = $clientSecret | ConvertTo-SecureString -AsPlainText -Force #Set $clientSecret variable to your own value
'Scopes' = 'https://graph.microsoft.com/.default'
}
#Using the above information, it throws this error on Invoke-RestMethod:
#ERROR: "{"error":{"code":"BadRequest","message":"/me request is only valid with delegated authentication flow."
Import-Module MSAL.PS
try {
$myAccessToken = Get-MsalToken #connectionDetails
Write-Host $myAccessToken.AccessToken
$authHeader = #{
'Authorization' = $myAccessToken.CreateAuthorizationHeader()
}
$response = Invoke-RestMethod -Uri $graphEndPointUrl -Headers $authHeader
#Error thrown below
if ($null -eq $response) {
Write-Error "An unexpected error occurred making a test Graph API call to the $($graphEndPointUrl) endpoint" -ForegroundColor Red
}
else {
Write-Host "The test Graph API call to the $($graphEndPointUrl) endpoint was successful!"
}
}
catch {
$result = $_.Exception.Response.GetResponseStream()
$reader = New-Object System.IO.StreamReader($result)
$reader.BaseStream.Position = 0
$reader.DiscardBufferedData()
$responseBody = $reader.ReadToEnd();
$errorObj = ConvertFrom-Json -InputObject $responseBody
Write-Host "An unexpected error occurred making a test Graph API call to the $($graphEndPointUrl) endpoint: $($errorObj.error.message)"
}
Update
Everything you need I believe is written in this Microsoft article - https://learn.microsoft.com/en-us/azure/active-directory/develop/v2-oauth2-auth-code-flow
I noticed a key difference in the tutorial you referenced and your setup, thats the ID Token that is used with the hybrid setup, you can read more about it in the MS guide, check response_mode .
So principal goes as follow > You get an authorization code > use that code to generate your access and refresh tokens > then you can use different API calls with the Access token in the header.
As for your code, here are the things that are missing or are not correctly implemented.
You are missing the RedirectUri param. Make sure the URL is exact I have just put https://localhost as an example.
$connectionDetails = #{
'TenantId' = $tenantId #Set variable to your own value
'ClientId' = $clientId #Set variable to your own value
'ClientSecret' = $clientSecret | ConvertTo-SecureString -AsPlainText -Force #Set $clientSecret variable to your own value
'Scopes' = 'https://graph.microsoft.com/.default'
'RedirectUri' = 'https://localhost'
}
Your header is not implementing the Authorization properly. Check out the example from the tutorial
$myAuthMethods = (Invoke-RestMethod -Headers #{Authorization = "Bearer $($myAccessToken.AccessToken)" } `
-Uri "https://graph.microsoft.com/beta/me/authentication/methods" `
-Method Get).value
$myAuthMethods
The header have the following structure, where the word Bearer is before the token.
$header = #{
"Authorization" = "Bearer $($myAccessToken.AccessToken)"
}
Make sure you have the access token $myAccessToken.AccessToken in the header.
At that point, you should at least get a different error if request does not proceed.
The issue is that an additional "Mobile and desktop applications" platform needs to be added to the App Registration on the Authentication page (with "https://login.microsoftonline.com/common/oauth2/nativeclient" as a Redirect URI), as it only has a web platform currently. Then Get-MsalToken works without a client Id:
$myAccessToken = Get-MsalToken -ClientId $clientID -TenantId $tenantId -Interactive -Scopes 'https://graph.microsoft.com/.default' -RedirectUri 'https://login.microsoftonline.com/common/oauth2/nativeclient'
However, an additional request is needed to get a refresh token and use that for the Graph call:
$myAccessToken = Get-MsalToken -ClientId $clientID -TenantId $tenantId -Silent

Execute an App registration without AzureAD

For a professional project, a chunk of the pipeline must be able to create an application (the first App registration, so I only have a global Admin) automatically within Azure AD. So far I used AzureAD which works well with Powershell 5.6 on Windows.
I now must be able to run the code with Ubuntu 20.04 and its Powershell 7.2. Unfortunately for me, AzureAD module is only supported on non-core Windows PowerShell, therefore it does not work on core PS6 or PS7. A very simplified piece of code is the following:
# Connection infos
$tenantId = "abcdef12345-1234-1234-124-abcdef12346789"
$account = "my_admin#domain.com" # Is cloud Admin by default
$password = ConvertTo-SecureString "MyPassword" -AsPlainText -Force
$psCred = New-Object System.Management.Automation.PSCredential -ArgumentList ($account, $password)
Connect-AzureAD -Credential $psCred -Tenant $tenantId
# Create app
$appName = "MyApp"
New-App -appName $appName -tenant_id $tenantId
I am stuck and my question is the following: how could I run such an operation with Powershell 7.2 considering AzureAD is not usable? I did check Connect-MgGraph for the connection part only (https://github.com/microsoftgraph/msgraph-sdk-powershell) but the clientId is an infos that I don't have -and want to create-.
Thanks in advance
You can use DeviceLogin as explained in this article to obtain an oAuth access token for you Global Administrator account in PowerShell (independent of the version) but this first step needs a human interaction.
After obtaining the token, you can use it to make Graph API calls with your Global Administrator permissions to create an application.
Once you create your first application, you must attribute required permissions and use it to automate the process (obtain token programmatically using API calls) for application creation in PowerShell.
You could use Resource Owner Password Credentials (ROPC) to authenticate, however Microsoft actively discourages it in their documentation due to the security implications of sending a password over the wire.
If the security issues present with this method of authentication are still tolerated within your acceptance criteria, you would still need a ClientID. Luckily, AzureAD has a well-known ClientID that you can use to authenticate. This ID is 1950a258-227b-4e31-a9cf-717495945fc2
The below Powershell code should get you started. I've basically translated the HTTP request within Microsoft's documentation into a splatted Invoke-RestMethod command.
$LoginWithROPCParameters = #{
URI = "https://login.microsoftonline.com/contoso.onmicrosoft.com/oauth2/v2.0/token"
Method = "POST"
Body = #{
client_id = "1950a258-227b-4e31-a9cf-717495945fc2"
scope = "user.read openid profile offline_access"
username = "username#contoso.onmicrosoft.com"
password = "hunter2"
grant_type = "password"
}
}
Invoke-RestMethod #LoginWithROPCParameters

Authenticate to an Azure webapp through a second app registration using an Azure user token?

Working scenario
I have an Azure web application (https://www.contoso.com) to which I want to authenticate against and perform web call on.
I want to authenticate myself using Connect-AzAccount and make calls to it using my own identity.
I use the following code with success to obtain a valid token and call the application.
#Authenticate as myself or a sevice principal
Connect-AzAccount -Tenant 'a736bac3-c259-450f-90a4-f7d5bd7c4c78'
# App registration ID for Contoso
$ResourceUri = '24114488-8c26-4e87-8f71-90de62f5a8aa'
$Context = [Microsoft.Azure.Commands.Common.Authentication.Abstractions.AzureRmProfileProvider]::Instance.Profile.DefaultContext
$token = [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, $ResourceUri).AccessToken
$Headers = #{Headers = #{Authorization = "Bearer $token" } }
Invoke-RestMethod -Method Get -uri 'https://Contoso.com/module/Something' #Headers
That being said, the application itself does not implement forced user & groups assignment.
Instead, everybody can connect to it (by design).
The problematic
The solution presented above requires me to add Powershell Azure (1950a258-227b-4e31-a9cf-717495945fc2) in the list of Authorized client application to enable this scenario. By doing this operation, anybody in my directory can now technically make calls to the website, which is not desired.
Desired Scenario
Because user assignment is not enforced, I removed the Powershell Azure client from the main app and instead created a second App Registration for which I authorized the PS AZ client and added an API permission so it had access to the main App. The idea is that I can require user assignement for the second app registration and therefore limit the user that can make these kind of calls.
Initial (working) flow:
Connect-AzAccount authenticate the user
Code sample above is ran to obtain the token and call the app.
Desired flow
Connect-AzAccount authenticate the user
User connect through the client app registraion (new app) that has api permission to the main app.
User make calls to the main app.
Now, if I try to use my initial code sample with the Application Id of the second registration acting as a client ID, I get the following error
Invoke-RestMethod : {"code":401,"message":"IDX10214: Audience validation failed. Audiences: '[PII is hidden]'. Did not
match: validationParameters.ValidAudience: '[PII is hidden]' or validationParameters.ValidAudiences: '[PII is hidden]'."}
At line:1 char:12
+ $result2 = Invoke-RestMethod -Method Get -uri 'https://Contoso.com/so...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (System.Net.HttpWebRequest:HttpWebRequest) [Invoke-RestMethod], WebExcept
ion
+ FullyQualifiedErrorId : WebCmdletWebResponseException,Microsoft.PowerShell.Commands.InvokeRestMethodCommand
I believe that this is because I am obtaining a token for the second app registration and trying to call the main app URL, which is not authorized through the token.
A "working" alternative to this problem would be to connect using Client_credentials as grant_type such as this:
function get-Bearer([string]$TenantID, [string]$ClientID, [string]$ClientSecret) {
$TokenEndpoint = "https://login.windows.net/{0}/oauth2/token" -f $TenantID
$Body = #{
'resource' = '24114488-8c26-4e87-8f71-90de62f5a8aa'
'client_id' = $ClientID
'grant_type' = 'client_credentials'
'client_secret' = $ClientSecret
}
$params = #{
ContentType = 'application/x-www-form-urlencoded'
Headers = #{'accept' = 'application/json' }
Body = $Body
Method = 'Post'
URI = $TokenEndpoint
}
$token = Invoke-RestMethod #params
Return "Bearer " + ($token.access_token).ToString()
}
If I do that, even though I can make calls to the main app. again but it is connected with the actual client app. and not using the user identity obtained through Connect-AzAccount (for which tokens can be generated through the initial sample)
I was also able to make it work with a password grant_type but this is not making use of my bearer token and is also invalidated by MFA enabled users.
All insights are welcome.
References
Microsoft Identity platform documentation - v2-oauth2-on-behalf-of-flow
(digging around the application flow to try and get something working)
Application app ids, not object ids, should be used as resources identifiers. Keep getting tokens using the first application (24114488-8c26-4e87-8f71-90de62f5a8aa) as resource/audience (using its application id uri) and the second application as client. Connect-Az will be out of the equation.
#Install the ADAL.PS package if it's not installed.
if(!(Get-Package adal.ps)) { Install-Package -Name adal.ps }
$authority = "https://login.windows.net/common/oauth2/authorize"
#this is the security and compliance center endpoint
$resourceUrl = "{first application id uri}"
#replace <redirect-uri>, with the Redirect URI from your Azure AD application registration.
$clientId = "{second application app id}"
$redirectUri = "<redirect-uri>"
$response = Get-ADALToken -Resource $resourceUrl -ClientId $clientId -RedirectUri $redirectUri -Authority $authority -PromptBehavior:Always
$response.AccessToken | clip
I finally succeeded in my attempt.
In case my initial question was confusing, I "simply" wanted to connect as myself on Web API B using an intermediary Web API A
The reason was that I could not implement control access on Web API B (it is a requirement that it is available for all users of the tenant) and I also could not add Powershell client ID to Web API B directly as this would have allowed everyone to obtain a token and make call to Web API B using Powershell.
Therefore, I needed the intermediary Web API A to allow Powershell client ID and provide a controlled / limited user access to Web API B
There is a flow in the Microsoft identity platform and Oauth 2.0 called On-Behal-Of flow which allow exactly that scenario.
Prerequisites
App Registration for Web API B
API Permission: Delegated permission to User.Read (Graph API)
Expose an API: add a User_Impersonation scope and the Application ID of Web API A in the authorized client ID
App Registration for Web API A
In Api Permissions, add the User_Impersonation scope of Web API B
From there, the following code will get you connected
Connect-AzAccount -TenantId '584e32bc-214f-4046-89e5-79b7bac9ae75'
$Params = #{
ClientId = '6788d279-fb83-4962-b371-445479875e0e'
ClientSecret = '16cc0f3d88cb4dd189fe6bf8e845982d'
resourceId = 'aeefa559-cb1c-41eb-ac5d-0bcb0bc01eaf'
Scope = 'https://graph.microsoft.com/user.read+offline_access'
}
function Get-AzCustomTokenOnBehalOf {
[CmdletBinding()]
param (
$ClientId,
$ClientSecret,
#Application ID of the target resource
$ResourceId,
$Scope
)
# Get Token For WebApp A $TenantId
$Context = [Microsoft.Azure.Commands.Common.Authentication.Abstractions.AzureRmProfileProvider]::Instance.Profile.DefaultContext
$IntermediaryToken = [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, $ClientId).AccessToken
$TokenUri = "https://login.microsoftonline.com/$($Context.Tenant.Id)/oauth2/token"
$FinalToken = Invoke-RestMethod -Uri $TokenUri -Method Post -Body #{
grant_type = 'urn:ietf:params:oauth:grant-type:jwt-bearer'
resource = $ResourceId
client_id = $ClientId
client_secret = $ClientSecret
assertion = $IntermediaryToken
scope = $Scope
requested_token_use = 'on_behalf_of'
}
return $FinalToken
}
$Token = Get-AzCustomTokenOnBehalOf #Params
$Headers = #{Headers = #{Authorization = "bearer $($Token.access_token)" } }
$MYsite = Invoke-RestMethod -Method Get -uri 'https://SomeWebsite.com/' #Headers
Additional considerations
This scenario do require a client secret (This is the one I demonstratedO or a client certificate in order to work.
It is also subject to some limitations defined in the reference documentation and might not be suitable to all scenarios.
References:
Microsoft identity platform and OAuth 2.0 On-Behalf-Of flow

Partner Central rest api 401 unauthorized access

I followed the Microsoft doc to get the billing profile of a customer.
With the auth tutorial with the Powershell Code
$credential = Get-Credential
Connect-PartnerCenter -Credential $credential -ServicePrincipal -TenantId '<TenantId>'
Copied the access token and produced a Postman Get request but still got an 401 unauthorized request
It could be from the security update of Microsoft , but the Auth documentation is from january so i think These are the steps to get access to the parner central
https://www.microsoftpartnercommunity.com/t5/UK-Partner-Zone-Discussions/FY19-CSP-program-new-mandatory-security-requirements/td-p/6981
Or I don't have the right permissions as a user to get the billing profile.
I know it's one step that I oversee or that it's one thing that I did wrong but I can't see it
I am aware that there are some questions on stackoverflow about this issue. But Can't seem to find a solution there
Tried to get the access token with this Powershell code:
$appId = 'xxxxxxxxxxxxxxxxxxxxx'
$appSecret = 'xxxxxxxxxxxxxxxx' | ConvertTo-SecureString -AsPlainText -Force
$credential = New-Object System.Management.Automation.PSCredential $appId, $appSecret
$token = New-PartnerAccessToken -Consent -Credential $credential -Resource https://api.partnercenter.microsoft.com -ServicePrincipal
New-PartnerAccessToken -RefreshToken $token.RefreshToken -Resource https://api.partnercenter.microsoft.com -Credential $credential -ServicePrincipal
Source and probably more explanation to this you can find here: docs
If the link becomes invalid search for Partner Consent process for the Partner Center
Hope this one helps you out, this was what worked for me. Struggled a lot to find this out also.
Also make sure in your Azure AD app you select access token in the Authentication Setting beneath Implicit grant. And to select , urn:ietf:wg:oauth:2.0:oob beneath the suggested redirect URL's
which token you tried to access the Partner center api. First you have to generate the token by registering app with Partner center.
You have to use client/application id, tenant id/domain name, client secret.
Use that token to access the partner center api.

Acquire Authorization Token for Azure Function App from PowerShell

I've a problem getting token for my azure application
here is the code
function Get-Token
{
ipmo "${env:ProgramFiles(x86)}\Microsoft SDKs\Azure\PowerShell\ServiceManagement\Azure\Services\Microsoft.IdentityModel.Clients.ActiveDirectory.dll"
ipmo "${env:ProgramFiles(x86)}\Microsoft SDKs\Azure\PowerShell\ServiceManagement\Azure\Services\Microsoft.IdentityModel.Clients.ActiveDirectory.WindowsForms.dll"
$clientId = "1b730954-1685-4b74-9bfd-dac224a7b894"
$redirectUri = "urn:ietf:wg:oauth:2.0:oob"
$resourceAppIdURI = "https://ios111.azurewebsites.net/"
$authority = "https://login.microsoftonline.com/common/"
$authContext = [Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationContext]$authority
$authResult = $authContext.AcquireToken($resourceAppIdURI, $clientId, $redirectUri, "Auto")
$authResult.CreateAuthorizationHeader()
}
I receive the following error
Exception calling "AcquireToken" with "4" argument(s): "AADSTS65005:
Invalid resource. The client has requested access to a resource which
is not listed in the requested permissions in the client's application
registration. Client app ID: 1b730954-1685-4b74-9bfd-dac224a7b894.
Resource value from request: https://ios111.azurewebsites.net/.
Resource app ID: f4c1cc8d-629a-4c7e-836a-120ff078e664. List of valid
resources from app registration: .
However if i change the $resourceAppIdURI to
$resourceAppIdURI = "https://management.core.windows.net/"
It's all ok, and i'm authorized to access my application with received token (if i set Authorization header value to this token), but without roles claim which i define in application manifest for this user and which i want to check.
If i just access my function from browser, after login page redirected me back to a function, there is no a Authorization header specified by browser but ARRAffinity cookie and ClaimsPrincipal.Current.Claims in function context has correct roles claim. So, seems in case of PS, there JWT token acquired by .AcquireToken is deserialized to ClaimsPrincipal.Current without using internal web app logic.
Any ideas how to give PS client a permission to access my app ?
Thanks !
To Get Azuere App Token with the required roles, you need a ClientId and Secret, along with required permissions setup, if admin-consent is needed, you should click the 'Grant Permissions' button on the application properites in the Azure Portal.
Then, if all is set correct, you can get a token like this (with the roles included):
Example for the Microsoft Graph API
$adal = "${env:ProgramFiles(x86)}\Microsoft SDKs\Azure\PowerShell\ServiceManagement\Azure\Services\Microsoft.IdentityModel.Clients.ActiveDirectory.dll"
[System.Reflection.Assembly]::LoadFrom($adal) | Out-Null
$TenantName = "tenant.onmicrosoft.com"
$ClientId = "d1245516-2bg3-1234-123d-7cd067ff66b4" # Your AppId (Just a sample)
$Secret = "H7dd+PejUddGhuuGYY234Xhhhjs7739iQn112317zg=" # Your App Key Secret (Just a sample)
$AuthId = New-Object Microsoft.IdentityModel.Clients.ActiveDirectory.ClientCredential($clientId,$secret)
$redirectUri = "urn:ietf:wg:oauth:2.0:oob"
$resourceAppIdURI = "https://graph.microsoft.com"
[uri]$authority = "https://login.windows.net/$TenantName/oauth2/authorize"
$authContext = New-Object "Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationContext" -ArgumentList $authority
$Token = $authResult = $authContext.AcquireToken($resourceAppIdURI, $AuthId)
To Check the token you can use this JWT Token Decoder, to see if it has the required roles:
http://jwt.calebb.net/
Solved !
Thank for the help you guys !
Actually, i had to register native azure application, give it access for my WebApp and use this appId as the clientId in the script above. Using "1b730954-1685-4b74-9bfd-dac224a7b894" as Well Known PowerShell clientId probably possible for standard MS app, but there no way you can grand PS access for you app, at least not from azure portal.
Here is the link https://markscholman.com/2016/08/consuming-azure-api-app-azure-ad-authentication-using-powershell/ with step by step explanation given by WayneYang-MSFT