[英]Azure B2c security groups authorization through custom policy using Rest Post call
我正在嘗試使用能夠部署 REST API 的自定義策略來獲取組聲明
<ClaimsSchema>
<ClaimType Id="groups">
<DisplayName>B2C-user-test,B2C-admin-test</DisplayName>
<DataType>stringCollection</DataType>
</ClaimType>
<!--Demo: List of permitted security groups user can sign-in.
Null or empty means, user any user can sign-in.
This claim sends to REST API-->
<ClaimType Id="onlyMembersOf">
<DisplayName>onlyMembersOf</DisplayName>
<DataType>string</DataType>
</ClaimType>
</ClaimsSchema>
</BuildingBlocks>
<ClaimsProviders>
<ClaimsProvider>
<DisplayName>Local Account</DisplayName>
<TechnicalProfiles>
<TechnicalProfile Id="SelfAsserted-LocalAccountSignin-Email">
<OutputClaims>
<!--Demo: Add the groups claim type to the output claim collection-->
<OutputClaim ClaimTypeReferenceId="groups" />
</OutputClaims>
<ValidationTechnicalProfiles>
<!-- Demo: Make sure you first call the login-NonInteractive technical profile, to get the user ID.
Then call the role-based access control REST API to get adn validate user's groups -->
<ValidationTechnicalProfile ReferenceId="login-NonInteractive" />
<ValidationTechnicalProfile ReferenceId="REST-RBAC" />
</ValidationTechnicalProfiles>
</TechnicalProfile>
</TechnicalProfiles>
</ClaimsProvider>
<!-- Local account Sign-In claims provider -->
<ClaimsProvider>
<DisplayName>Local Account SignIn</DisplayName>
<TechnicalProfiles>
<TechnicalProfile Id="login-NonInteractive">
<Metadata>
<Item Key="client_id">444b09a2-0f8b-4f05-b454-54495b5ef601</Item>
<Item Key="IdTokenAudience">bd80807b-81d0-4732-a517-1132b128206c</Item>
</Metadata>
<InputClaims>
<InputClaim ClaimTypeReferenceId="client_id" DefaultValue="444b09a2-0f8b-4f05-b454-54495b5ef601" />
<InputClaim ClaimTypeReferenceId="resource_id" PartnerClaimType="resource" DefaultValue="bd80807b-81d0-4732-a517-1132b128206c" />
</InputClaims>
</TechnicalProfile>
</TechnicalProfiles>
</ClaimsProvider>
<ClaimsProvider>
<DisplayName>REST APIs</DisplayName>
<TechnicalProfiles>
<TechnicalProfile Id="REST-RBAC">
<DisplayName>Read and validate user's groups</DisplayName>
<Protocol Name="Proprietary" Handler="Web.TPEngine.Providers.RestfulProvider, Web.TPEngine, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
<Metadata>
<!--Demo: Change the service URL with your REST API location-->
<Item Key="ServiceUrl">https://test.azurewebsites.net/api/Identity/IsMemberOf</Item>
<!--Demo: Change the AuthenticationType to basic or ClientCertificate.
For more information, see: https://docs.microsoft.com/en-us/azure/active-directory-b2c/active-directory-b2c-custom-rest-api-netfw-secure-cert-->
<Item Key="AuthenticationType">None</Item>
<Item Key="SendClaimsIn">Body</Item>
<Item Key="AllowInsecureAuthInProduction">true</Item>
</Metadata>
<CryptographicKeys>
<Key Id="BasicAuthenticationUsername" StorageReferenceId="B2C_1A_RestApiUsername" />
<Key Id="BasicAuthenticationPassword" StorageReferenceId="B2C_1A_RestApiPassword" />
</CryptographicKeys>
<InputClaims>
<InputClaim ClaimTypeReferenceId="objectId" />
<!--Demo: set the DefaultValue to empty string or comma delimiter list
of security groups to validate-->
<InputClaim ClaimTypeReferenceId="onlyMembersOf" DefaultValue="B2C-user-test" />
</InputClaims>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="groups" />
</OutputClaims>
<UseTechnicalProfileForSessionManagement ReferenceId="SM-Noop" />
</TechnicalProfile>
</TechnicalProfiles>
</ClaimsProvider>
</ClaimsProviders>
所以上面的 TrustFrameworkExtensions 策略調用 Rest 方法,但它返回 catch 語句異常
catch (Exception ex)
{
if (ex.Message.Contains("Request_ResourceNotFound"))
{
return StatusCode((int)HttpStatusCode.Conflict, new B2CResponseModel("Can not read user groups, user not found", HttpStatusCode.Conflict));
}
return StatusCode((int)HttpStatusCode.Conflict, new B2CResponseModel("Can not read user groups", HttpStatusCode.Conflict));
}"
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading.Tasks;
using AADB2C.RBAC.Sample.Models;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Options;
using System.Net.Http;
namespace AADB2C.RBAC.Sample.Controllers
{
[Route("api/[controller]/[action]")]
public class IdentityController : Controller
{
private readonly AppSettingsModel AppSettings;
// Demo: Inject an instance of an AppSettingsModel class into the constructor of the consuming class,
// and let dependency injection handle the rest
public IdentityController(IOptions<AppSettingsModel> appSettings)
{
this.AppSettings = appSettings.Value;
}
[HttpPost(Name = "IsMemberOf")]
public async Task<ActionResult> IsMemberOf()
{
string input = null;
// If not data came in, then return
if (this.Request.Body == null)
{
return StatusCode((int)HttpStatusCode.Conflict, new B2CResponseModel("Request content is null", HttpStatusCode.Conflict));
}
//Read the input claims from the request body
using (StreamReader reader = new StreamReader(Request.Body, Encoding.UTF8))
{
input = await reader.ReadToEndAsync();
}
//string input = Request.Content.ReadAsStringAsync().Result;
//string content = "";
//System.Web.HttpContext.Current.Request.InputStream.Position = 0;
//using (var reader = new StreamReader(
// Request.InputStream, System.Text.Encoding.UTF8, true, 4096, true))
//{
// content = reader.ReadToEnd();
//}
////Rest
//System.Web.HttpContext.Current.Request.InputStream.Position = 0;
// Check input content value
if (string.IsNullOrEmpty(input))
{
return StatusCode((int)HttpStatusCode.Conflict, new B2CResponseModel("Request content is empty", HttpStatusCode.Conflict));
}
// Convert the input string into InputClaimsModel object
InputClaimsModel inputClaims = InputClaimsModel.Parse(input);
if (inputClaims == null)
{
return StatusCode((int)HttpStatusCode.Conflict, new B2CResponseModel("Can not deserialize input claims", HttpStatusCode.Conflict));
}
if (string.IsNullOrEmpty(inputClaims.objectId))
{
return StatusCode((int)HttpStatusCode.Conflict, new B2CResponseModel("User 'objectId' is null or empty", HttpStatusCode.Conflict));
}
try
{
AzureADGraphClient azureADGraphClient = new AzureADGraphClient(this.AppSettings.Tenant, this.AppSettings.ClientId, this.AppSettings.ClientSecret);
// Demo: Get user's groups
GraphGroupsModel groups = await azureADGraphClient.GetUserGroup(inputClaims.objectId);
// Demo: Add the groups to string collections
List<string> groupsList = new List<string>();
foreach (var item in groups.value)
{
groupsList.Add(item.displayName);
}
// Demo: Set the output claims
OutputClaimsModel output = new OutputClaimsModel() { groups = groupsList };
// Demo: Check if user needs to be a member of a security group
if (!string.IsNullOrEmpty(inputClaims.onlyMembersOf))
{
List<string> onlyMembersOf = inputClaims.onlyMembersOf.ToLower().Split(',').ToList<string>();
bool isMemberOf = false;
foreach (var item in output.groups)
{
if (onlyMembersOf.Contains(item.ToLower()))
{
isMemberOf = true;
break;
}
}
// Demo: Throw error if user is not member of one of the security groups
if (isMemberOf == false)
{
return StatusCode((int)HttpStatusCode.Conflict, new B2CResponseModel("You are not authorized to sign-in to this application.", HttpStatusCode.Conflict));
}
}
// Demo: Return the groups collection
return Ok(output);
}
catch (Exception ex)
{
if (ex.Message.Contains("Request_ResourceNotFound"))
{
return StatusCode((int)HttpStatusCode.Conflict, new B2CResponseModel("Can not read user groups, user not found", HttpStatusCode.Conflict));
}
return StatusCode((int)HttpStatusCode.Conflict, new B2CResponseModel("Can not read user groups", HttpStatusCode.Conflict));
}
}
}
}
所以我已經達到了自定義策略使用 REST POST 方法來獲取組詳細信息的地步,但是提到的這段代碼沒有提供組詳細信息,因為它去 catch 語句並且拋出無法讀取用戶組。這里的問題是我不能使用 localhost運行 Rest API 並通過自定義策略命中 API 的斷點,因為它不受支持,我嘗試使用返回。
任何幫助或示例都會非常有幫助
我的做法有點不同,但我的方法比使用與你的類似代碼更幸運,我在許多示例中也發現了這種代碼。 我也更喜歡這種方法,而不是 mrochon.azurewebsites.net 上經常引用的指南,因為這不需要設置應用程序注冊,也不需要在任何地方存儲機密,並且對於多租戶應用程序效果更好。
我所做的是將以下 OutputClaim 添加到針對 login.microsoftonline.com 進行登錄的技術配置文件中。 這為我們提供了身份提供者提供的用戶令牌。 然后我們以后可以使用這個token直接查詢Graph API。
<OutputClaim ClaimTypeReferenceId="identityProviderAccessToken" PartnerClaimType="{oauth2:access_token}" />
然后,我使用另一個 OrchestrationStep 調用我的 TechnicalProfile,它執行 REST POST。 我的 TechnicalProfile 看起來像這樣:
<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://PATH-TO-YOUR-REST-API/groups</Item>
<Item Key="AuthenticationType">None</Item>
<Item Key="SendClaimsIn">Body</Item>
</Metadata>
<InputClaims>
<InputClaim ClaimTypeReferenceId="objectId" /><!-- optional -->
<InputClaim ClaimTypeReferenceId="tenantId" /><!-- optional -->
<InputClaim ClaimTypeReferenceId="identityProviderAccessToken" />
</InputClaims>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="groups" />
</OutputClaims>
<UseTechnicalProfileForSessionManagement ReferenceId="SM-Noop" />
</TechnicalProfile>
用戶令牌將傳送到您的 REST API 同名: identityProviderAccessToken
這個token可以用來直接查找Graph API,所以只需要這樣的代碼(roleGroups.Keys用來限制我們想要返回的組):
var graphService = await GraphService.CreateOnBehalfOfUserAsync(inputClaims.identityProviderAccessToken);
var memberGroups = await graphService.CheckMemberGroupsDelegateAsync(roleGroups.Keys);
這是我在上面使用的 GraphService:
public class GraphService
{
private readonly IGraphServiceClient _client;
private GraphService(IGraphServiceClient client)
{
_client = client;
}
public static async Task<GraphService> CreateOnBehalfOfUserAsync(string userToken)
{
GraphServiceClient graphClient = new GraphServiceClient(
"https://graph.microsoft.com/v1.0",
new DelegateAuthenticationProvider(async (requestMessage) =>
{
requestMessage.Headers.Authorization = new AuthenticationHeaderValue("bearer", userToken);
}));
return new GraphService(graphClient);
}
public async Task<IEnumerable<string>> CheckMemberGroupsDelegateAsync(IEnumerable<string> groupIds)
{
//You can check up to a maximum of 20 groups per request (see graph api doc).
var batchSize = 20;
var tasks = new List<Task<IDirectoryObjectCheckMemberGroupsCollectionPage>>();
foreach (var groupsBatch in groupIds.Batch(batchSize))
{
tasks.Add(_client.Me.CheckMemberGroups(groupsBatch).Request().PostAsync());
}
await Task.WhenAll(tasks);
return tasks.SelectMany(x => x.Result.ToList());
}
}
如果您需要擴展來對結果進行批處理,這也是:
public static IEnumerable<IEnumerable<T>> Batch<T>(this IEnumerable<T> items,
int maxItems)
{
return items.Select((item, inx) => new { item, inx })
.GroupBy(x => x.inx / maxItems)
.Select(g => g.Select(x => x.item));
}
祝你好運。 在這些自定義政策中,這是一次瘋狂的旅程。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.