Как сделать так, чтобы роли приложений Azure Active Directory отображались в заявках?

Я использую Azure Active Directory для аутентификации в бэк-офисе на своем веб-сайте с Umbraco версии 11.0.

Это работает хорошо, и я могу войти в систему, но я хочу улучшить работу, используя роли приложений в Azure для управления группой пользователя в Umbraco.

Моя настройка Azure

Я создал регистрацию приложения в Azure со следующей конфигурацией:

  • Добавлен URI перенаправления:
  • URI: https://localhost:44391/umbraco-signin-microsoft/
  • Включено 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 в своем приложении Azure AD?

Sridevi 13.02.2023 13:14

Я так думаю, хотя я не смог найти ничего конкретно помеченного App roles. Я обновил свой вопрос.

0Neji 13.02.2023 17:47

Когда вы создали роли приложения, тип участников: Пользователи/Группы или Оба?

Oliver 14.02.2023 11:31

Вы извлекли полученный токен из сетевой вкладки интерактивного веб-входа и проверили его содержимое на jwt.ms или jwt.io, если он содержит роли?

Oliver 14.02.2023 11:34

@Oliver Тип члена для ролей установлен на User/Group + Applications. Я проверил возвращаемый токен, и он не содержит ролей. Хотя, если я нажму на конечную точку маркера вручную с помощью client_credentialsgrant_type, она будет содержать роли. Вместо этого в коде используется authorisation_code, который не содержит ролей.

0Neji 15.02.2023 16:51
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
4
5
117
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Чтобы получить роли приложений в утверждениях токенов, вы можете использовать поток учетных данных клиента для создания токенов доступа, предоставив согласие администратора.

Я попытался воспроизвести то же самое в своей среде через 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.

0Neji 15.02.2023 16:20

Изменили ли вы параметры grant_type на client_credentials и scope при создании токена в ASP.NET?

Sridevi 15.02.2023 16:25

Он по-прежнему использует authorization_code как grant_type. Я не уверен, как/могу ли я изменить их с помощью текущей настройки ASP.NET.

0Neji 15.02.2023 16:29
Ответ принят как подходящий

Чтобы решить эту проблему, я заменил AddMicrosoftAccountAuthenticationBuilder на 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;
}

Другие вопросы по теме