Triggering for multi-factor authentication (MFA)
This topic assumes that you have read Multi-factor authentication.
To get command-line help for MFA, type p4 help mfa
to see the topic "MFA - Multi-Factor Authentication".
Support for multi-factor authentication is provided by installing three mandatory triggers:
- auth-pre-2fa, which presents the user a list of authentication methods
- auth-init-2fa, which begins the flow for the chosen authentication method
- auth-check-2fa, which checks whether passwords are valid
Only one trigger of each type can be defined and all three triggers must be present.
These triggers return JSON results to the server. If the triggers are installed and the server has been restarted, the security level is set to 3
implicity, and can be explicitly set higher.
To configure a user to require MFA, the AuthMethod
field in the user spec for that user must be modified to either perforce+2fa
or ldap+2fa
. This requires that this user run the p4 login2 command to perform the steps for the second authentication. If automatic login prompting is enabled, users automatically perform this after their normal password-based authentication. See the p4 help login2
command-line help.
There are three phases to MFA, each based on the execution of that phase's trigger:
The list-methods phase (auth-pre-2fa)
This phase presents the user with a list of available MFA methods. For example, users might be configured to use either SMS or a mobile authentication application. In interactive mode, if the user only has one method, this is chosen automatically. These methods are returned by the auth-pre-2fa
trigger. The trigger can also indicate that this user doesn't require additional authentication at this time, or that this user is not permitted access. This trigger is expected to return 0
on success and return a JSON string to the server via STDOUT.
The JSON response should be in the following format:
{ "status" : 0, "methodlist" : [ [ "method1" , "method description1" ], [ "method2" , "method description2" ] ], "message" : "Error message" }
The status field is required, and should be be 0
on success with the methodlist
populated with a dictionary of authentication, where the key is the method name and the value is the method description.
If the status is set to 2
, MFA is not required for this user on this host at this time. Any other status value is considered to be a rejection of the authentication attempt. In these cases, the methodlist is not needed and instead a message can be provided to be relayed to the user.
For example:
{ "status" : 2, "message" : "Second factor authentication not required" }
The init-auth phase (auth-init-2fa)
This phase begins the second authentication flow for the chosen method. It calls the auth-init-2fa
trigger, returning the status (0
for success) and the scheme. In the case of an error, the status would be non-zero and the scheme is not required. In addition, a message might be reported to the user in either case.
An optional challenge can be set to be presented to the user. For authentication flows that require state between init and check, a token can be set. This token is stored in the server but never reported to the user. The token is available to the next trigger via a trigger variable.
Here is an example JSON response:
{ "status" : 0, "scheme" : "challenge", "message" : "Please enter your response", "challenge" : "ABBACD", "token" : "REQID:20003339189" }
There are four possible values for scheme:
- otp-generated - A One-Time-Password generated by a user device
- otp-requested - A One-Time-Password sent to the user
- challenge - A challenge/response based on a token displayed to the user
- external - A request to a 3rd-party prompting method, like an app-based push notification
The check-auth phase (auth-check-2fa)
This phase performs the verification step for the authentication flow initialized by init-auth
. If the scheme is "external", the auth-2fa-check
trigger is called to query the status of the prompt from the authentication provider. Otherwise the user is prompted for her or his One-Time-Password or challenge response, which is passed to the auth-2fa-check
trigger via STDIN and is validated against the second factor authentication provider. The response of this trigger is represented in JSON as a status field and an option message to the user. The status values are 0
for success and non-zero for failure (authentication rejected).
If the scheme is "external", it is possible that the authentication provider might still be waiting for the user's response. Returning a status value of 2
instructs the server to neither accept or reject the authentication attempt. For example:
{ "status" : 2, "message" : "A token was sent to your phone" }
Variables for the three mandatory triggers
All three trigger's specific variables are:
%user% - the user's username
%fullname% - the user's fullname
%email% - the user's email address
%host% - the user's host's IP address
%method% - the authentication method from list-methods (can be set to "unknown")
%scheme% - the authentication scheme set by init-auth (can be set to "unknown")
%token% - the stashed token from the last init-auth (can be empty)
Given that the %fullname%
and %email%
fields are populated from fields in the user spec which are modifiable by default, if these are used, we recommend that you set dm.user.allowselfupdate=0
to prevent users from modifying those fields.
Expiration of authorization
If the user logs out (see p4 logout), the authorization expires for that session.
If your organization is using SAML or OIDC, we recommend that you use your cloud-based IdP for MFA in conjunction with the Helix Authentication Service.
However, we continue to support the legacy Helix MFA Authenticator for Helix Core clients and plugins, which dates from 2018. After you have configured the three triggers described above, your users can download the Helix MFA Authenticator from https://www.perforce.com/downloads/helix-mfa-authenticator.
Let your users know that when they install Helix MFA Authenticator, the "Don't ask again for this computer" checkbox only persists the preference until authorization expires for the end user's current session.