Azure B2C: как получить групповое заявление в токене JWT

В Azure B2C я имел возможность получать утверждение «группы» в своих токенах JWT, следуя Получение информации о группе Azure AD с помощью JWT:

  • Откройте старый менеджер Azure (https://manage.windowsazure.com)
  • Зарегистрируйте мое приложение в B2C
  • Загрузите манифест B2C для приложения
  • В манифесте измените запись groupMembershipClaims на
    # P2 #
  • Загрузите измененный манифест B2C еще раз

Эта проблема

Это хорошо работало в прошлом (примерно месяц назад, я думаю ...), но теперь это не так. Подробнее см. Ниже ...

Что я пробовал софар

План A. Использование Azure Manager

Следуйте известному рецепту выше.

К сожалению, это больше не работает - я получаю следующую ошибку, когда этот клиент пытается аутентифицировать меня с помощью B2C:

AADB2C90068: предоставленное приложение с идентификатором '032fe196-e17d-4287-9cfd-25386d49c0d5' недействительно для этой службы. Воспользуйтесь приложением, созданным через портал B2C, и попробуйте еще раз »

Хорошо, честно - нас переводят на новый Портал.

План Б. Использование портала Azure

Следуйте старому доброму рецепту, используя новый Портал.

Но это тоже не работает - когда я дохожу до части «скачать манифест», я не могу найти никакого способа получить доступ к манифесту (и поиск в Google говорит мне, что он, вероятно, исчез навсегда ...).

План C. Смешайте портал Azure и менеджер

Немного отчаявшись, я попытался смешать планы A и B: зарегистрировать приложение с помощью нового портала, а затем изменить манифест с помощью старого Azure Manager.

Но не повезло - когда я пытаюсь загрузить манифест, он терпит неудачу с сообщением

ParameterValidationException = Предоставлены недопустимые параметры; BadRequestException = Обновления конвергентных приложений в этой версии запрещены.

План Z: использование Graph API для получения данных о членстве в группах

Просто откажитесь от утверждения о «группе» - вместо этого, когда мне понадобится информация о группе, просто запросите сервер B2C с помощью Graph API.

Я действительно, очень не хочу этого делать - это разрушило бы автономность токена доступа и сделало бы систему очень «болтливой».

Но я включил его как план Z здесь, просто чтобы сказать: да, я знаю, что вариант существует, нет, я не пробовал его - и я бы предпочел не делать этого.

Вопрос:

Как мне получить "групповое" заявление в моем токене JWT в наши дни?


person Thomas Schaumburg    schedule 03.01.2017    source источник


Ответы (1)


Боюсь, план Z. Я не знаю, почему они не возвращают его, но в настоящее время он помечены как запланированные на их портале отзывов (это элемент с наивысшим рейтингом).

Вот как я это делаю. Запрашивая группы, когда пользователь аутентифицирован, вы также можете делать это по-своему - просто запрашивайте, когда вам нужно. Зависит от вашего варианта использования.

public partial class Startup
{
    public void ConfigureAuth(IAppBuilder app)
    {
        app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
        app.UseKentorOwinCookieSaver();
        app.UseCookieAuthentication(new CookieAuthenticationOptions
        {
            LoginPath = new PathString("/account/unauthorised"),
            CookieSecure = CookieSecureOption.Always,
            ExpireTimeSpan = TimeSpan.FromMinutes(20),
            SlidingExpiration = true,
            CookieHttpOnly = true
        });

        // Configure OpenID Connect middleware for each policy
        app.UseOpenIdConnectAuthentication(CreateOptionsFromPolicy(Globals.SignInPolicyId));
    }

    private OpenIdConnectAuthenticationOptions CreateOptionsFromPolicy(string policy)
    {
        return new OpenIdConnectAuthenticationOptions
        {
            // For each policy, give OWIN the policy-specific metadata address, and
            // set the authentication type to the id of the policy
            MetadataAddress = string.Format(Globals.AadInstance, Globals.TenantName, policy),
            AuthenticationType = policy,
            AuthenticationMode = AuthenticationMode.Active,
            // These are standard OpenID Connect parameters, with values pulled from web.config
            ClientId = Globals.ClientIdForLogin,
            RedirectUri = Globals.RedirectUri,
            PostLogoutRedirectUri = Globals.RedirectUri,
            Notifications = new OpenIdConnectAuthenticationNotifications
            {
                AuthenticationFailed = AuthenticationFailed,
                SecurityTokenValidated = SecurityTokenValidated
            },
            Scope = "openid",
            ResponseType = "id_token",

            // This piece is optional - it is used for displaying the user's name in the navigation bar.
            TokenValidationParameters = new TokenValidationParameters
            {
                NameClaimType = "name",
            }
        };
    }

    private async Task SecurityTokenValidated(SecurityTokenValidatedNotification<OpenIdConnectMessage, OpenIdConnectAuthenticationOptions> token)
    {
            var groups = await _metaDataService.GetGroups(token.AuthenticationTicket.Identity.FindFirst("http://schemas.microsoft.com/identity/claims/objectidentifier").Value);

            if (groups?.Value != null && groups.Value.Any())
            {
                foreach (IGroup group in groups.Value.ToList())
                {
                    token.AuthenticationTicket.Identity.AddClaim(
                        new Claim(ClaimTypes.Role, group.DisplayName, ClaimValueTypes.String, "GRAPH"));
                }
            }
    }

    // Used for avoiding yellow-screen-of-death
    private Task AuthenticationFailed(AuthenticationFailedNotification<OpenIdConnectMessage, OpenIdConnectAuthenticationOptions> notification)
    {
        notification.HandleResponse();

        if (notification.Exception.Message == "access_denied")
        {
            notification.Response.Redirect("/");
        }
        else
        {
            notification.Response.Redirect("/error?message=" + notification.Exception.Message);
        }

        return Task.FromResult(0);
    }
}

Мой GetGroups метод просто запрашивает getMemberGroups метод в API пользователей

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

public static bool UserIsInRole(IPrincipal user, string roleName)
{
    var claims = user.Identity as ClaimsIdentity;

    if (claims == null) return false;

    return claims.FindAll(x => x.Type == ClaimTypes.Role).Any(x => x.Value == roleName);
}
person gfyans    schedule 03.01.2017
comment
Этот ответ кажется ужасно неполным. Я не понимаю шести голосов, поэтому я упускаю кое-что очевидное. Что такое _metaDataService? К какому URL вы подключаетесь? Где находится код для GetGroups (который, кажется, имеет решающее значение для понимания этого ответа)? - person Quarkly; 22.04.2018
comment
@MikeDoonsebury ознакомьтесь с этим вопросом: stackoverflow.com/questions/40302231/ - person Brett; 21.06.2018