Skip to main content

Delegated Authentication

About Delegated Authentication

Delegated Authentication Background

InterSystems IRIS supports delegated authentication, which allows you to implement custom mechanisms to replace the authentication and role-management activities that are part of InterSystems security, for example, an enterprise’s existing authentication system. As the application developer, you fully control the content of delegated authentication code. Delegated authentication occurs if an instance of InterSystems IRIS finds a ZAUTHENTICATE routine in its %SYS namespace. If such a routine exists, InterSystems IRIS uses it to authenticate users, either with calls to new or existing code. InterSystems IRIS includes a routine, ZAUTHENTICATE.mac, that serves as a template for creating the ZAUTHENTICATE routine.

Important:

If using authentication with HealthShare®, you must use the ZAUTHENTICATE routine provided by InterSystemsOpens in a new tab and cannot write your own.

How Delegated Authentication Works

When a user attempts to log in and InterSystems IRIS invokes delegated authentication, the sequence of events is:

  1. When a service or application uses delegated authentication, a login attempt automatically results in a call to the ZAUTHENTICATE routine. The authentication code in this routine can be any user-defined ObjectScript, class methods, or $ZF callout code.

  2. The next step depends on whether or not authentication succeeds and whether or not this is the first login using ZAUTHENTICATE:

    • If ZAUTHENTICATE succeeds and this is the first time that the user has been authenticated through this mechanism, the user is added to the list of InterSystems IRIS users with a type of “Delegated user”. If ZAUTHENTICATE sets roles or other characteristics, these become part of the user’s properties.

    • If ZAUTHENTICATE succeeds and this is not the first login, ZAUTHENTICATE updates the user’s properties.

    • If ZAUTHENTICATE fails, then the user receives an access denied error and is unable to access the system. To determine why this has occurred:

      1. Check the Reason for Failing to Login field in the User Profile.

      2. For more detailed information, check the audit log for the relevant %System/%Login/LoginFailure event. If auditing or the LoginFailure event are not enabled, you may need to enable both of these and then re-create the circumstances of the login failure.

  3. If two-factor authentication is enabled for the instance and the relevant services, then there is a check that the user’s PhoneNumber and PhoneProvider properties have been set. If these properties are set, then two-factor authentication proceeds; if they are not set, two-factor authentication cannot proceed and the user is not authenticated.

  4. A delegated user is listed as such in the Type column of the list of users on the Users page (System Administration > Security > Users). The user’s properties are displayed read-only in the Management Portal and are not editable from within InterSystems IRIS (since all the information comes from outside InterSystems IRIS).

    Note:

    A delegated user cannot also be an InterSystems IRIS password user.

Overview of Configuring Delegated Authentication

To use delegated authentication, the steps are:

  1. Create the user-defined authentication code in the ZAUTHENTICATE routine. This can include the use of two-factor authentication. This routine can also perform basic setup for a user account, such as specifying roles and other user properties.

    If you are using HealthShare Health Connect, create a custom ZAUTHENTICATE routine as described in this guide.

    If you are using HealthShare Unified Care Record, you cannot create a custom version of ZAUTHENTICATE to implement delegated authentication because Unified Care Record comes with its own version of the routine. Instead, you must customize methods in the class HS.Local.ZAUTHENTICATE. For more information, see “Customizing Authentication via Local ZAUTHENTICATEOpens in a new tab” in the book Authenticating HealthShare Users.

  2. Enable delegated authentication for the InterSystems IRIS instance on the Authentication Options page.

  3. Enable delegated authentication for the relevant services, applications, or both, as required.

  4. Optionally enable two-factor authentication for the InterSystems IRIS instance and, if required, for web applications and client-server applications.

For example, to use delegated authentication for an instance’s Management Portal, the steps are:

  1. Create the user-defined authentication code in ZAUTHENTICATE.

  2. Enable delegated authentication for the InterSystems IRIS instance as a whole.

  3. Enable delegated authentication for the set of /csp/sys* applications.

Create Delegated (User-Defined) Authentication Code

This section describes various aspects of creating your own ZAUTHENTICATE routine:

Authentication Code Fundamentals

InterSystems provides a sample routine, ZAUTHENTICATE.mac, that you can copy and modify. This routine is part of the Samples-Security sample on GitHub (https://github.com/intersystems/Samples-SecurityOpens in a new tab). You can download the entire sample as described in Downloading Samples for Use with InterSystems IRIS , but it may be more convenient to simply open the routine on GitHub and copy its contents.

To create your own ZAUTHENTICATE.mac:

  1. To use ZAUTHENTICATE.mac as a template, copy its contents and save them into a ZAUTHENTICATE.mac routine in the %SYS namespace.

  2. Review the comments in the ZAUTHENTICATE.mac sample. These provide important guidance about how to implement a custom version of the routine.

  3. Edit your routine by adding custom authentication code and any desired code to set user account characteristics.

Caution:

Because InterSystems IRIS places no constraints on the authentication code in ZAUTHENTICATE, the application programmer is responsible for ensuring that this code is sufficiently secure.

Signature

The signature of ZAUTHENTICATE is:

ZAUTHENTICATE(ServiceName, Namespace, Username, Password, Credentials, 
        Properties) PUBLIC {

    // authentication code
    // optional code to specify user account properties and roles
}

where:

  • ServiceName — A string representing the name of the service through which the user is connecting to InterSystems IRIS, such as %Service_Console or %Service_WebGateway.

  • Namespace — A string representing the namespace on the InterSystems IRIS server to which a connection is being established. This is for use with the %Service_Bindings service, such as with Studio or ODBC.

  • Username — A string representing the name of the account entered by the user that is to be validated by the routine’s code.

  • Password — A string representing the password entered by the user that is to be validated.

  • CredentialsPassed by reference. Not implemented in this version of InterSystems IRIS.

  • PropertiesPassed by reference. An array of returned values that defines characteristics of the account named by Username.

When InterSystems IRIS calls ZAUTHENTICATE, it has values for these arguments and supplies them to the routine.

Note:

In older versions of InterSystems products, ZAUTHENTICATE took four arguments. For backwards compatibility, you can still use the four-argument version. If you are updating your code from the old to new version, note that the new arguments are second and fifth ones: Namespace and Credentials.

Authentication Code

The content of authentication code is application specific. If authentication succeeds, the routine should return the $$$OK macro; otherwise, it should return an error code. See Return Value and Error Messages for more information on return values.

Caution:

Because InterSystems IRIS does not and cannot place any constraints on the authentication code in ZAUTHENTICATE, the application programmer is responsible for ensuring that this code is sufficiently secure.

The GetCredentials Entry Point

ZAUTHENTICATE includes an GetCredentials entry point. This entry point is called whenever delegated authentication is enabled for a service, and is called before the user is prompted for a username and password. Instead of getting a username and password from the user, code in the function (created by the application developer) specifies the username and password. The username and password returned are then authenticated in the normal manner as if the user entered them. A possible use of this mechanism is to provide a username and password within the entry point and then, within authentication code, to $roles for the process.

The username and password returned from this entry point can be obtained by any mechanism that the application developer chooses. They can come from a global, come from an external DLL or LDAP call, or simply be set within the routine. The application developer could even provide code to prompt for the username and password, such as in a terminal connection or with a login page.

When there is a call to the GetCredentials entry point, the return value and other factors determine what happens next:

  • If the code sets the values of Username and Password and also returns a success status ($$$OK), then:

    • There is no additional username/password prompting.

    • The authentication process proceeds.

    Important:

    If the access point returns $$$OK, then its code must set the values of Username and Password. Otherwise, the user is denied access to the system and an error is written to the audit log.

  • If the entry point returns the error status $SYSTEM.Status.Error($$$GetCredentialsFailed), then normal username/password prompting proceeds.

  • If the entry point returns any other error status, then:

    • The user is denied access to the system.

    • The error is logged in the audit log.

In the following example of a GetCredentials entry point, the code performs different actions for different services:

  • For %Service_Console, it does not prompt the user for any information and sets the process’s username and password to _SYSTEM and SYS, respectively.

  • For %Service_Bindings, it forces the user to provide a username and password.

  • For web applications, it checks if the application in use is the /csp/ samples application; if it is that application, it sets the username and password to AdminUser and Test. For all other web applications, it denies access.

  • For any other service, it denies access.

Finally, the Error entry point performs clean-up as necessary.

The code is:

GetCredentials(ServiceName,Namespace,Username,Password,Credentials) Public {

  // For console sessions, authenticate as _SYSTEM.
  If ServiceName="%Service_Console" {
    Set Username="_SYSTEM"
    Set Password="SYS"
    Quit $SYSTEM.Status.OK()
  }

  // For a web application, authenticate as AdminUser.
  If $isobject($get(%request)) { 
    If %request.Application="/csp/samples/" {
      Set Username="AdminUser"
      Set Password="Test"
      Quit $System.Status.OK()
    }
  }

  // For bindings connections, use regular prompting.
  If ServiceName="%Service_Bindings" {
    Quit $SYSTEM.Status.Error($$$GetCredentialsFailed)
  }

  // For all other connections, deny access.
  Quit $SYSTEM.Status.Error($$$AccessDenied)
}

For more details, see the comments for this entry point in ZAUTHENTICATE.mac.

The SendTwoFactorToken Entry Point

ZAUTHENTICATE includes an SendTwoFactorToken entry point. This entry point is for use with two-factor authentication. If it is defined and the InterSystems IRIS instance has two-factor authentication enabled, then you can override the default system setting for the format of the message and token that the instance sends to the user’s mobile phone. This allows for messages that can vary by application even on the same InterSystems IRIS instance.

For more details and an example of how to use this entry point, see this entry point in the sample ZAUTHENTICATE.mac.

Set Values for Roles and Other User Characteristics

If initial authentication succeeds, ZAUTHENTICATE can establish the roles and other characteristics for the authenticated user. For subsequent logins, ZAUTHENTICATE can update these elements of the user record.

For this to happen, code in ZAUTHENTICATE sets the values of the Properties array. (Properties is passed by reference to ZAUTHENTICATE.) Typically, the source for the values being set is a repository of user information that is available to ZAUTHENTICATE.

User Properties

The elements in the Properties array are:

  • Properties("Comment") — Any text

  • Properties("FullName") — The first and last name of the user

  • Properties("NameSpace") — The default namespace for a Terminal login

  • Properties("Roles") — The comma-separated list of roles that the user holds in InterSystems IRIS

  • Properties("Routine") — The routine that is executed for a Terminal login

  • Properties("Password") — The user’s password

  • Properties("Username") — The user’s username

  • Properties("PhoneNumber") — The user’s mobile phone number, for use with two-factor authentication

  • Properties("PhoneProvider") — The user’s mobile phone’s service provider, for use with two-factor authentication

Each of these elements is described in more detail in one of the following sections.

Note:

The value of each element in the properties array determines the value of its associated property for the user being authenticated. It is not possible to use only a subset of the properties or to manipulate their values after authentication.

Comment

If ZAUTHENTICATE sets the value of Properties("Comment"), then that string becomes the value of the user account’s Comment property in InterSystems IRIS. (This property is described in User Account Properties.) If no value is passed back to the calling routine, then the value of Comment for the user account is a null string and the relevant field in the Management Portal then holds no content.

FullName

If ZAUTHENTICATE sets the value of Properties("FullName"), then that string becomes the value of the user account’s Full name property in InterSystems IRIS. (This property is described in User Account Properties.) If no value is passed back to the calling routine, then the value of Full name for the user account is a null string and the relevant field in the Management Portal then holds no content.

NameSpace

If ZAUTHENTICATE sets the value of Properties("Namespace"), then that string becomes the value of the user account’s Startup Namespace property in InterSystems IRIS. (This property is described in User Account Properties.) If no value is passed back to the calling routine, then the value of Startup Namespace for the user account is a null string and the relevant field in the Management Portal then holds no content.

Once connected to InterSystems IRIS, the value of Startup Namespace (hence, that of Properties("Namespace")) determines the initial namespace for any user authenticated for local access (such as for Console, Terminal, or Telnet). If Startup Namespace has no value (since Properties("Namespace") has no value), then the initial namespace for any user authenticated for local access is determined as follows:

  1. If the USER namespace exists, that is the initial namespace.

  2. If the USER namespace does not exist, the initial namespace is the %SYS namespace.

Note:

If the user does not have the appropriate privileges for the initial namespace, access is denied.

Password

If ZAUTHENTICATE sets the value of Properties("Password"), then that string becomes the value of the user account’s Password property in InterSystems IRIS. (This property is described in User Account Properties.) If no value is passed back to the calling routine, then the value of Password for the user account is a null string and the relevant field in the Management Portal then holds no content.

Roles

If ZAUTHENTICATE sets the value of Properties("Roles"), then that string specifies the Roles to which a user is assigned; this value is a string containing a comma-delimited list of roles. If no value is passed back to the calling routine, then the value of Roles for the user account is a null string and the relevant field in the Management Portal then holds no content. Information about a user’s roles is available on the Roles tab of a user’s Edit User page.

If any roles returned in Properties("Roles") are not defined, then the user is not assigned to the role.

Hence, the logged-in user is assigned to roles as follows:

  • If a role is listed in Properties("Roles") and is defined by the InterSystems IRIS instance, then the user is assigned to the role.

  • If a role is listed in Properties("Roles") and is not defined by the InterSystems IRIS instance, then the user is not assigned to the role.

  • A user is always assigned to those roles associated with the _PUBLIC user. A user also has access to all public resources. For information on the _PUBLIC user, see The _PUBLIC Account; for information on public resources, see Services and Their Resources.

Routine

If ZAUTHENTICATE sets the value of Properties("Routine"), then that string becomes the value of the user account’s Startup Tag^Routine property in InterSystems IRIS. (This property is described in User Account Properties.) If no value is passed back to the calling routine, then the value of Startup Tag^Routine for the user account is a null string and the relevant field in the Management Portal then holds no content.

If Properties("Routine") has a value, then this value specifies the routine to execute automatically following login on a terminal-type service (such as for Console, Terminal, or Telnet). If Properties("Routine") has no value, then login starts the Terminal session in programmer mode.

Username

If ZAUTHENTICATE returns the Username property, then the value of Username is written to the security database after any processing in the function; this provides chance to modify the value that the user entered at the prompt. If ZAUTHENTICATE does not return the Username property, then the value of the property is written to the security database as entered.

If ZAUTHENTICATE sets the value of Properties("Username"), then that string becomes the value of the user account’s Name property in InterSystems IRIS. (This property is described in User Account Properties.) This provides the application programmer with an opportunity to normalize content provided by the end-user at the login prompt.

If there is no explicit call that passes the value of Properties("Username") back to the calling routine, then there is no normalization and the value entered by the end-user at the prompt serves as the value of the user account’s Name property without any modification.

PhoneNumber and PhoneProvider

These are properties associated with two-factor authentication.

If ZAUTHENTICATE sets the value of Properties("PhoneNumber") and Properties("PhoneProvider"), then these then these are written to the InterSystems IRIS database for the user as the user’s mobile phone number and mobile phone service provider. If these are not passed back to the calling routine, then the phone number and service provider written to the InterSystems IRIS database are a null string. Hence, to use two-factor authentication with delegated authentication, you must supply both of these.

The User Information Repository

ZAUTHENTICATE can refer to any kind of repository of user information, such as a global or an external file. It is up to the code in the routine to set any external properties in the Properties array so that the authenticated user can be created or updated with this information. For example, while a repository can include information such as roles and namespaces, ZAUTHENTICATE code must make that information available to InterSystems IRIS.

If information in the repository changes, this information is only propagated back into the InterSystems IRIS user information if there is code in ZAUTHENTICATE to perform this action. Also, if there is such code, changes to users’ roles must occur in the repository; if you change a user’s roles during a session, the change does not become effective until the next login, at which point the user’s roles are re-set by ZAUTHENTICATE.

Return Value and Error Messages

The routine returns one of the following values:

  • Success — $$$OK. This indicates that username/password combination was successfully authenticated

  • Failure — $SYSTEM.Status.Error($$$ERRORMESSAGE). This indicates that authentication failed.

ZAUTHENTICATE can return system-defined or application-specific error messages. All these messages use the Error method of the %SYSTEM.StatusOpens in a new tab class. This method is invoked as $SYSTEM.Status.Error and takes one or two arguments, depending on the error condition.

The available system-defined error messages are:

  • $SYSTEM.Status.Error($$$AccessDenied) — Error message of “Access Denied”

  • $SYSTEM.Status.Error($$$InvalidUsernameOrPassword) — Error message of “Invalid Username or Password”

  • $SYSTEM.Status.Error($$$UserNotAuthorizedOnSystem,Username) — Error message of “User Username is not authorized”

  • $SYSTEM.Status.Error($$$UserAccountIsDisabled,Username) — Error message of “User Username account is disabled”

  • $SYSTEM.Status.Error($$$UserInvalidUsernameOrPassword,Username) — Error message of “User Username invalid name or password”

  • $SYSTEM.Status.Error($$$UserLoginTimeout) — Error message of “Login timeout”

  • $SYSTEM.Status.Error($$$UserCTRLC) — Error message of “Login aborted”

  • $SYSTEM.Status.Error($$$UserDoesNotExist,Username) — Error message of “User Username does not exist”

  • $SYSTEM.Status.Error($$$UserInvalid,Username) — Error message of “Username Username is invalid”

  • $SYSTEM.Status.Error($$$PasswordChangeRequired) — Error message of “Password change required”

  • $SYSTEM.Status.Error($$$UserAccountIsExpired,Username) — Error message of “User Username account has expired”

  • $SYSTEM.Status.Error($$$UserAccountIsInactive,Username) — Error message of “User Username account is inactive”

  • $SYSTEM.Status.Error($$$UserInvalidPassword) — Error message of “Invalid password”

  • $SYSTEM.Status.Error($$$ServiceDisabled,ServiceName) — Error message of “Logins for Service Servicename are disabled”

  • $SYSTEM.Status.Error($$$ServiceLoginsDisabled) — Error message of “Logins are disabled”

  • $SYSTEM.Status.Error($$$ServiceNotAuthorized,ServiceName) — Error message of “User not authorized for service”

To generate a custom message, use the $SYSTEM.Status.Error() method, passing it the $$$GeneralError macro and specifying any custom text as the second argument. For example:

$SYSTEM.Status.Error($$$GeneralError,"Any text here")

Note that when an error message is returned to the caller, it is logged in the audit database (if LoginFailure event auditing is turned on). However, the only error message the user sees is $SYSTEM.Status.Error($$$AccessDenied). However, the user also sees the message for the $$$PasswordChangeRequired error. Return this error if you want the user to change from the current to a new password.

Set Up Delegated Authentication

Once you have created a ZAUTHENTICATE routine to perform authentication (and, optionally, authorization tasks), the next step is to enable it for the instance’s relevant services or applications. This procedure is:

  1. Enable delegated authentication for entire instance. On the Authentication/Web Session Options page (System Administration > Security > System Security > Authentication/Web Session Options), select Allow Delegated authentication and click Save.

    With delegated authentication enabled for the instance, a Delegated check box appears on the Edit Service page for relevant services and the Edit Web Application page for those applications.

  2. Enable delegated authenticated for services and applications, as appropriate.

The following services support delegated authentication:

  • %Service_Bindings

  • %Service_CallIn

  • %Service_ComPort

  • %Service_Console

  • %Service_Login

  • %Service_Terminal

  • %Service_Telnet

  • %Service_WebGateway

These fall into several categories of access modes:

  • Local Access

    %Service_CallIn, %Service_ComPort, %Service_Console, %Service_Login, %Service_Terminal, %Service_Telnet

    To use delegated authentication with local connections, enable it for the service.

  • Client-Server Access

    %Service_Bindings

    To use delegated authentication with client-server connections, enable it for the service.

  • Web Access

    %Service_WebGateway

    To use delegated authentication with web-based connections, enable it for the web application. You may also enable it for the Web Gateway by enabling the service %Service_WebGateway

After Delegated Authentication Succeeds

Once the user has authenticated, two important topics are:

The State of the System

Any user who is initially authenticated using delegated authentication is listed in the table of users on the Users page (System Administration > Security > Users) as having a type of “Delegated user”. If a system administrator has explicitly created a user through the Management Portal (or using any other native InterSystems IRIS facility), that user has a type of “InterSystems IRIS password user”. If a user attempts to log in using delegated authentication and is successfully authenticated, InterSystems IRIS determines that this user already exists as an InterSystems IRIS user — not a Delegated user — and so login fails.

Note:

To perform sharded operations on a sharded cluster, a delegated user must have been previously authenticated on each node of the sharded cluster by some means other than an internal sharding connection. For more information on sharding, see Horizontally Scaling InterSystems IRIS with Sharding.

Change Passwords

The ZAUTHENTICATE routine also includes an entry point, ChangePassword, to include code to change a user’s password. The signature of this entry point is:

ChangePassword(Username,NewPassword,OldPassword,Status) Public {}

where

  • Username is a string specifying the user whose password is being changed.

  • NewPassword is a string specifying the new value of the user’s password.

  • OldPassword is a string specifying the old value of the user’s password.

  • Status (passed by reference) receives an InterSystems IRIS status value indicating either that the password change has been successful or specifying the error that caused the routine to fail.

Use LDAP with Delegated Authentication or Other Mechanisms

You can also use LDAP as part of a custom authentication system (that is, with the InterSystems IRIS delegated authentication feature). To do this, use calls to the %SYS.LDAPOpens in a new tab class as part of the custom authentication code in the ZAUTHENTICATE routine.

InterSystems provides a sample routine, LDAP.mac, that demonstrates these calls. This routine is part of the Samples-Security sample on GitHub (https://github.com/intersystems/Samples-SecurityOpens in a new tab).

Also, if you need to authenticate to LDAP or use instance authentication after collecting credentials through another mechanism, call $SYSTEM.Security.Login with those credentials to authenticate the user.

FeedbackOpens in a new tab