Azure B2C - REST API 调用错误 "Message: The claims exchange <Id> specified in step <order> returned HTTP error response that could not be parsed"
Azure B2C - REST API call Error "Message: The claims exchange <Id> specified in step <order> returned HTTP error response that could not be parsed"
谢谢您的帮助。我正在从 Azure B2C 中的自定义策略进行 REST api 调用。当我在浏览器中调用 azure 函数时,在 auzre 门户中 test/run 或通过 PowerShell Invoke-RestMehod,我得到了预期的结果。当我在门户(通过 Monitor blade)或 Application Insights 中查看 azure 函数时,我得到 200 状态 OK。但是,B2C 一直抱怨错误“消息:步骤‘7’中指定的声明交换 'GetUserGroups' 返回了无法解析的 HTTP 错误响应。”
这是 TFExtensions.xml
中的声明类型
<ClaimsSchema>
<ClaimType Id="groupsMy">
<DisplayName>Comma delimited list of group names</DisplayName>
<DataType>stringCollection</DataType>
<UserInputType>Readonly</UserInputType>
</ClaimType>
</ClaimsSchema>
这是 REST 技术简介
<TechnicalProfile Id="GetUserGroups">
<DisplayName>Retrieves security groups assigned to the user</DisplayName>
<Protocol Name="Proprietary" Handler="Web.TPEngine.Providers.RestfulProvider, Web.TPEngine, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
<Metadata>
<Item Key="ServiceUrl">https://functionapp.azurewebsites.net/api/GetGroupsFromGraph</Item>
<Item Key="AuthenticationType">None</Item>
<Item Key="SendClaimsIn">QueryString</Item>
<Item Key="AllowInsecureAuthInProduction">true</Item>
</Metadata>
<InputClaims>
<InputClaim Required="true" ClaimTypeReferenceId="objectId" />
</InputClaims>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="groupsMy" PartnerClaimType="groupsMy" />
</OutputClaims>
<UseTechnicalProfileForSessionManagement ReferenceId="SM-Noop" />
</TechnicalProfile>
这里是调用技术配置文件的编排步骤
<OrchestrationStep Order="7" Type="ClaimsExchange">
<ClaimsExchanges>
<ClaimsExchange Id="GetUserGroups" TechnicalProfileReferenceId="GetUserGroups" />
</ClaimsExchanges>
</OrchestrationStep>
这是 PowerShell Azure 函数
using namespace System.Net
# Input bindings are passed in via param block.
param($Request, $TriggerMetadata)
# Write to the Azure Functions log stream.
Write-Host "PowerShell HTTP trigger function processed a request."
function getAccessTokenMicrosoftGraph($clientid, $tenant, $ClientSecret) {
$loginURL = "https://login.microsoftonline.com"
$resource = "https://graph.microsoft.com" #Microsoft Graph API
# Get an OAuth 2 access token based on client id, secret and tenant
$body = @{grant_type="client_credentials";client_id=$ClientID;client_secret=$ClientSecret;resource=$resource}
$oauth = Invoke-RestMethod -Method Post -Uri $loginURL/$tenant/oauth2/token?api-version=1.0 -Body $body
return $oauth
}
function returnUserGroups($objectId){
$tenant = "tenant.onmicrosoft.com"
$ClientID = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxx"
$ClientSecret = "%%%%%%%%%%%%%%%%%%%%%%%%%"
$body = @{grant_type="client_credentials";client_id=$ClientID;client_secret=$ClientSecret;resource=$resource}
#get Microsoft Graph Token
$oauthToken = getAccessTokenMicrosoftGraph -clientid $ClientID -tenant $tenant -ClientSecret $ClientSecret
#Generate the authentication header and make the request
$AuthHeader = @{"Authorization"= $oauthToken.access_token;"Content-Type"="application/json";"ContentLength"=$body.length }
#Get membership
$MemberOf = Invoke-WebRequest -Headers $AuthHeader -Uri "https://graph.microsoft.com/beta/users/$objectId/transitiveMemberOf"
#$MemberOf = Invoke-WebRequest -Headers $AuthHeader -Uri "https://graph.windows.net/$tenant/users/$objectId/$links/memberOf"
#The query will return a JSON object, which you can convert to an array via the following:
$result = ($MemberOf.Content | ConvertFrom-Json).Value
#parse into comma-separated
#$groups = ($result | Where-Object {$_.'@odata.type' -eq '#microsoft.graph.group'} | Select-Object DisplayName).DisplayName -join ","
$groups = ($result | Where-Object {$_.'@odata.type' -eq '#microsoft.graph.group'} | Select-Object @{name="groupsMy" ; expression={$_.DisplayName}})
return $groups | ConvertTo-Json
}
# Interact with query parameters or the body of the request.
$objectId = $Request.Query.objectId
if (-not $objectId) {
$objectId = $Request.Body.objectId
}
if ($objectId) {
Write-Host "ObjectID successfully received by fxn"
$body = $objectId
$pshell = returnUserGroups -objectId $body
}
else {
$status = [HttpStatusCode]::BadRequest
$body = "Please pass a name on the query string or in the request body."
}
if ($pshell){
$status = [HttpStatusCode]::OK
Write-Host "groups from graph API loaded"
}
else {
$status = [HttpStatusCode]::BadRequest
$body = "groups not successfully received from Graph API."
}
# Associate values to output bindings by calling 'Push-OutputBinding'.
Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{
StatusCode = $status
Body = $pshell | ConvertTo-Json
})
最后,我的依赖方技术配置文件 (SAML)
<RelyingParty>
<DefaultUserJourney ReferenceId="SignUpOrSignIn" />
<TechnicalProfile Id="PolicyProfile">
<DisplayName>PolicyProfile</DisplayName>
<Protocol Name="SAML2"/>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="displayName" />
<OutputClaim ClaimTypeReferenceId="givenName" />
<OutputClaim ClaimTypeReferenceId="surname" />
<OutputClaim ClaimTypeReferenceId="groupsMy" PartnerClaimType="groupsMy"/>
<OutputClaim ClaimTypeReferenceId="email" DefaultValue="" />
<OutputClaim ClaimTypeReferenceId="identityProvider" DefaultValue="" />
<OutputClaim ClaimTypeReferenceId="objectId"/>
</OutputClaims>
<SubjectNamingInfo ClaimType="email" Format="urn:oasis:names:tc:SAML:2.0:nameid-format:persistent" ExcludeAsClaim="true"/>
</TechnicalProfile>
</RelyingParty>
using Microsoft.AspNetCore.Mvc;
[HttpGet]
public async Task<JsonResult> Groups(string objectId)
{
List<string> groups = [your list of groups];
//!!!!!This is the trick if I sent plain json I got the error
JsonResult o = new JsonResult(
new
{
groups
});
return o;
}
正确的,按 Azure B2C rest-interface 预期的 JSON 格式,对于 stringCollection 是那个:
{
"groups": [
"Ford",
"BMW",
"Fiat"
]
}
我想通了。 API 是一个 Azure PowerShell 函数。我只需要使用 ConvertTo-Json 将我的 System.Collections.Generic.List[string] 转换为 JSON。然后,我进一步解析如下: $res = @" { "roleNames": [ $json-variable ] } "@ Stopped getting the error from the B2C REST TP after that.
谢谢您的帮助。我正在从 Azure B2C 中的自定义策略进行 REST api 调用。当我在浏览器中调用 azure 函数时,在 auzre 门户中 test/run 或通过 PowerShell Invoke-RestMehod,我得到了预期的结果。当我在门户(通过 Monitor blade)或 Application Insights 中查看 azure 函数时,我得到 200 状态 OK。但是,B2C 一直抱怨错误“消息:步骤‘7’中指定的声明交换 'GetUserGroups' 返回了无法解析的 HTTP 错误响应。”
这是 TFExtensions.xml
中的声明类型<ClaimsSchema>
<ClaimType Id="groupsMy">
<DisplayName>Comma delimited list of group names</DisplayName>
<DataType>stringCollection</DataType>
<UserInputType>Readonly</UserInputType>
</ClaimType>
</ClaimsSchema>
这是 REST 技术简介
<TechnicalProfile Id="GetUserGroups">
<DisplayName>Retrieves security groups assigned to the user</DisplayName>
<Protocol Name="Proprietary" Handler="Web.TPEngine.Providers.RestfulProvider, Web.TPEngine, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
<Metadata>
<Item Key="ServiceUrl">https://functionapp.azurewebsites.net/api/GetGroupsFromGraph</Item>
<Item Key="AuthenticationType">None</Item>
<Item Key="SendClaimsIn">QueryString</Item>
<Item Key="AllowInsecureAuthInProduction">true</Item>
</Metadata>
<InputClaims>
<InputClaim Required="true" ClaimTypeReferenceId="objectId" />
</InputClaims>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="groupsMy" PartnerClaimType="groupsMy" />
</OutputClaims>
<UseTechnicalProfileForSessionManagement ReferenceId="SM-Noop" />
</TechnicalProfile>
这里是调用技术配置文件的编排步骤
<OrchestrationStep Order="7" Type="ClaimsExchange">
<ClaimsExchanges>
<ClaimsExchange Id="GetUserGroups" TechnicalProfileReferenceId="GetUserGroups" />
</ClaimsExchanges>
</OrchestrationStep>
这是 PowerShell Azure 函数
using namespace System.Net
# Input bindings are passed in via param block.
param($Request, $TriggerMetadata)
# Write to the Azure Functions log stream.
Write-Host "PowerShell HTTP trigger function processed a request."
function getAccessTokenMicrosoftGraph($clientid, $tenant, $ClientSecret) {
$loginURL = "https://login.microsoftonline.com"
$resource = "https://graph.microsoft.com" #Microsoft Graph API
# Get an OAuth 2 access token based on client id, secret and tenant
$body = @{grant_type="client_credentials";client_id=$ClientID;client_secret=$ClientSecret;resource=$resource}
$oauth = Invoke-RestMethod -Method Post -Uri $loginURL/$tenant/oauth2/token?api-version=1.0 -Body $body
return $oauth
}
function returnUserGroups($objectId){
$tenant = "tenant.onmicrosoft.com"
$ClientID = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxx"
$ClientSecret = "%%%%%%%%%%%%%%%%%%%%%%%%%"
$body = @{grant_type="client_credentials";client_id=$ClientID;client_secret=$ClientSecret;resource=$resource}
#get Microsoft Graph Token
$oauthToken = getAccessTokenMicrosoftGraph -clientid $ClientID -tenant $tenant -ClientSecret $ClientSecret
#Generate the authentication header and make the request
$AuthHeader = @{"Authorization"= $oauthToken.access_token;"Content-Type"="application/json";"ContentLength"=$body.length }
#Get membership
$MemberOf = Invoke-WebRequest -Headers $AuthHeader -Uri "https://graph.microsoft.com/beta/users/$objectId/transitiveMemberOf"
#$MemberOf = Invoke-WebRequest -Headers $AuthHeader -Uri "https://graph.windows.net/$tenant/users/$objectId/$links/memberOf"
#The query will return a JSON object, which you can convert to an array via the following:
$result = ($MemberOf.Content | ConvertFrom-Json).Value
#parse into comma-separated
#$groups = ($result | Where-Object {$_.'@odata.type' -eq '#microsoft.graph.group'} | Select-Object DisplayName).DisplayName -join ","
$groups = ($result | Where-Object {$_.'@odata.type' -eq '#microsoft.graph.group'} | Select-Object @{name="groupsMy" ; expression={$_.DisplayName}})
return $groups | ConvertTo-Json
}
# Interact with query parameters or the body of the request.
$objectId = $Request.Query.objectId
if (-not $objectId) {
$objectId = $Request.Body.objectId
}
if ($objectId) {
Write-Host "ObjectID successfully received by fxn"
$body = $objectId
$pshell = returnUserGroups -objectId $body
}
else {
$status = [HttpStatusCode]::BadRequest
$body = "Please pass a name on the query string or in the request body."
}
if ($pshell){
$status = [HttpStatusCode]::OK
Write-Host "groups from graph API loaded"
}
else {
$status = [HttpStatusCode]::BadRequest
$body = "groups not successfully received from Graph API."
}
# Associate values to output bindings by calling 'Push-OutputBinding'.
Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{
StatusCode = $status
Body = $pshell | ConvertTo-Json
})
最后,我的依赖方技术配置文件 (SAML)
<RelyingParty>
<DefaultUserJourney ReferenceId="SignUpOrSignIn" />
<TechnicalProfile Id="PolicyProfile">
<DisplayName>PolicyProfile</DisplayName>
<Protocol Name="SAML2"/>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="displayName" />
<OutputClaim ClaimTypeReferenceId="givenName" />
<OutputClaim ClaimTypeReferenceId="surname" />
<OutputClaim ClaimTypeReferenceId="groupsMy" PartnerClaimType="groupsMy"/>
<OutputClaim ClaimTypeReferenceId="email" DefaultValue="" />
<OutputClaim ClaimTypeReferenceId="identityProvider" DefaultValue="" />
<OutputClaim ClaimTypeReferenceId="objectId"/>
</OutputClaims>
<SubjectNamingInfo ClaimType="email" Format="urn:oasis:names:tc:SAML:2.0:nameid-format:persistent" ExcludeAsClaim="true"/>
</TechnicalProfile>
</RelyingParty>
using Microsoft.AspNetCore.Mvc;
[HttpGet]
public async Task<JsonResult> Groups(string objectId)
{
List<string> groups = [your list of groups];
//!!!!!This is the trick if I sent plain json I got the error
JsonResult o = new JsonResult(
new
{
groups
});
return o;
}
正确的,按 Azure B2C rest-interface 预期的 JSON 格式,对于 stringCollection 是那个:
{
"groups": [
"Ford",
"BMW",
"Fiat"
]
}
我想通了。 API 是一个 Azure PowerShell 函数。我只需要使用 ConvertTo-Json 将我的 System.Collections.Generic.List[string] 转换为 JSON。然后,我进一步解析如下: $res = @" { "roleNames": [ $json-variable ] } "@ Stopped getting the error from the B2C REST TP after that.