Authentication and authorization
To use any of the SOAP services or the ImageServer protocol, you must first authenticate and login to the XProtect VMS, using the ServerCommandService to obtain authentication tokens for the session.
ServerCommandService endpoints
There are currently two ServerCommandService endpoints available:
ManagementServer/ServerCommandServiceOAuth.svcXProtect 2021R1 or later. Provides better security for Basic users if accessed over HTTPS.ManagementServer/ServerCommandService.svc
Most of the protocol samples rely on a convenience wrapper, ServerCommandWrapper, that uses ServerCommandServiceOAuth.svc if available and fall back on ServerCommandService.svc.
Authentication method
Three authentication methods are available:
- OpenID Connect and OAuth2, authenticating towards an identity provider service (IDP), either internal or external (external IDP supported in 2022 R1 and later). Authenticates users created as Basic or Windows users. Supported in XProtect 2021R1 or later.
- XProtect Basic user credentials, using HTTP Basic Authentication over HTTPS..
- Windows user credentials, using Kerberos or NTLM to authenticate towards a Windows AD domain or workgroup.
The convenience wrapper, ServerCommandWrapper used by most of the protocol samples will use ServerCommandServiceOAuth.svc if available and falls back on ServerCommandService.svc.
Register your protocol integration application
You are encouraged to register your application after each initial login. This will provide data for reporting applications used at this XProtect VMS site. The feature is available in XProtect 2020 R3 and later releases.
The ServerCommandWrapper for ServerCommandServiceOAuth.svc and ServerCommandServiceOAuth.svc includes an overloaded Login() method that takes care of the registration.
When registering your application, you provide the following information:
| Parameter | Type | Description |
|---|---|---|
| instanceId | string | Identifier uniquely identifying the calling instance. Typically, each ID should refer to a specific machine running an integration. |
| integrationId | guid | Unique identifier representing the integration. Should be hardcoded in the integrating application. |
| integrationVersion | string | Version of the calling application. |
| integrationName | string | Name of the calling application. |
| manufacturerName | string | Name of the manufacturer of the calling application |
All strings are max 256 characters.
Login and register using ServerCommandServiceOAuth.svc or ServerCommandService.svc
The TcpVideoViewer sample illustrates how a convenience wrapper, ServerCommandWrapper, can simplify authentication and login, and also how to maintain token-authenticated sessions, in this case ImageServer connections. This section highlights the initial login and subsequent re-logins; it is not a complete walk-through of the TcpVideoViewer sample.
In the TcpVideoViewer sample, the proxy class SystemAccess takes care of the connection:
- The
NtlmConnectionandBasicConnectionclasses from theServerCommandServicewrapper manages authentication and login forSystemAccess. TheNtlmConnection()andBasicConnectionmethods in theServerCommandServicewrapper usesManagementServer/ServerCommandServiceOAuth.svcif available and falls back onManagementServer/ServerCommandService.svc. - The
NtlmConnection.LoginandBasicConnection.Loginmethods are overloaded so that you can do just theLoginrequest, or both theLoginand theRegisterIntegrationrequests in oneLogincall. - The
NtlmConnection.LoginandBasicConnection.Loginmethods also set up a timer with a callback that refreshes the token and invokes anOnTokenRefreshedevent. - An event handler in
SystemAccesssubscribes toOnTokenRefreshedevents in order to refresh tokens in current sessions.
public class SystemAccess
{
private static readonly Guid IntegrationId = new Guid("BE07504F-B330-4475-9AE4-1A7FF10BD486");
private const string IntegrationName = "TCP Video Viewer";
private const string Version = "1.0";
public AuthenticationType AuthenticationType = AuthenticationType.WindowsDefault;
public String Server = "localhost";
public String User = "";
public String Password = "";
public String Domain = "";
public LoginInfo LoginInfo;
private NtlmConnection _ntlmConnection;
private BasicConnection _basicConnection;
public event EventHandler<string> OnTokenRefreshed = delegate { };
/// <summary>
/// Connect to the specified server
/// </summary>
/// <param name="server">URL of the server</param>
public void Connect(String server)
{
if (_basicConnection != null)
{
_basicConnection.OnTokenRefreshed -= _connection_OnTokenRefreshed;
_basicConnection.Logout();
_basicConnection = null;
}
if (_ntlmConnection != null)
{
_ntlmConnection.OnTokenRefreshed -= _connection_OnTokenRefreshed;
_ntlmConnection.Logout();
_ntlmConnection = null;
}
Server = server;
switch (AuthenticationType)
{
case AuthenticationType.Basic:
{
int port = 443;
_basicConnection = new BasicConnection(User, Password, Server, port);
LoginInfo = _basicConnection.Login(IntegrationId, Version, IntegrationName);
_basicConnection.OnTokenRefreshed += _connection_OnTokenRefreshed;
break;
}
case AuthenticationType.Windows:
case AuthenticationType.WindowsDefault:
{
_ntlmConnection = new NtlmConnection(Domain, AuthenticationType, User, Password, Server);
LoginInfo = _ntlmConnection.Login(IntegrationId, Version, IntegrationName);
_ntlmConnection.OnTokenRefreshed += _connection_OnTokenRefreshed;
break;
}
default:
//empty
break;
}
}
private void _connection_OnTokenRefreshed(object sender, string e)
{
OnTokenRefreshed.Invoke(this, e);
}
...
When the OnTokenRefreshed event is raised, the new token is passed on to all connections in the session.
/// <summary>
/// Interaction logic for WindowMain.xaml
/// </summary>
public partial class WindowMain : Window , INotifyPropertyChanged
{
private SystemAccess _sysInfo = new SystemAccess();
...
private ImageServerConnection _isc = null;
...
public WindowMain()
{
...
_sysInfo.OnTokenRefreshed += Token_Refreshed;
}
private void Token_Refreshed(object sender, string e)
{
TokenString = e;
...
if (_isc != null)
{
_isc.SetToken(TokenString);
// We need to update the Token on the recording server:
// This will pass the token back on the live imageserver TCP session using the CONNECTUPDATE command
// DoLiveCmd() will do nothing if there is no live sesssion
_isc.DoLiveCmd(_isc.FormatConnectUpdate());
// This will cause an active playback to pass the token back on the imageserver TCP session using the CONNECTUPDATE command
// If no playback session is active, this will cause no change.
_isc.PlaybackSendConnectUpdateFlag = true;
}
}
ImageServer requests like the connectupdate request can be wrapped in a simple string formatting function like this:
class ImageServerConnection
{
...
public string FormatConnectUpdate()
{
string sendBuffer = string.Format(
"<?xml version=\"1.0\" encoding=\"utf-8\"?><methodcall><requestid>{0}</requestid>" +
"<methodname>connectupdate</methodname>" +
"<connectparam>id={1}&connectiontoken={2}</connectparam>" +
"</methodcall>\r\n\r\n",
++_reqCounter, _cameraGuid, _token);
return sendBuffer;
}
....