我一直在撞墙,所以希望有些CRM / Dynamics专家可以帮助我!

我正在尝试以编程方式从我们的Dynamics CRM实例中获取数据,在Node驱动的Express应用程序中使用一组管理凭据 . 此Express应用程序托管在托管CRM的网络之外的单独服务器上 . 然后,应用程序将请求,处理并将CRM数据提供给任何具有访问权限的用户(由应用程序中的角色/权限控制),这意味着最终用户只需登录Express应用程序,而无需登录通过ADFS,以便应用程序访问CRM实例 .

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

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

CRM具有 endpoints ,例如 https://my.crm.endpoint 并且我在Azure门户中注册了一个应用程序(称为CRM App),主页设置为CRM endpoints https://my.crm.endpoint .

Question 将应用程序的主页设置为 https://my.crm.endpoint 足够"link"它到我们的内部CRM实例?

我编写了一个脚本(crm.js),它使用它的应用程序ID成功为Azure门户中注册的 CRM App 请求访问令牌 .

Example Token

eyJ0dWNyIjoiMSIsImlkcCI6Imh0dHBzOi8vc3RzLndpbmRvd3MubmV0LzE5ZTk1...

然后,我使用承载令牌尝试通过通常的 endpoints 从Dynamics中获取一些联系人:https://my.crm.endpoint/api/data/v8.2/contacts?$select=fullname,contactid

这失败了,我收到了 401 Unauthorised 错误消息 .

Question 任何人都可以建议问题是什么?和/或提供有关如何连接Web应用程序(在我的案例中为Express)的详细信息,以便对使用ADFS的内部部署服务器(IFD)上运行的Dynamics CRM进行身份验证请求?

crm.js

let util = require('util');
let request = require("request");

let test = {
    username: '<my.email@address.com>',
    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;

Error Response

HTTP Error 401 - Unauthorized: Access is denied

ADDITIONAL INFO

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

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

UPDATE

在@ andresm53之后's comment, I think I do need to authenticate against ADFS directly. I'发现了this blog post,它描述了在ADFS中生成可与OAuth一起使用的共享密钥 .

“使用这种形式的客户端身份验证,您可以将客户端标识符(作为client_id)和客户端密钥(作为client_secret)发送到STS endpoints . 以下是此类HTTP POST的示例(使用客户端凭据授予,仅添加了换行符)为了便于阅读):“

resource=https%3a%2f%2fmy.crm.endpoint
&client_id=**2954b462-a5de-5af6-83bc-497cc20bddde ** ???????
&client_secret=56V0RnQ1COwhf4YbN9VSkECTKW9sOHsgIuTl1FV9
&grant_type=client_credentials

UPDATE 2

我现在已经在ADFS中创建了服务器应用程序,并使用正确的client_id和client_secret对上述有效负载进行POST .

但是,我得到了一条消息 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'

QUESTION 任何人都可以描述一下我做错了什么以及我应该做些什么才能正确地对ADFS / CRM进行身份验证?

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

UPDATE 3

请参阅新问题here