Adaptive multi-factor authentication overview

CyberArk Identity features an adaptive multi-factor authentication (MFA) solution. You can customize the solution based on your security needs.

This process requires the following calls from a client:

  • /Security/StartAuthentication to initiate authentication.

  • Zero or more calls to /Security/AdvanceAuthentication to respond to an authentication challenge or challenges (in certain situations, the call to /Security/StartAuthentication logs in the user, in which case, there is no need to call /Security/AdvanceAuthentication).

While the authentication process requires some flexibility on the part of the client, as the number and types of challenges may vary, and some challenges require a different type of response from others, the basic process for a successful login is as follows:

  1. The client requests authentication for a user by calling /Security/StartAuthentication and passing enough information to identify the user and tenant.

  2. The server validates the user and tenant, and creates an MFA package that includes, among other things, a session ID and zero or more authentication challenges for the client to answer.

  3. The server caches the information and returns the MFA package to the client. Note that the number and type of challenges (user/password, one-time passcode, security question, and so on) depend on various factors configured by customer administrators. These factors include the policies that are assigned to the user, the device from which a user signs in, whether the IP address of the device is within the corporate firewall, and so on.

  4. The client calls /Security/AdvanceAuthentication to respond to an authentication challenge.

    There may be a single challenge, multiple challenges in a list, or one challenge that must be answered first, followed by a list to choose from. The rest of this topic addresses each of these possibilities.

  5. If the client answers the challenge correctly, the server does one of the following:

    • Logs the user in and returns LoginSuccess.

    • Requests that the user answer another challenge mechanism.

    • Notifies the client that an out-of-band (OOB) challenge is pending. The client must poll until the server indicates that the user has answered the challenge.

      For more details, see Respond to a pending OOB challenge.

The next section, Initiate authentication, steps you through a basic scenario in which you call /Security/StartAuthentication to generate a security challenge for a user and call /Security/AdvanceAuthentication to respond to the challenge. Subsequent sections show how to handle a variety of authentication mechanisms and scenarios.

When you successfully call /Security/AdvanceAuthentication (or if the call to /Security/StartAuthenitcation logs in the user), the server returns a set of cookies, including an authentication cookie. You must pass these cookies to all subsequent calls that you make on behalf of this user. See Authentication cookies overview.

Initiate authentication

To initiate authentication, the client calls /Security/StartAuthentication and passes User, Version, and TenantId (optional, if the hostname contains a tenant-specific URL or if the user name contains a known alias):

/Security/StartAuthentication
{
	"TenantId": "ABC1234",
	"User": "mr.wright@doccraft",
	"Version": "1.0"
}

After a call to /Security/StartAuthentication, examine Summary in the response from the server to determine what the client needs to do next:

  • Failure - Only occurs if the user name or version is missing. In any case, the client should not allow a user to send a login request with a blank user name, so you should never see this error in a production system. However, if you do, this means the client called the function without a user name or version.

  • PodFqdn - The fully-qualified domain name of the tenant. If the client calls /Security/StartAuthentication with a system-generated tenant URL, while a custom URL is defined for the tenant, the call returns the custom tenant URL in PodFQDN. Call /Security/StartAuthentication again with the custom-defined tenant URL.

  • NewPackage - The server creates and returns an MFA package with one or more challenge mechanisms. Call /Security/AdvanceAuthentication to respond to the first authentication mechanism. The next section explains how to respond to a new package with a single challenge mechanism.

  • LoginSuccess - Generally, /Security/StartAuthentication does not complete the login process; however, it is possible for it to do so. For example, if the call passes a valid authentication cookie, the server will log in the user and return LoginSuccess in the summary.

Respond to a single challenge mechanism

This section shows how to respond to a single challenge mechanism. In this example, after the client calls /Security/StartAuthentication to initiate authentication, the server returns a single, user-password (UP) challenge mechanism:

{
	"ClientHints": {
		"PersistDefault": False,
		"AllowPersist": True,
		"AllowForgotPassword": False
	},
	"Version": "1.0",
	"SessionId": "1e5214e4-0921-4e9e-8ada-3ef2970f7c1f",
		"Challenges": [
			"Mechanisms": [
				{
					"AnswerType": "Text",
					"Name": "UP",
					"MechanismId": "4a23390d-dee9-4ead-aa33-2bacd93f81fa"
				}
			] 
		}
	],
	"Summary": "NewPackage",
	"TenantId": "ABC1234"
}

Note the following about the response:

  • In the Summary field, NewPackage indicates that the server has created a new MFA package.

  • ClientHints determine whether you can display a password link (AllowForgotPassword) and Keep me signed in checkbox (AllowPersist) for the user. PersistDefault indicates the default for AllowPersist. You can pass a different value to /Security/AdvanceAuthentication if the user checks or unchecks the box in the login dialog that the client presents.

  • In the AnswerType field, Text indicates that the server expects a text string in response to this challenge mechanism.

  • When you initiate authentication with a call to /Security/StartAuthentication, the server returns a new package, whether the user that you pass is valid or invalid. This is to prevent hackers from brute-force guessing at login names. In the case of an invalid user name, the server returns a default challenge mechanism. When you call /Security/AdvanceAuthentication to answer the challenge mechanism, the server returns an error message and summary: Undefined or Failure in response.

Respond to a new package by calling /Security/AdvanceAuthentication, passing TenantId, SessionId, and MechanismId (retrieved from the response to /Security/StartAuthentication) and answering the challenge mechanism:

/Security/AdvanceAuthentication
{
	"TenantId": "ABC1234",
	"SessionId": "1e5214e4-0921-4e9e-8ada-3ef2970f7c1f",
	"MechanismId": "4a23390d-dee9-4ead-aa33-2bacd93f81fa",
	"Action": "Answer",
	"Answer": "Pass1234"
}

If the server validates the user (passed to /Security/StartAuthentication) and the password (passed to /Security/AdvanceAuthentication), it returns information about the user, and Summary shows LoginSuccess, as shown here:

{
	"success": true,
	"Result": 
	{
		"AuthLevel": "Normal",
		"DisplayName": "MRWright",
		"Auth": "6936714B84F54...",
		"EmailAddress": "mr.wright@acme.com",
		"UserDirectory": "CDS",
		"PodFqdn": "abc1234.my-dev.idaptive.app",
		"User": "mr.wright@doccraft",
		"CustomerID": "ABC1234",
		"SystemID": "ABC1234",
		"SourceDsType": "CDS",
		"Summary": "LoginSuccess"
 	},  "Message": null, "MessageID": null, "Exception": null,  "ErrorID": null, "ErrorCode": null, "InnerExceptions": null
}

If the server does not validate the user, or if the password is incorrect, it returns an error message and shows summary: Undefined or Failure in the response, as shown here:

{
	"success": false,
	"Result": 
	{
		"Summary": "Undefined"
	},
		"Message": "Failed to login. Please try again or contact your system administrator.",
		"MessageID": null,
		"Exception": null,
		"ErrorID": "c426637a-b8c3-46cb-be7a-4c7c6ceba131:00b917d8e98c4ccbb794dd0374c0c1f9",
		"ErrorCode": null,
		"InnerExceptions": null
	}
}

The client must call /Security/StartAuthentication again and pass a valid user name to initiate the authentication process, and then call /Security/AdvanceAuthentication and pass the correct password to advance the authentication.

Respond to multiple challenge mechanisms

In this example, after you call /Security/StartAuthentication, the server returns multiple authentication mechanisms, shown here:

{
	"success": true,
	"Result": {
		"ClientHints": {
			"PersistDefault": False,
			"AllowPersist": True,
			"AllowForgotPassword": False
		},
		"Version": "1.0",
		"SessionId": "1e5214e4-0921-4e9e-8ada-3ef2970f7c1f",
		"Challenges": [
			{
				"Mechanisms": [
					{
						"AnswerType": "Text",
						"Name": "UP",
						"MechanismId": "4a23390d-dee9-4ead-aa33-2bacd93f81fa"
					}
				]
			},
			{
				"Mechanisms": [
					{
						"AnswerType": "Text",
						"PartialAddress": "idaptive.app",
						"EmailType": "Primary",
						"Name": "EMAIL",
						"MechanismId": "06d82f0c-cb09-4420-a64f-ecd9efda74e8"
					},
					{
						"AnswerType": "Text",
						"PartialDeviceAddress": "6098",
						"Name": "SMS",
						"MechanismId": "2bcddd0b-37b9-4a6b-b393-9cd03eb7c9aa"
					},
					{
						"AnswerType": "Text",
						"Question": "Tonight's Homework",
						"Name": "SQ",
						"MechanismId": "5778ff68-4e65-4ceb-b9e8-361e281228a8"
					},
					{
						"AnswerType": "Text",
						"PartialPhoneNumber": "6098",
						"Name": "PF",
						"MechanismId": "2f0a3e0c-bea8-4c91-95f0-b9cdd736f668"
					},
					{
						"AnswerType": "Text",
						"PartialPhoneNumber": "5290",
						"Name": "PF",
						"MechanismId": "415a2e99-371a-49e3-bf3b-267b1a83be96"
					}
				]
			}
		],
	"Summary": "NewPackage",
	"TenantId": "ABC1234"
}

The server returns two challenges:

  • The first one defines a single, user-password mechanism. The client must answer this one first.

  • The second one defines multiple mechanisms (SMS, SQ, PF, and so on). After answering the first challenge, the client must answer one of these mechanisms.

Call /Security/AdvanceAuthentication to respond to the first challenge:

/Security/AdvanceAuthentication
{
	"SessionId": "1e5214e4-0921-4e9e-8ada-3ef2970f7c1f",
	"MechanismId": "4a23390d-dee9-4ead-aa33-2bacd93f81fa",
	"Action": "Answer",
	"Answer": "Pass1234"
}

The server responds with a request to answer the next challenge (Summary: StartNextChallenge), shown here:

{
	"success": true,
	"Result": 
	{
		"Summary": "StartNextChallenge"
	},
	"Message": null, "MessageID": null, "Exception": null, "ErrorID": null, "ErrorCode": null, "InnerExceptions": null
}

The value in the Summary field (StartNextChallenge) indicates that there are additional challenges to which you must respond. It does not necessarily indicate that the response to the challenge was correct. To prevent hackers from guessing which challenge failed, the server may wait for all challenge responses before validating or invalidating the responses.

Call /Security/AdvanceAuthentication to respond to one of the challenges; for example, the security question (SQ). The server provides all the information you need to present to the user for each challenge. For example, the security challenge provides the text of the question (Tonight's homework), the SMS challenge provides a partial number, and so on. Note, however, that the server does not provide translations of the prompts. You are responsible for creating and translating prompts, if necessary, for your client.

/Security/AdvanceAuthentication
{
	"TenantId": "ABC123",
	"SessionId": "1e5214e4-0921-4e9e-8ada-3ef2970f7c1f",
	"MechanismId": "5778ff68-4e65-4ceb-b9e8-361e281228a8",
	"Action": "Answer",
	"Answer": "math 101"
}

Check the summary to see if there are additional challenges to answer. Once you have correctly answered all challenges, the server returns a response (Summary: LoginSuccess) that is similar to the following LoginSuccess response:

{
	"SystemID": "ABC1234",
	"DisplayName": ""MRWright"",
	"EmailAddress": "mr.wright@acme.com",
	"PasswordExpDate": "Fri, 31 Dec 9999 15:59:59 GMT-08:00",
	"CustomerID": "ABC1234",
	"AuthLevel": "Normal",
	"PodFqdn": "abc1234.my-dev.idaptive.app",
	"Auth": "C85562A8A3B425095981BFFD7D92F7...",
	"User": "mr.wright@doccraft",
	"UserDirectory": "CDS",
	"Summary": "LoginSuccess",
	"UserId": "c2c7bcc6-9560-44e0-8dff-5be221cd37ee",
	"SourceDsType": "CDS"
}

"Summary": "LoginSuccess" indicates that the server has authenticated the user.

The response includes information about the user, such as display name, email address, password expiration, and so on. It also includes UserId, which you can pass to other API functions to get information about the user.

The response from /Security/AdvanceAuthentication includes the cookies that you must pass to each subsequent call on behalf of the validated user. For more details, see Authentication cookies overview.

Respond to a pending OOB challenge

An out-of-band (OOB) mechanism is one in which the server contacts an outside device or system; for example, by sending an SMS message to a user's device, or an email message to their email address. To complete authentication, a user must retrieve the message and follow the instructions; for example, by clicking an authentication link.

In this example, the server returns a new package in response to your call to /Security/StartAuthentication or /Security/AdvanceAuthentication that contains an OOB challenge mechanism such as this:

{
	"AnswerType": "StartOob",
	"Name": "EMAIL",
	"MechanismId": "8110671b-7d6c-4604-98c5-4fd273f8063f-099e7f417e646300",
	"PartialAddress": "acme.com"
}

You respond to the mechanism by calling /Security/AdvanceAuthentication as follows:

/Security/AdvanceAuthentication
{
	"TenantId": "ABC1234",
	"SessionId": "1db90fe4-3b96-4c3e-a3c1-9a10fa7514c9-27f4e64e86ac08e8",
	"MechanismId": "8110671b-7d6c-4604-98c5-4fd273f8063f-099e7f417e646300",
	"Action": "StartOOB"
}

The server does the following:

  • Sends an email message to the user's acme.com address, containing a link to complete the login

  • Sends an OOB pending response to the client and waits for the client to answer

{
	"Summary": "OobPending"
     
}

The client should poll the server by calling /Security/AdvanceAuthentication periodically (no more than once per second) until the value in the summary changes from OobPending. In the payload, specify Poll in the Action field:

/Security/AdvanceAuthentication
{
	"TenantId": "AB123",
	"SessionId": "1db90fe4-3b96-4c3e-a3c1-9a10fa7514c9-27f4e64e86ac08e8",
	"MechanismId": "8110671b-7d6c-4604-98c5-4fd273f8063f-099e7f417e646300",
	"Action": "Poll"
}

Continue to poll until you see "Summary": "LoginSuccess", which indicates that the server has authenticated the user, or a failure message indicating that the server did not authenticate the user.

Authenticate a user with an expired password

This section shows how to call the API to authenticate a user whose password has expired. While the example is explicitly about how to handle a user with an expired password, it illustrates the broader point that in response to /Security/AdvanceAuthentication, the server may return a new package that does any of the following:

  • Removes, replaces, or adds mechanisms to existing challenges

  • Adds new challenges

  • Removes existing challenges

When a client calls /Security/StartAuthentication to initiate authentication, the server doesn't know whether the password has expired, because the client has passed a user name, but not a password. The server validates the user and tenant and returns an MFA package with appropriate challenge mechanisms for the user; for example, a user-password challenge that must be answered first and a security-question challenge to be answered after the user-password challenge, such as these:

...
"Challenges": 
[
	{
		"Mechanisms": [
			{
				"AnswerType": "Text",
				"Name": "UP",
				"MechanismId": "51b8ca6f-9b15-4cec-ba7c-ce20cc2bfb77"
			}
		]
	},
	{
		"Mechanisms": [
			{
				"AnswerType": "Text",
				"Question": "Tonight's Homework",
				"Name": "SQ",
				"MechanismId": "bd2f1c84-1026-4d63-aef1-a357b5e7ab37"
			} 
		]
	}
],
"Summary": "NewPackage",
"TenantId": "AB123"

After the client calls /Security/AdvanceAuthentication to respond to the password challenge, the server is aware that the user's password has expired, and returns the following "New challenge mechanisms" package in response:

...
"Challenges": [
	{
		"Mechanisms": [
			{
				"AnswerType": "Text",
				"Question": "Tonight's Homework",
				"Name": "SQ",
				"MechanismId": "bd2f1c84-1026-4d63-aef1-a357b5e7ab37"
			}
		]
	},
	{
		"Mechanisms": [
			{
				"AnswerType": "Text",
				"Name": "RESET",
				"MechanismId": "35e70e0d-94a8-4d13-b4f2-ed5a314a908d"
			}
		]
	}
],
"Summary": "NewPackage",
"TenantId": "ABC123"
		

Note the following about the response:

  • The server sends a new package ("Summary": "NewPackage",) because the password is expired and the client needs to send a new response.

  • The server removes the user/password challenge from the response, because it has already been answered. It caches the client's successful response to the challenge for future use.

  • It moves the security question (SQ) challenge from second to first on the list — it hasn't been answered yet.

  • It adds a new second challenge (RESET).

Call /Security/AdvanceAuthentication to respond to the security-question challenge:

/Security/AdvanceAuthentication
{
	"TenantId": "ABC123",
	"SessionId": "81c60352-ee50-422c-b5c9-e73b55b8f314",
	"MechanismId": "bd2f1c84-1026-4d63-aef1-a357b5e7ab37",
	"Action": "Answer",
	"Answer": "math 101"
}
		

The server returns the following response:

{
	"Summary": "StartNextChallenge"
}
		

Call /Security/AdvanceAuthentication again, this time to answer the reset challenge:

/Security/AdvanceAuthentication
{
	"TenantId": "ABC1234",
	"SessionId": "a3f66039-2ae4-4320-8200-426285d995e7",
	"MechanismId": "35e70e0d-94a8-4d13-b4f2-ed5a314a908d",
	"Action": "Answer",
	"Answer": "Pass6789"
}
		

The server returns a response ("Summary": "LoginSuccess") similar to the following LoginSuccess response:

{
	"success": True,
	"Result": 
	{
		{
			"SystemID": "ABC1234",
			"DisplayName": ""MRWright"",
			"EmailAddress": "mr.wright@acme.com",
			...
			"Summary": "LoginSuccess",
		}
	},
}
		

Handle redirects

As explained in Use your Tenant URL, the Identity platform identifies a tenant through tenant URLs, which are of three types:

  • System-generated. The Identity platform automatically generates a URL by prefixing the string my.idaptive.app with the tenant ID. For example, tenant ID ABC1234 forms tenant URL ABC1234.my.idaptive.app.

  • Custom, created by client. An administrator creates a URL from a custom string, such as company name, prefixed to my.idaptive.app. For example, acme.my.idaptive.app.

  • Preferred, a custom tenant ID that administrator designates as the default, or preferred, URL to use.

If a preferred tenant URL exists, the Identity platform automatically directs logins from the system-generated URL to the preferred tenant URL. From the perspective of the API, if you call /Security/Start/Authentication using the system-generated tenant URL, you must redirect the call to the preferred URL. For example:

https://ABC1234.my.idaptive.app/Security/StartAuthentication
	{User: "user2", Version: "1.0"}
		

Although the call completes successfully, instead of returning an authentication package, it returns the preferred tenant URL in PodFqdn, as shown here:

{
	"success": true,
	"Result": 
	{
		"PodFqdn": "acme.my.idaptive.app"
	},
	"Message": null, "MessageID": null, "Exception": null, "ErrorID": null, "ErrorCode": null, "InnerExceptions": null
				}
		

In response, call /Security/Start/Authentication again but specify the preferred URL from PodFqdn:

https://acme.my.idaptive.app/Security/StartAuthentication
	{User: "user2", Version: "1.0"}
		

In response, /Security/Start/Authentication returns a new authentication package ("Summary": "NewPackage") that you can use to authenticate the user, as shown here:

{
	"success": true,
	"Result": 
	{
		"ClientHints": 
		{
			"PersistDefault": false,
			"AllowPersist": false,
			"AllowForgotPassword": true
		},
		"Version": "1.0",
		"SessionId": "7abd72d0-446b-4a51-bdd9-50b8027ec5fe-a479e2e0ff8d8820",
		"Challenges": 
		[
			{
				"Mechanisms": [
					{
						"AnswerType": "Text",
						"Name": "UP",
						"MechanismId": "7f43d95c-22ba-4d63-bb15-8548923ad6ed-7439116a4971648e"
					}
				]
			}
		],
		"Summary": "NewPackage",
		"TenantId": "ABC1234"
	},
	"Message": null, "MessageID": null, "Exception": null, "ErrorID": null, "ErrorCode": null, "InnerExceptions": null
}