Я пытаюсь выяснить, как разрешить использование групп в Azure Active Directory B2C. Я могу авторизовать через пользователя, например:
Однако это не очень эффективно, и я вижу очень мало случаев использования для этого. Альтернативным решением будет разрешение через роль. Однако по какой-то причине это не похоже на wowrk. Если я даю пользователю роль "Глобальный администратор", например, и попробуйте:
Это не работает. Есть ли способ авторизации через группы или роли?
Ответ 1
Это будет работать, однако вы должны написать пару строк кода в своей логике аутентификации, чтобы добиться того, что вы ищете.
Прежде всего, вы должны различать Roles
и Groups
в Azure AD (B2C).
User Role
очень специфичен и действителен только в пределах Azure AD (B2C). Роль определяет, какие разрешения внутри Azure AD имеет пользователь.
Group
(или Security Group
) определяет членство в группе пользователей, которое может быть подвержено внешним приложениям. Внешние приложения могут моделировать управление доступом на основе ролей поверх групп безопасности. Да, я знаю, это может показаться немного запутанным, но что это такое.
Итак, ваш первый шаг - смоделировать ваш Groups
в Azure AD B2C - вам нужно создать группы и вручную назначить пользователей этим группам. Вы можете сделать это на портале Azure (https://portal.azure.com/):
![введите описание изображения здесь]()
Затем вернемся к вашему приложению, вам придется немного закодировать код и спросить Azure AD B2C Graph API для членства пользователей после того, как пользователь успешно завершена. Вы можете использовать этот пример, чтобы получить представление о том, как получить членство в группах пользователей. Лучше всего выполнить этот код в одном из уведомлений OpenID (т.е. SecurityTokenValidated) и добавить роль пользователей в ClaimsPrincipal.
Как только вы измените ClaimsPrincipal на наличие групп безопасности Azure AD и значений "Role Claim", вы сможете использовать атрибут Authrize с функцией "Роли". Это действительно 5-6 строк кода.
Наконец, вы можете проголосовать за эту функцию здесь: https://feedback.azure.com/forums/169401-azure-active-directory/suggestions/10123836-get-user-membership-groups-in-the-claims-with-ad-b, чтобы получить заявку на членство в группе без запроса API-интерфейса Graph для этого.
Ответ 2
Получение членства в группах для пользователя из Azure AD требует немного больше, чем просто "пара строк кода", поэтому я решил поделиться тем, что в конечном итоге помогло мне сохранить других в течение нескольких дней притягивания волос и головокружение.
Начнем с добавления следующих зависимостей к project.json:
"dependencies": {
...
"Microsoft.IdentityModel.Clients.ActiveDirectory": "3.13.8",
"Microsoft.Azure.ActiveDirectory.GraphClient": "2.0.2"
}
Первый необходим, поскольку нам необходимо аутентифицировать наше приложение, чтобы он мог получить доступ к API-интерфейсу AAD Graph.
Вторая - это клиентская библиотека Graph API, которую мы будем использовать для запросов пользователей.
Само собой разумеется, что версии действительны только на момент написания этой статьи и могут измениться в будущем.
Далее, в методе Configure() класса Startup, возможно, непосредственно перед настройкой аутентификации OpenID Connect мы создаем клиент Graph API следующим образом:
var authContext = new AuthenticationContext("https://login.microsoftonline.com/<your_directory_name>.onmicrosoft.com");
var clientCredential = new ClientCredential("<your_b2c_app_id>", "<your_b2c_secret_app_key>");
const string AAD_GRAPH_URI = "https://graph.windows.net";
var graphUri = new Uri(AAD_GRAPH_URI);
var serviceRoot = new Uri(graphUri, "<your_directory_name>.onmicrosoft.com");
this.aadClient = new ActiveDirectoryClient(serviceRoot, async () => await AcquireGraphAPIAccessToken(AAD_GRAPH_URI, authContext, clientCredential));
ПРЕДУПРЕЖДЕНИЕ: НЕ ЗАПРЕЩАЕТСЯ программировать секретный ключ приложения, а вместо этого храните его в надежном месте. Ну, ты уже это знал, да?:)
Асинхронный метод AcquireGraphAPIAccessToken(), который мы передали конструктору клиента AD, будет вызываться по мере необходимости, когда клиенту необходимо получить токен аутентификации. Вот как выглядит метод:
private async Task<string> AcquireGraphAPIAccessToken(string graphAPIUrl, AuthenticationContext authContext, ClientCredential clientCredential)
{
AuthenticationResult result = null;
var retryCount = 0;
var retry = false;
do
{
retry = false;
try
{
// ADAL includes an in-memory cache, so this will only send a request if the cached token has expired
result = await authContext.AcquireTokenAsync(graphAPIUrl, clientCredential);
}
catch (AdalException ex)
{
if (ex.ErrorCode == "temporarily_unavailable")
{
retry = true;
retryCount++;
await Task.Delay(3000);
}
}
} while (retry && (retryCount < 3));
if (result != null)
{
return result.AccessToken;
}
return null;
}
Обратите внимание, что он имеет встроенный механизм повтора для обработки переходных условий, которые вы, возможно, захотите адаптировать к потребностям вашего приложения.
Теперь, когда мы позаботились о проверке подлинности приложения и настройке клиента AD, мы можем перейти к событиям OpenIdConnect, чтобы, наконец, использовать его.
Вернемся в метод Configure(), где мы обычно вызываем app.UseOpenIdConnectAuthentication()
и создаем экземпляр OpenIdConnectOptions, добавляем обработчик событий для события OnTokenValidated:
new OpenIdConnectOptions()
{
...
Events = new OpenIdConnectEvents()
{
...
OnTokenValidated = SecurityTokenValidated
},
};
Событие запускается, когда маркер доступа для пользователя входа в систему был получен, подтвержден и установлена идентификация пользователя. (Не путать с токеном доступа к приложению, необходимым для вызова API диаграммы AAD!)
Это выглядит как хорошее место для запроса API-интерфейса Graph для членства в группах пользователей и добавления этих групп в личность в виде дополнительных требований:
private Task SecurityTokenValidated(TokenValidatedContext context)
{
return Task.Run(async () =>
{
var oidClaim = context.SecurityToken.Claims.FirstOrDefault(c => c.Type == "oid");
if (!string.IsNullOrWhiteSpace(oidClaim?.Value))
{
var pagedCollection = await this.aadClient.Users.GetByObjectId(oidClaim.Value).MemberOf.ExecuteAsync();
do
{
var directoryObjects = pagedCollection.CurrentPage.ToList();
foreach (var directoryObject in directoryObjects)
{
var group = directoryObject as Group;
if (group != null)
{
((ClaimsIdentity)context.Ticket.Principal.Identity).AddClaim(new Claim(ClaimTypes.Role, group.DisplayName, ClaimValueTypes.String));
}
}
pagedCollection = pagedCollection.MorePagesAvailable ? await pagedCollection.GetNextPageAsync() : null;
}
while (pagedCollection != null);
}
});
}
Используется здесь тип заявки на роль, однако вы можете использовать пользовательский.
Сделав это, если вы используете ClaimType.Role, все, что вам нужно сделать, это украсить ваш класс или метод контроллера следующим образом:
[Authorize(Role = "Administrators")]
Это, конечно, при условии, что у вас есть назначенная группа, настроенная в B2C с отображаемым именем "Администраторы".
Если, однако, вы решили использовать тип пользовательских требований, вам нужно будет определить политику авторизации на основе типа претензии, добавив что-то вроде этого в метод ConfigureServices(), например:
services.AddAuthorization(options => options.AddPolicy("ADMIN_ONLY", policy => policy.RequireClaim("<your_custom_claim_type>", "Administrators")));
а затем украсить класс или метод привилегированного контроллера следующим образом:
[Authorize(Policy = "ADMIN_ONLY")]
Хорошо, мы все-таки сделали? - Ну, не совсем.
Если вы запустили приложение и попытались войти в систему, вы получите исключение из Graph API, требующее "Недостаточно привилегий для завершения операции".
Это может быть не очевидно, но пока ваше приложение успешно аутентифицируется с помощью AD с помощью app_id и app_key, у него нет привилегий, необходимых для чтения сведений о пользователях из вашего AD.
Чтобы предоставить приложение такой доступ, я решил использовать Azure Active Directory Module для PowerShell
Следующий script помогло:
$tenantGuid = "<your_tenant_GUID>"
$appID = "<your_app_id>"
$userVal = "<admin_user>@<your_AD>.onmicrosoft.com"
$pass = "<admin password in clear text>"
$Creds = New-Object System.Management.Automation.PsCredential($userVal, (ConvertTo-SecureString $pass -AsPlainText -Force))
Connect-MSOLSERVICE -Credential $Creds
$msSP = Get-MsolServicePrincipal -AppPrincipalId $appID -TenantID $tenantGuid
$objectId = $msSP.ObjectId
Add-MsolRoleMember -RoleName "Company Administrator" -RoleMemberType ServicePrincipal -RoleMemberObjectId $objectId
И теперь мы, наконец, закончили!
Как это для "пары строк кода"?:)