一直在用这种方法将我的头撞在墙上,所以希望一些CRM/Dynamics专家能帮到我!

我正在尝试使用Node Powered Express应用程序中的一组管理员凭据以编程方式从我们的Dynamics CRM实例中获取数据。此Express应用程序托管在托管CRM的网络外部的单独服务器上。然后,该应用程序将向所有有权访问(由应用程序中的角色/权限控制)的登录用户请求,处理和提供CRM数据,这意味着最终用户仅需登录Express应用程序,而不必也登录通过ADFS,以便应用访问CRM实例。

我们的CRM设置是配置为面向Internet(IFD)的本地服务器。这将使用Active Directory联合身份验证服务。我们有在网络外围运行联合服务的Web应用程序代理服务器,这些服务器与内部网络上的ADFS服务器进行通信。 ADFS根据本地AD对从网络外部(从Internet)进行连接的用户进行身份验证。一旦通过身份验证,代理将允许用户连接到CRM。

由于具有混合部署,因此本地 Activity 目录与Azure AD同步。任何O365服务(在线交换,共享点等)都在后台使用Azure AD。我们同步Active Directory,因此我们只需要在一个地方管理用户。

CRM具有端点,例如端点。 https://my.crm.endpoint,我已经在Azure门户中注册了一个应用程序(称为CRM App),其主页设置为CRM终结点https://my.crm.endpoint

问题是否将应用程序的主页设置为https://my.crm.endpoint足以将其“链接”到我们的内部CRM实例?

我已经编写了一个脚本(crm.js),该脚本使用其App ID成功请求了在Azure Portal中注册的 CRM应用程序的访问 token 。

示例 token

eyJ0dWNyIjoiMSIsImlkcCI6Imh0dHBzOi8vc3RzLndpbmRvd3MubmV0LzE5ZTk1...

然后,使用不记名 token ,尝试通过通常的终结点从Dynamics中获取一些联系人:https://my.crm.endpoint/api/data/v8.2/contacts?$select=fullname,contactid

这失败,并且我收到401 Unauthorised错误消息。

问题谁能建议这个问题是什么?和/或提供有关如何挂接Web应用程序(在我的情况下为Express)的详细信息,以向运行在使用ADFS的本地服务器(IFD)上的Dynamics CRM发出经过身份验证的请求?

crm.js
let util = require('util');
let request = require("request");

let test = {
    username: '<[email protected]>',
    password: '<my_password>',
    app_id: '<app_id>',
    secret: '<secret>',
    authenticate_url: 'https://login.microsoftonline.com/<tenant_id>/oauth2/token',
    crm_url: 'https://<my.crm.endpoint>'
};
function CRM() { }

CRM.prototype.authenticate = function () {
    return new Promise((resolve, reject) => {
        let options = {
            method: 'POST',
            url: test.authenticate_url,
            formData: {
                grant_type: 'client_credentials',
                client_id: test.app_id,         // application id
                client_secret: test.secret,     // secret
                username: test.username,        // on premise windows login (admin)
                password: test.password,        // password
                resource: test.app_id           // application id
            }
        };

        // ALWAYS RETURNS AN ACCESS_TOKEN
        request(options, function (error, response, body) {
            console.log('AUTHENTICATE RESPONSE', body);
            resolve(body);
        });
    })
};

CRM.prototype.getContacts = function (token) {
    return new Promise((resolve, reject) => {

        let options = {
            method: 'GET',
            url: `${test.crm_url}/api/data/v8.2/contacts?$select=fullname,contactid`,
            headers: {
                'Authorization': `Bearer ${token}`,
                'Accept': 'application/json',
                'OData-MaxVersion': 4.0,
                'OData-Version': 4.0,
                'Content-Type': 'application/json; charset=utf-8'
            }
        };

        request(options, (error, response, body) => {
            console.log('getContacts', util.inspect(error), util.inspect(body));
            resolve(body);
        });

    });
};

let API = new CRM();    // instantiate the CRM object

API.authenticate()      // call authenticate function
    .then(response => {
        if (response) {

            let json = JSON.parse(response);
            let token = json.access_token;

            console.log('TOKEN', token);

            API.getContacts('token')
            .then(contacts => {
                // DO SOMETHING WITH THE CONTACTS
                console.log('CONTACTS', contacts);
            })
        }
    });


module.exports = CRM;

错误响应
HTTP Error 401 - Unauthorized: Access is denied

其他信息

我当前的解决方案基于这些文档...

https://docs.microsoft.com/en-us/azure/active-directory/develop/active-directory-protocols-oauth-service-to-service

更新

遵循@ andresm53的评论,我认为我确实需要直接针对ADFS进行身份验证。我发现this blog post描述了可在OFS中使用的ADFS中生成共享 secret 。

“使用这种形式的客户端身份验证,您可以将客户端标识符(作为client_id)和客户端 secret (作为client_secret)发布到STS端点。这是这样的HTTP POST(使用“客户端证书授予”,仅添加了换行符)的示例以提高可读性):”
resource=https%3a%2f%2fmy.crm.endpoint
&client_id=**2954b462-a5de-5af6-83bc-497cc20bddde ** ???????
&client_secret=56V0RnQ1COwhf4YbN9VSkECTKW9sOHsgIuTl1FV9
&grant_type=client_credentials

更新2

现在,我已经在ADFS中创建了服务器应用程序,并且正在使用正确的client_id和client_secret发布上述有效负载。

但是,我收到了Object moved消息。
RESOLVED BODY: '<html><head><title>Object moved</title></head><body>\r\n<h2>Object moved to <a href="https://fs.our.domain.name/adfs/ls/?wa=wsignin1.0&amp;wtrealm=https%3a%2f%2fmy.crm.endpoint%2f&amp;wctx=http%253a%252f%252f2954b462-a5de-5af6-83bc-497cc20bddde%252f&amp;wct=2018-04-16T13%3a17%3a29Z&amp;wauth=urn%3afederation%3aauthentication%3awindows">here</a>.</h2>\r\n</body></html>\r\n'

问题谁能描述我在做错什么以及我应该做些什么,以便针对ADFS/CRM正确进行身份验证?

注意:当我在浏览器中访问https://my.crm.endpoint时,系统会提示我输入我的用户名和密码。输入我的信誉,我就可以使用CRM。在“网络”标签中是否注意到它正在使用NTLM来执行此操作?这会改变我需要采取的方法吗?

更新3

请查看新问题here

最佳答案

我们也有这种情况。
我们的组织是OnPrem 8.2。可通过VPN或家庭网络进行访问。
如果您以非常普通的外行方式看待问题,那么我们的CRM便无法从外部获得。
我们所做的是

  • 我们从CRM创建了Action的WebAPI。
  • 我们通过其他端口向外部世界公开了此WebAPI。
  • 我们将此WebAPI作为服务添加到IIS中。
  • 但是,我们确保只能通过在Web.config文件中创建的特定userName和Passoword来访问此WebAPI。
  • 在后台我们创建了Action。
  • 依次执行操作将运行插件,并根据要求返回数据,即可以修改WebAPI url。例如:.../acounts将返回Account实体,前提是您已在插件中内置了逻辑。
    请不要将此与Dynamics CRM OOB WebAPI混淆。我的意思是创建我们自己的API并将其作为服务使用自己的用户名和密码添加到IIS中。

  • 我认为这至少会给您一些提示,让您了解应该朝哪个方向发展。

    关于node.js - 如何从Web应用程序查询本地Dynamics CRM( Node/快捷方式),我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/49763453/

    10-16 18:05