【问题标题】:C# ADAL AcquireTokenAsync() without pop-up boxC# ADAL AcquireTokenAsync() 没有弹出框
【发布时间】:2016-10-26 21:38:52
【问题描述】:

我们正在编写一个必须与 Dynamics CRM 2016 Online 集成的 WCF 服务。我正在尝试使用 ADAL 进行身份验证,使用方法 AcquireTokenAsync()。问题是,它会显示一个弹出框,提示用户输入凭据。自然,我们的应用程序是一个服务,这不是我们想要的。我们一直在寻找一种无需此弹出框即可进行身份验证的方法。

有一个名为AuthenticationContextIntegratedAuthExtensions 的类,它应该有助于“用户名/密码流”。它有单一的方法AcquireTokenAsync,可以抑制弹出框,但我们还没有找到任何方法将密码传递给它。仅使用用户名运行时,它会引发基本上说“未提供密码”的异常。

有人知道如何解决这个问题吗?甚至不必是 ADAL。只是获取 OAuth 令牌的东西。

【问题讨论】:

    标签: c# wcf active-directory dynamics-crm adal


    【解决方案1】:

    好的,最终找到了解决方案。

    如果您已向 Azure AD 注册您的应用程序(作为 Web 应用程序/Web API,而不是本机应用程序),您将收到该应用程序的客户端 ID 和密钥。

    不弹窗获取token的代码如下:

    AuthenticationParameters ap = AuthenticationParameters.CreateFromResourceUrlAsync(
                            new Uri(resource+"/api/data/v8.1")).Result;
    
    AuthenticationContext ac = new AuthenticationContext(ap.Authority);
    
    AuthenticationResult r = await ac.AcquireTokenAsync(ap.Resource, new ClientCredential(clientId,clientSecret));
    

    resource 是您的 Dynamics CRM 部署的基本 URL。

    验证参数是在运行时发现的,正如MSDN acticle 中所建议的那样。

    【讨论】:

    • 您是否尝试使用此令牌对 CRM 的 webapi 进行调用?我很好奇您拥有的令牌在传递给 CRM 时是否会起作用,因为没有指定用户。如果它确实有效,那么在 CRM 中拨打谁的电话?
    • 还没有。但是,当您在 Azure AD 中注册应用程序时,您可以授予它对 Dynamics CRM 的权限。请注意,不是任何特定的 CRM,因此我假设您可以访问部署在该 Azure AD 上的任何实例。 AuthenticationParameters 是通过 ping 我们的 CRM 实例来获取的,然后我们通过将 AuthenticationParameters.Resource 传递给 AcquireTokenAsync() 来对底层 AD 进行身份验证。
    • 但是您没有作为用户进行身份验证,CRM 允许“委派权限”而不是“应用程序权限”,这意味着您需要登录用户。当您将此令牌提供给 CRM 时,您可能会收到 401。
    • 是的,未作为任何特定用户进行身份验证。我今天将向 CRM 写一些查询,看看它是否抱怨。
    • @MattDearing 你是对的。给我 401 错误。猜猜我们必须传递用户详细信息。
    【解决方案2】:
    private static string API_BASE_URL = "https://<CRM DOMAIN>.com/";
    private static string API_URL = "https://<CRM DOMAIN>.com/api/data/v8.1/";
    private static string CLIENT_ID = "<CLIENT ID>";
    
    static void Main(string[] args)
    {
        var ap = AuthenticationParameters.CreateFromResourceUrlAsync(
                    new Uri(API_URL)).Result;
    
        var authContext = new AuthenticationContext(ap.Authority, false);
    
        var userCredential = new UserCredential("<USERNAME>", "<PASSWORD>");
    
        var result = authContext.AcquireToken(API_BASE_URL, CLIENT_ID, userCredential);
    
        var httpClient = HttpWebRequest.CreateHttp(Path.Combine(API_URL, "accounts"));
        httpClient.Headers.Add(HttpRequestHeader.Authorization, "Bearer:" + result.AccessToken);
        using (var sr = new StreamReader(httpClient.GetResponse().GetResponseStream()))
        {
            Console.WriteLine(sr.ReadToEnd());
        }
    }
    

    注意:我使用的是旧版本的 ADAL (2.19.208020213),因为密码参数似乎已从 UserCredential 构造函数中取出。

    编辑: ADAL 的最新版本有UserPasswordCredential,可以用来代替UserCredential(并且可能在PasswordUserCredential 中删除后立即添加)

    编辑 2: CRM 现在支持 Server to Server Authentication,它允许您创建应用程序用户。

    【讨论】:

    • 是的。无论出于何种原因,密码字段都被删除了。事实上,我们没有找到任何与密码有关的类或方法。有些类有一个构造函数,它接受一个用户名,UserIdentifier。尽管它的目的似乎是自动完成,而不是任何静默身份验证的尝试。
    • 对,我也不确定它为什么被删除(这个博客 cloudidentity.com/blog/2014/07/08/… 是我记得第一次介绍时读过的博客),但如果你使用上面的特定版本,你可以进行身份​​验证没有弹出窗口,令牌将适用于针对 CRM 的请求。
    • 看来我们将不得不使用您的解决方案。我在这里发布的那个不允许我们访问 CRM,出现 401 错误。
    【解决方案3】:

    不使用 ADAL 的价值

    var postData = new List<KeyValuePair<string, string>>
            {
                new KeyValuePair<string, string>("resource", cred.ResourceId),
                new KeyValuePair<string, string>("grant_type", "client_credentials"),
                new KeyValuePair<string, string>("client_id", cred.ClientId),
                new KeyValuePair<string, string>("client_secret", cred.ClientSecret),
    
            };
    
            using (var client = new HttpClient()) {
    
                string baseUrl = "https://login.windows.net/YourAADInstanceName.onmicrosoft.com/oauth2/";
                client.BaseAddress = new Uri(baseUrl);                
                client.DefaultRequestHeaders.Accept.Clear();
                client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
    
                var content = new FormUrlEncodedContent(postData);
    
                HttpResponseMessage response = await client.PostAsync("token", content);               
                string jsonString = await response.Content.ReadAsStringAsync();                
                var responseData = JsonConvert.DeserializeObject<Token>(jsonString);
                return responseData;
            }
    

    【讨论】:

    • 这会验证并返回一个 accessToken 但当我使用该令牌时它会给出 401。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-09-21
    • 2017-12-10
    • 1970-01-01
    • 2019-08-10
    • 2012-08-28
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多