Доступ к B2B в мультитенантном приложении

Я успешно настроил мультитенантное приложение. На данный момент я могу аутентифицировать пользователя и использовать токены для доступа к другим ресурсам. (Microsoft Graph и Microsoft AD Graph)

Теперь я хочу, чтобы B2B работал. Текущий поток: - пользователь входит в систему - AuthorizationCodeReceived получает токен получения (через конечную точку $commonAuthority) - при запросе токена для графа объявлений я использую $tenantAuthority

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

Однако, если я вхожу в систему с другим пользователем (от другого арендатора, которому доверено действительное арендатор) и использую $tenantAuthority = доверенные права доступа, я всегда получаю следующую ошибку: Сбой токена обновления: AADSTS65001: пользователь или администратор не дал согласие на использовать приложение с ID

Если я изменю $tenantAuthority на полномочия арендатора 'source', в котором был создан пользователь, все работает нормально.

Любая помощь будет принята с благодарностью.

Обновление: пример кода

Приложение имеет двух арендаторов (tenantA и tenantB), и я буду использовать пользователя из tenantB, которому tenantA будет доверять этот пользователь.

AuthorizationCodeReceived = async context =>
                    {
                        TenantContext.TenantId = "someguid";
                        var tenantId =
                            TenantContext.TenantId;

                        // get token cache via func, because the userid is only known at runtime
                        var getTokenCache = container.Resolve<Func<string, TokenCache>>();
                        var userId = context.AuthenticationTicket.Identity.FindFirst(ClaimTypes.ObjectIdentifier).Value;
                        var tokenCache = getTokenCache(userId);
                        var authenticationContext = new AuthenticationContext($"{configuration.Authority}",
                            tokenCache);

                        await authenticationContext.AcquireTokenByAuthorizationCodeAsync(
                            context.Code,
                            new Uri(context.Request.Uri.GetLeftPart(UriPartial.Authority)),
                            new ClientCredential(configuration.ClientId, configuration.ClientSecret),
                            configuration.GraphResourceId);
                    }

Этот код работает отлично. Вход с пользователем от обоих арендаторов работает отлично.

Но когда мне нужен клиент службы Graph или ActiveDirectoryClient, мне нужно получить токены доступа, чтобы иметь возможность обращаться к API для определенного арендатора. Я получаю токены доступа следующим образом:

public IGraphServiceClient CreateGraphServiceClient()
    {
        var client = new GraphServiceClient(
            new DelegateAuthenticationProvider(
                async requestMessage =>
                {
                    Logger.Debug("Retrieving authentication token to use in Microsoft Graph.");

                    string token;
                    var currentUserHomeTenantId = TenantContext.TenantId;
                    var currentUserObjectId = ClaimsPrincipal.Current.FindFirst(ClaimTypes.ObjectIdentifier).Value;
                    var authenticationContext =
                        new AuthenticationContext($"{_configuration.TenantAuthorityPrefix}{currentUserHomeTenantId}",
                            _tokenCacheFactoryMethod(currentUserObjectId));
                    var clientCredential = new ClientCredential(_configuration.ClientId, _configuration.ClientSecret);

                    try
                    {
                        token = await GetTokenSilently(authenticationContext, _configuration.GraphResourceId, currentUserObjectId);
                    }
                    catch (AdalSilentTokenAcquisitionException e)
                    {
                        Logger.Error("Failed to retrieve authentication token silently, trying to refresh the token.", e);
                        var result = await authenticationContext.AcquireTokenAsync(_configuration.GraphResourceId, clientCredential);
                        token = result.AccessToken;
                    }

                    requestMessage.Headers.Authorization = new AuthenticationHeaderValue(AuthenticationHeaderKeys.Bearer, token);
                }));

        return client;
    }


    public IActiveDirectoryClient CreateAdClient()
    {
        var currentUserHomeTenantId = TenantContext.TenantId;
        var currentUserObjectId = ClaimsPrincipal.Current.FindFirst(ClaimTypes.ObjectIdentifier).Value;
        var graphServiceUrl = $"{_configuration.AdGraphResourceId}/{currentUserHomeTenantId}";
        var tokenCache = _tokenCacheFactoryMethod(currentUserObjectId);

        var client = new ActiveDirectoryClient(new Uri(graphServiceUrl),
            () => GetTokenSilently(
                new AuthenticationContext(
                    $"{_configuration.TenantAuthorityPrefix}{ClaimsPrincipal.Current.FindFirst(ClaimTypes.TenantId).Value}", tokenCache
                ),
                _configuration.AdGraphResourceId, currentUserObjectId
            ));

        return client;
    }

Когда я выполняю запрос с одним из двух клиентских SDK, я получаю следующую ошибку: Сбой токена обновления: AADSTS65001: пользователь или администратор не согласились использовать приложение с идентификатором.

2 ответа

Решение

Смена метода catch при получении токена сделала свое дело:

if(e.ErrorCode == "failed_to_acquire_token_silently")
{
    HttpContext.Current.Response.Redirect(authenticationContext.GetAuthorizationRequestUrlAsync(resourceId, _configuration.ClientId, new Uri(currentUrl),
                        new UserIdentifier(currentUserId, UserIdentifierType.UniqueId), string.Empty);
}

Я не вижу, чтобы вы упоминали об этом так: в сотрудничестве с B2B вы сначала должны пригласить пользователя от другого арендатора. Шаги таковы:

  • пригласить и авторизовать набор внешних пользователей, загрузив значения через запятую - CSV-файл
  • Приглашение будет отправлено внешним пользователям.
  • Приглашенный пользователь либо войдет в существующую рабочую учетную запись в Microsoft (управляемую в Azure AD), либо получит новую рабочую учетную запись в Azure AD.
  • После входа пользователь будет перенаправлен в приложение, которое им предоставлено.

Это прекрасно работает в моем случае.

Что касается некоторых проблем, которые я обнаружил:

Завершая "/" в конце ресурса активного каталога - попробуйте удалить его, так как это может вызвать проблемы. Ниже вы найдете код для получения заголовков аутентификации:

string aadTenant = WebServiceClientConfiguration.Settings.ActiveDirectoryTenant;
string clientAppId = WebServiceClientConfiguration.Settings.ClientAppId;
string clientKey = WebServiceClientConfiguration.Settings.ClientKey;
string aadResource = WebServiceClientConfiguration.Settings.ActiveDirectoryResource;

AuthenticationContext authenticationContext = new AuthenticationContext(aadTenant);
ClientCredential clientCredential = new ClientCredential(clientAppId, clientKey);
UserPasswordCredential upc = new UserPasswordCredential(WebServiceClientConfiguration.Settings.UserName, WebServiceClientConfiguration.Settings.Password);

AuthenticationResult authenticationResult = await authenticationContext.AcquireTokenAsync(aadResource, clientAppId, upc);

return authenticationResult.CreateAuthorizationHeader();

Приложения, подготовленные в Azure AD, по умолчанию не могут использовать неявное предоставление OAuth2. Вы должны явно указать - более подробную информацию можно найти здесь: неявное предоставление Azure AD OAuth2

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