1

我已成功设置多租户应用程序。现在,我能够对用户进行身份验证并使用令牌来访问其他资源。(Microsoft 图形和 Microsoft AD 图形)

现在我想让 B2B 工作。当前流程: - 用户登录 - AuthorizationCodeReceived 获取获取令牌(通过 $commonAuthority 端点) - 在为广告图请求令牌时,我使用 $tenantAuthority

当 $tenantAuthority 与创建帐户的租户权限相同时,这非常有效。

但是,如果我使用另一个用户(来自另一个租户,信任实际租户)登录并使用 $tenantAuthority = 受信任的权限,那么我总是出现以下错误:刷新令牌失败:AADSTS65001:用户或管理员未同意使用带有 ID 的应用程序

如果我将 $tenantAuthority 更改为创建用户的“源”租户权限,一切正常。

任何帮助将不胜感激。

更新:代码示例

应用程序有两个租户(租户 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);
                    }

此代码完美运行。使用来自两个租户的用户登录效果很好。

但是当我需要 Graph Service Client 或 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 的应用程序。

4

2 回答 2

1

在检索 Token 时更改 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);
}
于 2016-11-21T08:58:19.060 回答
0

我没有看到您提到过:在 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 隐式授权

于 2016-11-16T06:22:35.113 回答