Доступ к 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