Я использую Azure Active Directory для аутентификации в бэк-офисе на своем веб-сайте с Umbraco версии 11.0.
Это работает хорошо, и я могу войти в систему, но я хочу улучшить работу, используя роли приложений в Azure для управления группой пользователя в Umbraco.
Моя настройка Azure
Я создал регистрацию приложения в Azure со следующей конфигурацией:
Access tokens (used for implicit flows)
ID tokens (used for implicit and hybrid flows)
Accounts in this organizational directory only (Example only - Single tenant)
В корпоративных приложениях я также добавил своим пользователям указанные выше роли приложений:
Мой код
Провайдер входа
namespace Example.Api.Features.Authentication.Extensions;
public static class UmbracoBuilderExtensions
{
public static IUmbracoBuilder ConfigureAuthentication(this IUmbracoBuilder builder)
{
builder.Services.ConfigureOptions<OpenIdConnectBackOfficeExternalLoginProviderOptions>();
builder.AddBackOfficeExternalLogins(logins =>
{
const string schema = MicrosoftAccountDefaults.AuthenticationScheme;
logins.AddBackOfficeLogin(
backOfficeAuthenticationBuilder =>
{
backOfficeAuthenticationBuilder.AddMicrosoftAccount(
// the scheme must be set with this method to work for the back office
backOfficeAuthenticationBuilder.SchemeForBackOffice(OpenIdConnectBackOfficeExternalLoginProviderOptions.SchemeName) ?? string.Empty,
options =>
{
//By default this is '/signin-microsoft' but it needs to be changed to this
options.CallbackPath = "/umbraco-signin-microsoft/";
//Obtained from the AZURE AD B2C WEB APP
options.ClientId = "CLIENT_ID";
//Obtained from the AZURE AD B2C WEB APP
options.ClientSecret = "CLIENT_SECRET";
options.TokenEndpoint = $"https://login.microsoftonline.com/TENANT/oauth2/v2.0/token";
options.AuthorizationEndpoint = $"https://login.microsoftonline.com/TENANT/oauth2/v2.0/authorize";
});
});
});
return builder;
}
}
Автоматическое связывание аккаунтов
namespace Example.Api.Features.Configuration;
public class OpenIdConnectBackOfficeExternalLoginProviderOptions : IConfigureNamedOptions<BackOfficeExternalLoginProviderOptions>
{
public const string SchemeName = "OpenIdConnect";
public void Configure(string name, BackOfficeExternalLoginProviderOptions options)
{
if (name != "Umbraco." + SchemeName)
{
return;
}
Configure(options);
}
public void Configure(BackOfficeExternalLoginProviderOptions options)
{
options.AutoLinkOptions = new ExternalSignInAutoLinkOptions(
// must be true for auto-linking to be enabled
autoLinkExternalAccount: true,
// Optionally specify default user group, else
// assign in the OnAutoLinking callback
// (default is editor)
defaultUserGroups: new[] { Constants.Security.EditorGroupAlias },
// Optionally you can disable the ability to link/unlink
// manually from within the back office. Set this to false
// if you don't want the user to unlink from this external
// provider.
allowManualLinking: false
)
{
// Optional callback
OnAutoLinking = (autoLinkUser, loginInfo) =>
{
// You can customize the user before it's linked.
// i.e. Modify the user's groups based on the Claims returned
// in the externalLogin info
autoLinkUser.IsApproved = true;
},
OnExternalLogin = (user, loginInfo) =>
{
// You can customize the user before it's saved whenever they have
// logged in with the external provider.
// i.e. Sync the user's name based on the Claims returned
// in the externalLogin info
return true; //returns a boolean indicating if sign in should continue or not.
}
};
// Optionally you can disable the ability for users
// to login with a username/password. If this is set
// to true, it will disable username/password login
// even if there are other external login providers installed.
options.DenyLocalLogin = true;
// Optionally choose to automatically redirect to the
// external login provider so the user doesn't have
// to click the login button. This is
options.AutoRedirectLoginToExternalProvider = true;
}
}
В этом файле я бы в идеале сделал так, как говорится в комментарии и i.e. Modify the user's groups based on the Claims returned in the externalLogin info
.
Также зарегистрирован в моем файле запуска
services.AddUmbraco(_env, _config)
.AddBackOffice()
.AddWebsite()
.AddComposers()
.ConfigureAuthentication()
.Build();
Я попытался дать следующие разрешения приложению, но безуспешно:
Текущее состояние игры таково, что я могу нормально войти в систему, но если я отлажу externalInfo
, там ничего не будет о пользователях, имеющих роль приложения Administrator
или Editor
, как настроено выше.
Я чувствую, что мне что-то не хватает в настройке Azure Active Directory, но я пробовал несколько разных конфигураций и не могу вернуть роли приложений.
Спасибо,
Бен
РЕДАКТИРОВАТЬ - 15.02.2023:
Я вижу, что роли возвращаются, когда я попадаю в конечную точку https://login.microsoftonline.com/{tenantId}/oauth2/v2.0/token
, используя client_credentials
в качестве grant_type
. Похоже, что приложение .NET вместо этого использует authorization_code
. Я расшифровал токен, полученный из него, и он не содержит ролей.
Интересно, есть ли какая-то конфигурация в приложении .NET, которая позволяет мне добавлять роли.
Я так думаю, хотя я не смог найти ничего конкретно помеченного App roles
. Я обновил свой вопрос.
Когда вы создали роли приложения, тип участников: Пользователи/Группы или Оба?
Вы извлекли полученный токен из сетевой вкладки интерактивного веб-входа и проверили его содержимое на jwt.ms или jwt.io, если он содержит роли?
@Oliver Тип члена для ролей установлен на User/Group + Applications
. Я проверил возвращаемый токен, и он не содержит ролей. Хотя, если я нажму на конечную точку маркера вручную с помощью client_credentials
grant_type
, она будет содержать роли. Вместо этого в коде используется authorisation_code
, который не содержит ролей.
Чтобы получить роли приложений в утверждениях токенов, вы можете использовать поток учетных данных клиента для создания токенов доступа, предоставив согласие администратора.
Я попытался воспроизвести то же самое в своей среде через Postman и получил следующие результаты:
Я зарегистрировал одно веб-приложение Azure AD и создал роли приложения, как показано ниже:
Теперь я назначил эти роли приложений пользователям в своем корпоративном приложении, как показано ниже:
Добавьте эти роли приложения в разрешения API приложения, как показано ниже:
Вы можете увидеть роли приложений в разделе разрешений Application
, как показано ниже:
Обязательно дайте согласие администратора на вышеуказанные разрешения, например:
При создании токена доступа областью действия должен быть URI вашего идентификатора приложения, оканчивающийся на /.default
Теперь я сгенерировал токен доступа, используя поток учетных данных клиента через Postman со следующими параметрами:
POST https://login.microsoftonline.com/<tenantID>/oauth2/v2.0/token
client_id: <appID>
grant_type:client_credentials
scope: api://<appID>/.default
client_secret: secret
Ответ:
Когда я расшифровал вышеуказанный токен в jwt.ms, я успешно получил роли приложений в roles
, как показано ниже:
Обратите внимание, что роли приложения — это разрешения приложения, которые будут работать только с потоками, такими как учетные данные клиента, которые не требуют взаимодействия с пользователем.
Таким образом, если вы используете делегированные потоки, такие как поток кода авторизации, поток имени пользователя, пароля и т. д., вы не получите роли приложения в утверждениях токена.
ОБНОВЛЯТЬ:
Вы можете использовать приведенный ниже код С# для получения токена доступа из потока учетных данных клиента следующим образом:
using Microsoft.Identity.Client;
var clientID = "bbb739ad-98a4-4566-8408-dxxxxxxxx3b";
var clientSecret = "K.k8Q~hwtxxxxxxxxxxxxxxxU";
var tenantID = "fb134080-e4d2-45f4-9562-xxxxxx";
var authority = $"https://login.microsoftonline.com/{tenantID}";
var clientApplication = ConfidentialClientApplicationBuilder.Create(clientID)
.WithClientSecret(clientSecret)
.WithAuthority(authority)
.Build();
var scopes = new string[] { "api://bbb739ad-98a4-4566-8408-xxxxxx/.default" };
var authenticationResult = await clientApplication.AcquireTokenForClient(scopes)
.ExecuteAsync()
.ConfigureAwait(false);
var accesstoken = authenticationResult.AccessToken;
Console.WriteLine(accesstoken);
Ответ:
Когда я расшифровал приведенный выше токен, он имеет roles
претензию с ролями приложения, как показано ниже:
Спасибо за ответ! К сожалению, хотя это работает и приятно видеть, что роли возвращаются, но токен, полученный ASP.NET, не содержит их. И это несмотря на то, что используется тот же TokenEndpoint
, что и в Postman. Я предполагаю, что на данный момент проблема связана с реализацией ASP, а не с Azure.
Изменили ли вы параметры grant_type
на client_credentials и scope
при создании токена в ASP.NET?
Он по-прежнему использует authorization_code
как grant_type
. Я не уверен, как/могу ли я изменить их с помощью текущей настройки ASP.NET.
Чтобы решить эту проблему, я заменил AddMicrosoftAccount
AuthenticationBuilder
на AddOpenIdConnect
. Похоже, это соответствует требованиям к токенам.
Это код, который я сейчас использую в методе ConfigureAuthentication
.
public static IUmbracoBuilder ConfigureAuthentication(this IUmbracoBuilder builder)
{
// Register OpenIdConnectBackOfficeExternalLoginProviderOptions here rather than require it in startup
builder.Services.ConfigureOptions<OpenIdConnectBackOfficeExternalLoginProviderOptions>();
builder.AddBackOfficeExternalLogins(logins =>
{
logins.AddBackOfficeLogin(
backOfficeAuthenticationBuilder =>
{
backOfficeAuthenticationBuilder.AddOpenIdConnect(
// The scheme must be set with this method to work for the back office
backOfficeAuthenticationBuilder.SchemeForBackOffice(OpenIdConnectBackOfficeExternalLoginProviderOptions.SchemeName),
options =>
{
options.CallbackPath = "/umbraco-signin-microsoft/";
// use cookies
options.SignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
// pass configured options along
options.Authority = "https://login.microsoftonline.com/{tenantId}/v2.0";
options.ClientId = "{clientId}";
options.ClientSecret = "{clientSecret}";
// Use the authorization code flow
options.ResponseType = OpenIdConnectResponseType.Code;
options.AuthenticationMethod = OpenIdConnectRedirectBehavior.RedirectGet;
// map claims
options.TokenValidationParameters.NameClaimType = "name";
options.TokenValidationParameters.RoleClaimType = "role";
options.RequireHttpsMetadata = true;
options.GetClaimsFromUserInfoEndpoint = true;
options.SaveTokens = true;
options.UsePkce = true;
options.Scope.Add("email");
});
});
});
return builder;
}
Дали ли вы согласие администратора на
App roles
в своем приложении Azure AD?