Могу ли я использовать пароли приложений MFA с Azure oauth2.0 ROPC?

В PowerShell у нас есть скрипт, который получает информацию из Azure REST API с использованием учетных данных пароля владельца ресурса.

https://docs.microsoft.com/bs-latn-ba/azure/active-directory/develop/v2-oauth-ropc.

Скрипт отлично работает с пользователями, у которых не включена MFA. Для пользователя с MFA это не работает. Я попытался использовать пароль приложения, который я создал для учетной записи пользователя с MFA, но это тоже не сработало.

https://support.microsoft.com/en-au/help/12409/microsoft-account-app-passwords-and-two-step-verification

Сценарий работает как служба, поэтому взаимодействие с пользователем недопустимо. Нам также необходимо использовать ROPC, потому что необходимая информация доступна только через делегированные разрешения в приложении Azure.

Есть ли у кого-нибудь опыт в этом?

Вот сценарий:

$tenantid = '*************************'
$subscriptionid = '*********************'
$clientid = '***********************'
$clientsecret = '******************'
$username = '*****************'
$password = '************************'

##################################################################
##################################################################
##################################################################

$return = Invoke-Command -ScriptBlock { 
param($tenantid,$subscriptionid,$clientid,$clientsecret,$username,$password)    

Add-Type -AssemblyName System.Web

$encPass = [System.Web.HttpUtility]::UrlEncode($password)
$encScope = [System.Web.HttpUtility]::UrlEncode('https://management.azure.com/user_impersonation')
$encSecret = [System.Web.HttpUtility]::UrlEncode($clientsecret)

$body = "client_id=$clientid&scope=$encScope&username=$username&password=$encPass&grant_type=password&client_secret=$encSecret"

$auth = Invoke-WebRequest "https://login.microsoftonline.com/$tenantid/oauth2/v2.0/token" -Method Post -ContentType "application/x-www-form-urlencoded" -Body $body -UseBasicParsing

$token = ($auth | ConvertFrom-Json).access_token
$headers = @{
    'Authorization'="Bearer $($token)"
}

$data = Invoke-WebRequest "https://management.azure.com/subscriptions/$subscriptionid/providers/Microsoft.Advisor/recommendations?api-version=2017-04-19" -Method GET -Headers $headers -UseBasicParsing

New-Object PSObject -Property @{
    content=$data.content
}

} -ArgumentList $tenantid,$subscriptionid,$clientid,$clientsecret,$username,$password

$content = $return.content

Write-Host $content

Результат, когда я использую пользователя с включенной MFA:

Invoke-WebRequest : {"error":"invalid_grant","error_description":"AADSTS50076: Due to a configuration change made by your administrator, or because you moved to a new location, you must use multi-factor authentication to 
access '*******'.\r\nTrace ID: d9a7f9f2-c52c-40ca-b057-9513bd353900\r\nCorrelation ID: 3329e686-7bd0-409d-b7da-91e49221bacc\r\nTimestamp: 2019-10-02 
13:19:36Z","error_codes":[50076],"timestamp":"2019-10-02 
13:19:36Z","trace_id":"d9a7f9f2-c52c-40ca-b057-9513bd353900","correlation_id":"3329e686-7bd0-409d-b7da-91e49221bacc","error_uri":"https://login.microsoftonline.com/error?code=50076","suberror":"basic_action"}
At C:\Users\Wouter.sterkens\Documents\VS Projects\Azure Monitoring\advisor.ps1:27 char:9
+ $auth = Invoke-WebRequest "https://login.microsoftonline.com/$tenanti ...
+         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidOperation: (System.Net.HttpWebRequest:HttpWebRequest) [Invoke-WebRequest], WebException
    + FullyQualifiedErrorId : WebCmdletWebResponseException,Microsoft.PowerShell.Commands.InvokeWebRequestCommand
ConvertFrom-Json : Cannot bind argument to parameter 'InputObject' because it is null.
At C:\Users\Wouter.sterkens\Documents\VS Projects\Azure Monitoring\advisor.ps1:29 char:19
+ $token = ($auth | ConvertFrom-Json).access_token
+                   ~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidData: (:) [ConvertFrom-Json], ParameterBindingValidationException
    + FullyQualifiedErrorId : ParameterArgumentValidationErrorNullNotAllowed,Microsoft.PowerShell.Commands.ConvertFromJsonCommand

Invoke-WebRequest : {"error":{"code":"AuthenticationFailedMissingToken","message":"Authentication failed. The 'Authorization' header is missing the access token."}}
At C:\Users\Wouter.sterkens\Documents\VS Projects\Azure Monitoring\advisor.ps1:34 char:9
+ $data = Invoke-WebRequest "https://management.azure.com/subscriptions ...
+         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidOperation: (System.Net.HttpWebRequest:HttpWebRequest) [Invoke-WebRequest], WebException
    + FullyQualifiedErrorId : WebCmdletWebResponseException,Microsoft.PowerShell.Commands.InvokeWebRequestCommand

Результат, когда я меняю пароль с помощью пароля приложения, созданного для учетной записи пользователя

Invoke-WebRequest : {"error":"invalid_grant","error_description":"AADSTS50126: Invalid username or password.\r\nTrace ID: 3674934a-120b-48f3-96d8-7ec8ddf44300\r\nCorrelation ID: 
593aecd7-bbb2-4c5a-96e1-050bc00047ac\r\nTimestamp: 2019-10-02 13:26:46Z","error_codes":[50126],"timestamp":"2019-10-02 
13:26:46Z","trace_id":"3674934a-120b-48f3-96d8-7ec8ddf44300","correlation_id":"593aecd7-bbb2-4c5a-96e1-050bc00047ac","error_uri":"https://login.microsoftonline.com/error?code=50126"}
At C:\Users\Wouter.sterkens\Documents\VS Projects\Azure Monitoring\advisor.ps1:27 char:9
+ $auth = Invoke-WebRequest "https://login.microsoftonline.com/$tenanti ...
+         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidOperation: (System.Net.HttpWebRequest:HttpWebRequest) [Invoke-WebRequest], WebException
    + FullyQualifiedErrorId : WebCmdletWebResponseException,Microsoft.PowerShell.Commands.InvokeWebRequestCommand
ConvertFrom-Json : Cannot bind argument to parameter 'InputObject' because it is null.
At C:\Users\Wouter.sterkens\Documents\VS Projects\Azure Monitoring\advisor.ps1:29 char:19
+ $token = ($auth | ConvertFrom-Json).access_token
+                   ~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidData: (:) [ConvertFrom-Json], ParameterBindingValidationException
    + FullyQualifiedErrorId : ParameterArgumentValidationErrorNullNotAllowed,Microsoft.PowerShell.Commands.ConvertFromJsonCommand

Invoke-WebRequest : {"error":{"code":"AuthenticationFailedMissingToken","message":"Authentication failed. The 'Authorization' header is missing the access token."}}
At C:\Users\Wouter.sterkens\Documents\VS Projects\Azure Monitoring\advisor.ps1:34 char:9
+ $data = Invoke-WebRequest "https://management.azure.com/subscriptions ...
+         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidOperation: (System.Net.HttpWebRequest:HttpWebRequest) [Invoke-WebRequest], WebException
    + FullyQualifiedErrorId : WebCmdletWebResponseException,Microsoft.PowerShell.Commands.InvokeWebRequestCommand

person WouterSterkens    schedule 02.10.2019    source источник
comment
Для пользователя с MFA это не работает, можете ли вы включить то, что видите?   -  person Rudy2015    schedule 02.10.2019
comment
@ Rudy2015 Я включил оба выхода. С MFA и с паролем приложения.   -  person WouterSterkens    schedule 02.10.2019


Ответы (1)


Насколько мне известно, пароль приложения используется для завершения MFA с клиентами, которые не поддерживают современную аутентификацию. Теперь вы используете поток ROPC OAuth. Пароль приложения не поддерживает его.

В зависимости от ситуации я предлагаю вам завершить MFA вручную, чтобы получить токен обновления, затем мы используем токен обновления, чтобы получить токен доступа и вызвать API. Поскольку токен обновления MFA не истечет, пока вы его не отзовете. Или вы используете OAuth 2.0 поток учетных данных клиента, чтобы получить токен доступа. Например

Токен обновления пользователя

  1. Зарегистрируйте приложение Azure AD

  2. Используйте поток кода авторизации OAuth 2.0, чтобы завершить MFA и получить токен обновления.

$Params = @{
    'client_id' = 'b0114608-677e-4eca-ae22-60c32e1782d9' 
    'redirect_URI' = 'https://www.baidu.com'
    'response_type'='code'
    'scope' = 'offline_access openid https://management.azure.com/user_impersonation'
}
$ClientSecret =''
$TeantID = ''
$Query = "?"; $Params.Keys | % {$Query+= "$($_)=$($Params.Item($_))&"} ; $Query = $Query.TrimEnd('&')


$IE= new-object -ComObject "InternetExplorer.Application"
$IE.Visible = $true
$IE.navigate2("https://login.microsoftonline.com/$($TeantID)/oauth2/v2.0/authorize$Query")

write-host "get authorization code"
pause

Add-Type -AssemblyName System.Web
[System.Web.HttpUtility]::ParseQueryString(([uri] $IE.LocationURL).Query)['code']
$Code = [System.Web.HttpUtility]::ParseQueryString(([uri] $IE.LocationURL).Query)['code']
$IE.Quit()

$TokenResult = Invoke-RestMethod -Method Post -ContentType 'application/x-www-form-urlencoded' -Uri "https://login.microsoftonline.com/$($TeantID)/oauth2/v2.0/token" -Body @{
    client_id     = $Params.client_id
    scope         = ''
    code          = $Code
    redirect_uri  = $Params.Redirect_URI
    grant_type    = 'authorization_code'
    client_secret = $ClientSecret
}

$TokenResult.refresh_token
  1. Получите токен доступа и вызовите API
$TokenResult = Invoke-RestMethod -Method Post -ContentType 'application/x-www-form-urlencoded' -Uri "https://login.microsoftonline.com/$($TeantID)/oauth2/v2.0/token" -Body @{
    client_id     = ''
    scope         = 'https://management.azure.com/user_impersonation'
    redirect_uri  = ''
    grant_type    = 'refresh_token'
    client_secret = ''
    refresh_token =''
}



 Invoke-RestMethod -Method Get -Uri '' -Headers @{Authorization = "Bearer "+ $TokenResult.access_token}

Использовать поток учетных данных клиента OAuth 2.0

$TokenResult = Invoke-RestMethod -Method Post -ContentType 'application/x-www-form-urlencoded' -Uri "https://login.microsoftonline.com/$($TeantID)/oauth2/v2.0/token" -Body @{
    client_id     = ''
    scope         = 'https://management.azure.com/.default'
    grant_type    = 'client_credentials'
    client_secret = ''

}

 Invoke-RestMethod -Method Get -Uri '' -Headers @{Authorization = "Bearer "+ $TokenResult.access_token}

Обновление В соответствии с вашими потребностями вы можете создать участника-службы и назначить субъекту службы роль RABC. Затем вы можете поток учетных данных клиента OAuth 2.0, чтобы получить токен доступа и вызвать Azure rest api. Подробные шаги приведены ниже.

  1. Создайте субъект-службу и назначьте ему роль RABC.
Connect-AzAccount
$password=''
$credentials = New-Object Microsoft.Azure.Commands.ActiveDirectory.PSADPasswordCredential -Property @{ StartDate=Get-Date; EndDate=Get-Date -Year 2024; Password=$password'}
$sp = New-AzAdServicePrincipal -DisplayName jimtest1 -PasswordCredential $credentials

New-AzRoleAssignment -ApplicationId $sp.ApplicationId -RoleDefinitionName Owner
  1. Получите токен доступа
# get access token
$TeantID='hanxia.onmicrosoft.com'
$TokenResult = Invoke-RestMethod -Method Post -ContentType 'application/x-www-form-urlencoded' -Uri "https://login.microsoftonline.com/$($TeantID)/oauth2/v2.0/token" -Body @{
    client_id     = $sp.ApplicationId # the application id of service principal
    scope         = 'https://management.azure.com/.default'
    grant_type    = 'client_credentials'
    client_secret = $password # you use it in step 1

}
  1. Вызов Azure Rest API
#list resource group
$values =Invoke-RestMethod -Method Get -Uri "https://management.azure.com/subscriptions/e5b0fcfa-e859-43f3-8d84-5e5fe29f4c68/resourcegroups?api-version=2019-05-10" -Headers @{
Authorization = "Bearer "+ $TokenResult.access_token
ContentType = 'application/json'
}

введите здесь описание изображения

Для получения более подробной информации, пожалуйста, обратитесь к

https://docs.microsoft.com/en-us/powershell/azure/create-azure-service-principal-azureps?view=azps-2.7.0

https://docs.microsoft.com/en-us/azure/azure-resource-manager/resource-manager-api-authentication#get-app-only-access-token-for-azure-resource-manager

person Jim Xu    schedule 03.10.2019
comment
Спасибо за ответ. Я думаю, что вариант с токеном обновления для нас невозможен. Мы не можем пройти аутентификацию вручную, потому что скрипт работает как сервис. Поток учетных данных клиента был бы хорошим решением, если management.azure.com использует разрешения приложения, но это не так. В этом API доступны только делегированные разрешения. - person WouterSterkens; 03.10.2019
comment
@WouterSterkens Вы можете использовать субъект-службу для вызова Azure REST API. Подробнее читайте в моем обновлении - person Jim Xu; 03.10.2019
comment
оно работает! Но я не понимаю, как: d Обычно я создаю приложение и настраиваю разрешения API. С субъектом службы также создается приложение, но разрешения api не требуются. Также есть разница в объеме. SP использует .default, приложение по умолчанию использует user_impersonation. Вы можете объяснить мне, в чем разница между этими двумя конфигурациями? - person WouterSterkens; 03.10.2019
comment
Во-первых, что касается того, почему у субъекта-службы нет разрешений api, поскольку мы назначили ему роль RABC, тогда у него есть разрешение на доступ к ресурсу Azure. И вы также можете назначить ему другие permsiioins API, а затем использовать его для доступа к другой службе, защищенной Azure AD. Дополнительные сведения см. В docs.microsoft.com/en-us/azure/active-directory/develop/ и docs.microsoft.com/en-us/azure/role-based-access-control/. - person Jim Xu; 07.10.2019
comment
Во-вторых, область /.defaul - это встроенная область для каждого приложения, которая ссылается на статический список настроенных разрешений. Это необходимо в потоке учетных данных клиента. Дополнительные сведения см. В docs.microsoft.com/en-us/azure/active-directory/develop/ - person Jim Xu; 07.10.2019
comment
@WouterSterkens Есть ли у вас другие проблемы? Если у вас нет других проблем, не могли бы вы принять ответ? Это может помочь большему количеству людей. - person Jim Xu; 10.10.2019
comment
Есть ли способ не открывать всплывающее окно для учетных данных, это решение хорошее, но с этим решением нельзя автоматизировать. - person Ashish-BeJovial; 01.09.2020
comment
@ Ashish-BeJovial, вы можете использовать поток учетных данных клиента OAuth 2.0 - person Jim Xu; 01.09.2020
comment
@Jim Xu, каким будет uri, я следую тем же путем и получаю следующую ошибку Invoke-RestMethod: Invalid URI: имя хоста не может быть проанализировано. В строке: 1 символ: 2 + Invoke-RestMethod -Method Get -Uri '' -Headers @ {Authorization = Be ... + ~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + CategoryInfo: NotSpecified: (:) [Invoke-RestMethod], UriFormatException + FullyQualifiedErrorId: System.UriFormatException, Microsoft.PowerShell.Commands.InvokeRestMethodCommand - person Ashish-BeJovial; 01.09.2020