Camera List Taking Too Long via GetConfiguration

The ServerCommandService.asmx on 2022R3 is currently being used to pull a list of cameras for each Milestone XProtect instance. This works fine until there are thousands of cameras managed at a site with a single instance of Milestone XProtect. When there are thousands of cameras it is taking too long to use this method in an online fashion.

What API methods are available to pull the list of cameras directly instead of having to receive the GetConfiguration response that contains much more information than just the cameras?

To use Rest API is faster than GetConfiguration. I have tested RestAPI and GetConfiguration using VMS installation with 680 devices. Please see below test results. (I measured 3 times.)

1. Rest API (https://{server name}/api/rest/v1/cameras) took 193, 138, 173 (milliseconds).

2. GetConfiguration took 397, 368, 386 (milliseconds).

3. I also tested Rest API with filters (https://{server name}/api/rest/v1/cameras?includeFields=(id,displayName)

It took 113, 139, 122 (milliseconds).

Apparently, 3. is the fastest but this filter function is available only newer than 2023R1 so it is not possible for you to use unfortunately.

But, as the results shows you that Rest API(1.) is faster than GetConfiguration, so please try to use Rest API - https://doc.developer.milestonesys.com/mipvmsapi/api/config-rest/v1/

I am able to use the https://{server name}/api/rest/v1/cameras endpoint using a basic user. I am trying to determine how to invoke this endpoint using a non basic user. We utilize ANT/LDAP accounts to pull cameras using the ServerCommandService.asmx configuration since basic user accounts are not installed on the individual Milestone XProtect instances. Is there information in the documentation showing how to use non basic accounts?

See Point 6.

https://doc.developer.milestonesys.com/mipvmsapi/content/quickstart/

This works for me, using a Windows user in the administrator’s role I just have the username in the form “domainname\username”

Thank you for the response. However, prefixing the domain name and “\” to the username value does not succeed. The response is:

{
  "error":"invalid_grant",
  "error_description":"invalid_username_or_password",
  "error_code":"InvalidCredentials"
}

I am running Milestone XProtect 2022 R3. I have verified the domain name, username, and password of the Windows AD account.

I can connect to the XProtect Management Client using the same account credentials but there is a difference in that I choose “Windows authentication” in the XProtect Management Client logon form. There does not appear to be an option for specifying the authentication mode when using the /idp/connect/token endpoint. Does this option exist?

I use Postman on a Windows workstation; this means I natively have NTLM. NTLM is a requirement.

You can see more on the login in this Python sample but I think that one also works assuming you have NTLM support. https://doc.developer.milestonesys.com/html/index.html?base=samples/componentsamples/restfulcommunication/readme.html&tree=tree_3.html

Could you please provide the details of the Postman request?

I can use Postman successfully to get a token using the basic user but when setting the Postman request to use NTLM I am unsuccessful. I need additional details regarding the request body. Since NTLM authentication is being used are the username and password attributes needed? If not, why did you say you prefix the username with the “domain/”? If you could please provide the details of the Postman request that would help.

Also, curl supports ntlm (–ntlm -u username:password). Can you export your Postman request to curl and see if it succeeds?

Checking the Python example I see that it passes windows_credentials for the grant_type, GrantValidatorClient for the client_id, and does not send username or password. I have attempted to replicate this using Postman but the response is “401 - Unauthorized: Access is denied due to invalid credentials”.

I am sorry because it was not true that I could use Windows authentication like described. After retesting I now have an updated describtion that I hope works for you also.

Get Oauth token through POSTMan

  • In Postman create a POST to this url https://{server}/IDP/connect/token (http if not using Secure communication / encryption)
  • [For AD user] In Authorization, choose NTLM Authentication type from the dropdown list. Set username and password for the user you want to get a token for.
  • Add header “Content-Type” with value “application/x-www-form-urlencoded”
  • In the body, add raw body:
    • [for Basic user] grant_type=password&username=[basicuser]&password=[password]&client_id=GrantValidatorClient
    • [for AD user] grant_type=windows_credentials&client_id=GrantValidatorClient
  • Send the POST message and in the response there will be a token

Take the token from the body. We will use the token to communicate with the gateway

  • In postman make a GET to this url http://{server}/api/Rest/v1/Sites
  • In the header add “Authorization” with “Bearer {token}” and paste in the token you got
  • Send the GET message, you should get a response showing the sites

Thanks again. I was able to write a small NodeJS script using axios and axios-ntlm to authenticate via AD account credentials and pull the list of cameras.

import qs from 'qs';
import { NtlmClient } from 'axios-ntlm';
import axios from 'axios';
 
function getCameras(token) {
  const cameras = [];
  const config = {
    maxBodyLength: Infinity,
    method: 'GET',
    url: 'https://hostname/API/rest/v1/cameras',
    headers: {
      'Content-Type': 'application/x-www-form-urlencoded',
      Authorization: `Bearer ${token}`
    }
  };
  axios.request(config)
    .then(response => {
      console.log(JSON.stringify(response.data));
    })
    .catch(error => {
      throw new Error(error);
    });
  return cameras;
}
 
(async () => {
 
  let data = qs.stringify({
    'client_id': 'GrantValidatorClient',
    'grant_type': 'windows_credentials',
  });
 
  let credentials = {
    username: 'username',
    password: 'password',
    domain: 'domain'
  };
 
  let client = NtlmClient(credentials);
 
  try {
    let resp = await client({
      url: 'https://hostname/idp/connect/token',
      method: 'post',
      data: data,
    });
    console.log(resp.data);
    const token = resp.data.access_token;
    console.log(`token is ${token}`);
    getCameras(token);
  }
  catch (err) {
    console.log(err);
    console.log("Failed");
  }
})()