多租户应用程序中的 B2B 访问
B2B access in multi tenant application
我已成功设置多租户应用程序。
现在,我能够对用户进行身份验证并使用令牌访问其他资源。 (Microsoft Graph 和 Microsoft AD Graph)
现在我想让 B2B 工作。
当前流量:
- 用户登录
- AuthorizationCodeReceived 获取令牌(通过 $commonAuthority 端点)
- 为广告图表请求令牌时,我使用的是 $tenantAuthority
当 $tenantAuthority 与创建帐户的租户权限相同时,这非常有效。
但是,如果我使用另一个用户登录(来自另一个租户,信任实际租户)并使用 $tenantAuthority = trusted authority,那么我总是会出现以下错误:
刷新令牌失败:
AADSTS65001:用户或管理员未同意使用 ID 为
的应用程序
如果我将 $tenantAuthority 更改为创建用户的 'source' 租户权限,一切正常。
如有任何帮助,我们将不胜感激。
更新:代码示例
应用程序有两个租户(租户 A 和租户 B),我将使用来自租户 B 的用户,租户 A 信任该用户。
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);
}
此代码完美运行。使用来自两个租户的用户登录完美无缺。
但是当我需要图形服务客户端或 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:用户或管理员未同意使用具有 ID 的应用程序。
我没看到你这么提:在 B2B 协作中,你必须先邀请其他租户的用户。步骤是这样的:
- 通过上传 comma-separated 值 - CSV 文件邀请和授权一组外部用户
- 将向外部用户发送邀请。
- 受邀用户将登录现有的 Microsoft 工作帐户(在 Azure AD 中管理),或在 Azure AD 中获取新的工作帐户。
- 登录后,用户将被重定向到与他们共享的应用程序
这对我来说非常有效。
关于我发现的一些问题:
活动目录资源末尾的“/” - 尝试删除它,因为这可能会导致问题。您将在下面找到一些代码来获得身份验证 headers:
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 implicit grant
在检索令牌时更改 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);
}
我已成功设置多租户应用程序。 现在,我能够对用户进行身份验证并使用令牌访问其他资源。 (Microsoft Graph 和 Microsoft AD Graph)
现在我想让 B2B 工作。 当前流量: - 用户登录 - AuthorizationCodeReceived 获取令牌(通过 $commonAuthority 端点) - 为广告图表请求令牌时,我使用的是 $tenantAuthority
当 $tenantAuthority 与创建帐户的租户权限相同时,这非常有效。
但是,如果我使用另一个用户登录(来自另一个租户,信任实际租户)并使用 $tenantAuthority = trusted authority,那么我总是会出现以下错误: 刷新令牌失败: AADSTS65001:用户或管理员未同意使用 ID 为
的应用程序如果我将 $tenantAuthority 更改为创建用户的 'source' 租户权限,一切正常。
如有任何帮助,我们将不胜感激。
更新:代码示例
应用程序有两个租户(租户 A 和租户 B),我将使用来自租户 B 的用户,租户 A 信任该用户。
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);
}
此代码完美运行。使用来自两个租户的用户登录完美无缺。
但是当我需要图形服务客户端或 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:用户或管理员未同意使用具有 ID 的应用程序。
我没看到你这么提:在 B2B 协作中,你必须先邀请其他租户的用户。步骤是这样的:
- 通过上传 comma-separated 值 - CSV 文件邀请和授权一组外部用户
- 将向外部用户发送邀请。
- 受邀用户将登录现有的 Microsoft 工作帐户(在 Azure AD 中管理),或在 Azure AD 中获取新的工作帐户。
- 登录后,用户将被重定向到与他们共享的应用程序
这对我来说非常有效。
关于我发现的一些问题:
活动目录资源末尾的“/” - 尝试删除它,因为这可能会导致问题。您将在下面找到一些代码来获得身份验证 headers:
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 implicit grant
在检索令牌时更改 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);
}