Newer Version Available

This content describes an older version of this product. View Latest

Deploy Third-Party, SMS-Based Multi-Factor Authentication

Multi-factor authentication (MFA) enhances security when validating a user’s identity and protects access to your Salesforce org. In addition to a password, SMS-based MFA requires the user to provide a one-time password (OTP) code received on a mobile device.

To implement MFA, you can take advantage of a third-party SMS or voice delivery service, like Twilio or TeleSign, together with a Salesforce login flow.

Let’s break down an SMS-based MFA process.

  1. As the user logs in, the login flow generates a random OTP and sends it via voice or text message to the user’s phone.
  2. The user provides the OTP to the Salesforce application.
  3. Salesforce verifies the code.
  4. If the code is valid, Salesforce permits user access.

The login flow has four steps.

  1. Get Records—Queries the user record to get the mobile phone number.
  2. Apex Action (Legacy)—Generates the OTP and uses a third-party SMS delivery service to send it to the user’s mobile device.
  3. Screen—Prompts the user to provide the received OTP.
  4. Decision—Compares the OTP generated by the Apex action with the one that the user provides. If equal, the flow is completed, and the user is redirected to the application. Otherwise, the flow generates another code and asks the user to re-verify.

Configure the Flow

This example uses the Twilio Apex SDK to perform SMS delivery operations. However, you can use any cloud-based SMS or voice vendor that has a public API to access its services.

  1. Open Flow Builder. From Setup, enter Flows in the Quick Find box, select Flows, and then click New Flow.
  2. Select Screen Flow, and click Create.
  3. From the toolbox, open the Manager tab, and click New Resource.
  4. Create a LoginFlow_UserId input text variable. This variable is populated with the user ID during the login event. Creating a LoginFlow_UserId variable
  5. Create text variables.
    • Mobile—The user’s mobile number
    • VerificationCode—The OTP generated by the Apex plug-in
    • Code—The OTP collected from the user
    • Status—The status returned when the plug-in executes
  6. From the toolbox, open the Elements tab. Add a Get Records element to the canvas to look up the user who’s trying to log in. In the Get Records element,              find the user records whose Id matches what's stored in the LoginFlow_UserId variable.
  7. Store the user’s mobile number in the Mobile input variable. In the Get Records element,choose to store only the first record, and choose to store the field values in separate variables. Below, store the MobilePhone field in the Mobile variable.
  8. Install the Twilio Apex SDK from https://github.com/twilio/twilio-salesforce.
  9. To allow the SMS plug-in to perform outbound API calls to Twilio web services, set up https://api.twilio.com as a remote site in Salesforce. In Setup, enter Remote Site Settings in the Quick Find box, select Remote Site Settings, and add the Twilio web services URL. Add the third-party web services URL as a remote site
  10. Create an Apex class.
    1global class SMSPlugin implements Process.Plugin {
    2
    3    global Process.PluginDescribeResult describe() {
    4
    5        Process.PluginDescribeResult result = new Process.PluginDescribeResult();
    6        result.tag='Identity';
    7        result.name='SMS Plugin';
    8        result.description='Multi-factor authentication with SMS';
    9
    10        result.inputParameters = new List<Process.PluginDescribeResult.InputParameter> {
    11            new Process.PluginDescribeResult.InputParameter('AccountSid', Process.PluginDescribeResult.ParameterType.STRING, true),
    12
    13            new Process.PluginDescribeResult.InputParameter('Token', Process.PluginDescribeResult.ParameterType.STRING, true),
    14            new Process.PluginDescribeResult.InputParameter('To', Process.PluginDescribeResult.ParameterType.STRING, true),
    15            new Process.PluginDescribeResult.InputParameter('From', Process.PluginDescribeResult.ParameterType.STRING, true),
    16            new Process.PluginDescribeResult.InputParameter('Message', Process.PluginDescribeResult.ParameterType.STRING, true)
    17        };
    18
    19        result.outputParameters = new List<Process.PluginDescribeResult.OutputParameter> {
    20            new Process.PluginDescribeResult.OutputParameter('Status', Process.PluginDescribeResult.ParameterType.STRING),
    21            new Process.PluginDescribeResult.OutputParameter('VerificationCode', Process.PluginDescribeResult.ParameterType.STRING)
    22        };
    23
    24        return result;
    25
    26    }
    27
    28
    29    global Process.PluginResult invoke(Process.PluginRequest request) {  
    30
    31        Map<String, Object> result = new Map<String, Object>(); 
    32        String AccountSid = (String)request.inputParameters.get('AccountSid');
    33        String token = (String)request.inputParameters.get('Token');
    34        String To = (String)request.inputParameters.get('To');
    35        String From_a = (String)request.inputParameters.get('From');
    36        String Message = (String)request.inputParameters.get('Message');
    37        if (Message == null) Message = 'Your verification code is: ';
    38
    39        TwilioRestClient client = new TwilioRestClient(AccountSid, Token);
    40        TwilioSMS sms;
    41
    42        Integer rand = Math.round(Math.random()*100000);
    43        String VerificationCode = string.valueOf(rand);
    44        String Body = Message + VerificationCode;
    45
    46        Map<String,String> params = new Map<String,String> {
    47            'To'  => To,
    48            'From' => From_a,
    49            'Body' => Body
    50        };
    51
    52        try {
    53            sms = client.getAccount().getSMSMessages().create(params);
    54            result.put('Status', sms.getStatus());
    55        } catch(Exception ex) {
    56            result.put('Status', 'Failure');
    57        }
    58        result.put('VerificationCode', VerificationCode);
    59        return new Process.PluginResult(result);
    60    }
    61}
  11. Create an SMS plug-in that generates an OTP code and sends it via SMS to the user’s mobile number. The plug-in takes these inputs.
    • AccountSid—Twilio Account SID (username from your Twilio account)
    • Token—Twilio Auth Token (password from your Twilio account)
    • From—The SMS From number
    • Message—The message sent to the user with the verification code
    • To—The user’s mobile phone number
    Create the SMS plugin
    The plug-in returns two values.
    • Status—The status of the SMS delivery operation
    • VerificationCode—The verification code generated and sent to the user The plugin returns two outputs
  12. Create a Screen element that prompts for the verification code received. Create a screen element
  13. Create a Decision element with two outcomes.
    • Valid—The verification code (stored in {!VerificationCode}) is the same as the code the user entered in the Code screen component.
    • Invalid—The Valid outcome’s condition isn’t met, so the outcome is invalid. To create this outcome, change the label of the default outcome to Invalid.
    Create a decision element
  14. Connect the elements together. When you connect the decision to the legacy Apex action, choose the Invalid outcome. SMS 2FA process
  15. Save and activate the flow.
  16. Connect the login flow to a user profile. Connect flow to a profile
  17. Log out, and then log in as a test user that’s connected with a test profile. sample welcome screen

Extending the Flow

In a production deployment, it’s common to extend this basic flow. For example, you can add customization, validation, or policies, such as:
  • Branding—Add a corporate logo and message to the verification screen.
  • Validation—Verify whether the user record included a phone number. If not, prompt the user to enter one.
  • Retries—If the OTP code that the user provides is wrong, the login flow generates a new OTP code and sends it to the user. It’s typical to limit the number of retries or to temporarily block a user login after several unsuccessful verification attempts.
  • Policies—If the user has registered a landline phone but not a mobile phone number, send the OTP over voice rather than SMS. Alternatively, if Salesforce doesn’t have a registered phone number for the user, send the OTP code by email. Another approach is to challenge the user with a verification method such as a Salesforce time-based OTP or a hardware-based OTP, like a YubiKey.