User Workflow

This section provides information about functions, conditions, and variable resolvers available for the user workflow, as well as initial actions and reserved actions.

Table of Contents

User Workflow: Initial Actions

The following initial actions are valid for Akana API Platform workflows relating to users:

@UserPhoneNotRequired

Used to define custom workflow for a scenario where phone number is not required for users who meet one or more specified conditions. For example, using @UserPhoneNotRequired in combination with the workflow condition, doesUserBelongToDomain, customizes the user workflow so that users belonging to a specific login domain are not required to provide a telephone number. If this functionality is in place, and the condition is met, the phone number field is not required, and is not shown to the user.

Examples/Notes/Additional Information

In the example below, @UserPhoneNotRequired is used in combination with doesUserBelongToDomain. If the user belongs to the specified domain, the condition is met, and the new user is allowed to log in without providing a phone number on the user profile.

Note: during domain configuration, the mail attribute must be mapped in the setting for the referenced domain so that the user can be identified. In the OpenID Connect Relying Party domain in the Community Manager developer portal (Admin > Domains), it's Tab 7. See Tab 7: User (Site Admin help).

<action id="25" name="@UserPhoneNotRequired">
  <restrict-to>
    <conditions type="AND">
      <condition type="doesUserBelongToDomain">
        <arg name="DomainName">acmepaymentscorpLDAP</arg>
      </condition>
    </conditions>
  </restrict-to>
  <results>
    <unconditional-result old-status="none" status="init-phone-check" step="-1"/>
  </results>
</action>

To see where these fit into the workflow document, you can download the user workflow from the Community Manager developer portal. The new initial action and condition are in the default workflow, but commented out.

@AllowMarkUserAsRegistered

Valid in version: 2019.1.35 and later

Overrides the default behavior for external domain users, which requires them to complete an abbreviated registration process before the user is moved to a registered state.

By default, when a user logs in to the Community Manager developer portal for the first time using a third-party domain, the user is in a pending_validation state. Instead, this initial action sets the user to a registered state.

This initial action is not the default, but can be added if needed via custom workflow. It only applies to external domain users.

Note: This initial action relies on the third-party provider returning the user's email after authentication, which is generally the case. The Community Manager developer portal must have the user email information to register the user.

Examples/Notes/Additional Information

In the example below, @AllowMarkUserAsRegistered is used to identify users who are logging in via a specific domain type or domain name, and mark them as Registered Users. This example uses two conditions with OR, which requires only one of the conditions to be met. With AND, the DomainType and DomainName values must both meet the condition. Another way that this could be implemented is to remove the entire <restrict-to> section. In this scenario, the initial action would apply to all users who log in via a third-party login domain.

<action id="500" name="@AllowMarkUserAsRegistered">
  <restrict-to>
    <conditions type="OR">
      <condition type="isDomainType">
        <arg name="DomainType">com.soa.securitydomain.openidconnect.relyingparty</arg>
      </condition>
      <condition type="isDomainName">
        <arg name="DomainName">GoogleRP</arg>
      </condition>
    </conditions>
  </restrict-to>
  <results>
    <unconditional-result old-status="none" status="unknown" step="-1"/>
  </results>
</action>

In the above:

  • DomainType value is the back-end value for the domain. To meet this condition, the domain must be an OpenID Connect Relying Party domain: com.soa.securitydomain.openidconnect.relyingparty.
  • DomainName value is the name of a third-party login domain set up in the Community Manager developer portal.

To set up custom workflow to change registration status of third-party domain users on first login

  1. In the Community Manager developer portal, go to Admin > Workflows and download the workflow:definition:user:v2 workflow.
  2. In the workflow, add the workflow section shown above, or some version of it, inside the <initial-actions> section.
  3. Update the values for DomainType and DomainName so that they are correct for your installation.
  4. Rename the workflow (for example, to default-user-workflow-v2-3p-auto-login.xml).
  5. Upload the modified user workflow. See To upload a custom workflow.
  6. Assign the updated workflow as the default for user workflow. See How do I specify a custom workflow for platform users?

When these steps are complete, test. A new user, logging in with the specified domain, should automatically be moved to a Registered state. If you are displaying API documentation in a third-party portal (see Rendering API documentation in a third-party portal), the user will be able to see the API documentation as an authenticated user without having to log in to the Community Manager developer portal.

User Workflow: Reserved Actions

The following reserved actions are defined for user workflows:

@Add

Used when the Site Admin adds a user using the UserAPI.addUser() operation (see POST /api/users).

The user is automatically registered before this initial action is added. This action does not affect the user's registered state, but can be used for post-functions such as sending notifications.

@AddApp

Determines whether the current user has permission to add an app.

By default, all registered users have permission to add an app. However, this action can be combined with a condition, such as authorizeByAtmosphereRole, authorizeByGroup, authorizeByDomain, or authorizeByEmail to determine whether the current user has permission to add an app.

For example, you might want to configure a custom workflow to support one or more of the following scenarios:

  • Only tenant business administrators can create apps.
  • Users that are self-registered can create apps, but not users added by the Site Admin.
  • All users can browse APIs, but only certain approved users can create apps. This implies users that are in a specific workflow state can create apps.

@AddGroup

Determines whether the current user has permission to add a group.

By default, all registered users have permission to add a group. However, this action can be combined with a condition, such as authorizeByAtmosphereRole, authorizeByGroup, authorizeByDomain, or authorizeByEmail to determine whether the current user has permission to add a group.

For example, you might want to configure a custom workflow to support one or more of the following scenarios:

  • Only tenant business administrators can create groups.
  • Users that are self-registered can create groups, but not users added by the Site Admin.
  • Only certain approved users can create groups.

@AgreementsAccepted

Indicates that any required platform legal agreement was accepted by the user. This is only used during the login process.

@ChallengeQuestionsAnswered

Indicates that the user has provided answers to any required security challenge questions.

This reserved action is a hook that you can use to extend the workflow with additional actions such as generating a notification and/or Board item, or initiating a back-end process.

@ForcedPasswordChanged

Indicates that the user has changed the password. Invoked when the user changes the password as the result of a change password requirement.

This reserved action is a hook that you can use to extend the workflow with additional actions such as generating a notification and/or Board item or initiating a back-end process.

@Invite

Checks the user settings to determine whether inviting a non-platform user to a team or group is allowed or not. The default workflow uses this setting to decide whether this initial action is allowed.

If you are creating a custom user workflow, include this initial action if you want to allow inviting users that are not registered in the platform. You can include one or more additional conditions as needed.

@Login

This reserved action is invoked whenever the user tries to log in.

Note: this is currently used only for platform users, not third-party domain users.

When the user logs in, the workflow determines the status value returned in the login response—which then determines the next step. If there is a pending task to be performed, these are prompted before login is complete. The possible pending tasks for @Login are:

  • ChangePassword: forces the user to change password as part of the login process. In the default workflow, this is applicable when the Site Admin has provided a temporary password for initial user login.
  • ForceAcceptAgreements: forces the user to accept the platform legal agreement as part of the login process.
  • securityQuestionsAnswered: forces the user to provide answers for security challenge questions as part of the login process. The number required depends on platform security settings.

As part of creating a default workflow, you could create additional pending tasks. However, you would also have to develop the UI to support those tasks so that users could log in via the UI.

@ModifyProfile

Used to determine whether the platform settings allow the user to modify the user profile.

In the default platform workflow, this is how it is used:

  • If users do self-signup, they can modify the profile or not based on this setting.
  • If users are added by administrators (Managed Users), they cannot modify the profile unless they are site administrators.
  • If the platform setting allowing users to modify their own profiles is turned on, even a Site Admin cannot modify his/her own profile; it must be modified by another Site Admin.

Examples/Notes/Additional Information

In the example below, the workflow for the @ModifyProfile workflow action checks to make sure that either the setting to allow users who are not Managed Users to modify their own profiles is turned on (condition userSettingsAllowModifyProfile), or else the current user is a Site Admin. If one of these conditions is met, the action can go forward.

Note that this condition only applies to users who signed up for the platform themselves (whether by invitation, by setting up a platform account, or by setting up an account with a third-party identity provider such as Google). Managed Users, added by the Site Admin, cannot modify their own profiles, even if the platform setting is turned on.

If the setting to allow users to modify their own profiles is disabled, a user who is a Site Admin cannot modify his/her own profile. In this case, only another Site Admin can modify this Site Admin's profile.

<action id="456" name="@ModifyProfile">
  <restrict-to>
    <conditions type="OR">
      <conditions type="AND">
        <condition type="authorizeSelf"/>
        <condition type="userSettingsAllowModifyProfile"/>
      </conditions>
      <conditions type="AND">
        <condition type="authorizeByAtmosphereRole">
          <arg name="role">&RoleSiteAdmin;</arg>
        </condition>
        <condition negate="true" type="authorizeSelf"/>
      </conditions>
    </conditions>
  </restrict-to>
  <results>
    <unconditional-result old-status="registered" status="registered" step="450"/>
  </results>
</action>

@PasswordChanged

Indicates that the user has changed the password. Invoked when the user voluntarily changes the password via the Profile > Password page.

This reserved action is a hook that you can use to extend the workflow with additional actions such as generating a notification and/or Board item or initiating a back-end process.

@Setup

Used in an upgrade scenario, where existing users are added to the scenario because an upgraded installation requires all users to be in the workflow. During the upgrade process, when the Upgrade CM Models admin action is run, this reserved action is used to add all registered users to the workflow. Currently this is applicable only to platform users, not third-party users.

@Signup

Used to determine whether the platform settings allow the user to sign up to the platform (user-initiated signup).

The UsersAPI.signupUser() operation, see POST /api/users/signupUser[/{InvitationCode}], uses the availability of this initial action to either reject the message request or proceed with the signup.

The platform user interface references the user settings to determine whether signup is allowed or not. The default workflow allows or rejects signup based on the UI setting.

@UserDisabled

Can be used to initiate one or more actions when the current user's account is disabled.

For example, you might want to configure a custom workflow so that when the current user's account is disabled, a Board item is created and a notification is sent to the user and, perhaps, a different notification to Site Admins and/or Business Admins.

Applicable board items and notifications:

  • com.soa.board.item.user.disabled
  • User notification: com.soa.notification.type.user.disabled
  • Admin notification: com.soa.notification.type.user.account.status.change

@UserEnabled

Can be used to initiate one or more actions when the current user's account is enabled.

For example, you might want to configure a custom workflow so that when the current user's account is enabled, a notification is sent to the user and, perhaps, a different notification to Site Admins and/or Business Admins.

Applicable board items and notifications:

  • com.soa.board.item.user.enabled
  • User notification: com.soa.notification.type.user.disabled
  • Admin notification: com.soa.notification.type.user.account.status.change

@UserLocked

Can be used to initiate one or more actions when a user's account is locked.

For example, you might want to configure a custom workflow so that when the current user's account is locked, a notification is sent to the user and, perhaps, a different notification to Site Admins and/or Business Admins.

Applicable board items and notifications:

  • com.soa.board.item.user.locked
  • User notification: com.soa.notification.type.user.locked
  • Admin notification: com.soa.notification.type.user.account.status.change

@UserUnlocked

Can be used to initiate one or more actions when a user's account is unlocked.

For example, you might want to configure a custom workflow so that when the current user's account is unlocked, a notification is sent to the user and, perhaps, a different notification to Site Admins and/or Business Admins.

Applicable board items and notifications:

  • com.soa.board.item.user.unlocked
  • User notification: com.soa.notification.type.user.unlocked
  • Admin notification: com.soa.notification.type.user.account.status.change

@ResolveLoginPendingTask

Used to determine whether there are pending tasks the user must complete before login is complete and, if so, to guide the user through those tasks; for example, validating a two-factor verification code or requesting a new verification code.

This reserved action is valid only when the user is in the login process; for example, if two-factor authentication is turned on and the user has authenticated with credentials but has not yet provided the verification code.

The default workflow restricts this action to be valid only when the isSessionInLoginProcess condition is true.

Additional functions can be used to specify actions that will be taken to resolve the pending task, such as:

  • If 2FA is turned on but has not been initiated, generate the verification code, send the cookie, and generate a notification.
  • If change password is required, forcing the user to change the password.
  • If change password is not required, check if agreements are accepted, and if there are pending legal agreements, guiding the user to accept the legal agreement.
  • If security questions are required to complete login, guiding the user to provide answers to the security questions.

Examples/Notes/Additional Information

In the example below, this reserved action is invoked if the pre-conditions are met. Pre-conditions are that the login is in process, the auth token property exists (indicating that 2FA is required) and the auth token property does not match 2FAComplete (indicating that the two-factor authentication process is not complete).

<action id="499" name="@ResolveLoginPendingTask">
  <restrict-to>
    <conditions type="AND">
      <condition type="isSessionInLoginProcess"/>
      <condition type="authTokenPropertyExists">
        <arg name="PropertyName">2FAData</arg>
        <arg name="message">2FA not initiated</arg>
      </condition>
      <condition negate="true" type="authTokenPropertyMatches">
          <arg name="PropertyName">2FAComplete</arg>
          <arg name="PropertyValue">Yes</arg>
      </condition>
      <condition type="argumentExists">
        <arg name="ArgName">Action</arg>
        <arg name="message">Action is required</arg>
      </condition>
      <condition type="argumentExists">
        <arg name="ArgName">task.id</arg>
        <arg name="message">Task id is required</arg>
      </condition>
    </conditions>
  </restrict-to>

@UserActivated

Valid in version: 2019.1.6 and later

In a scenario where platform self-signup is disabled, but instead a user account is added by the Site Admin, this reserved action detects whether a user account added by the Site Admin has been activated, so that one or more actions can be taken based on that.

By default, when the user added by the Site Admin is activated, no notification is sent.

By default, although the @UserActivated action is in the default user workflow, and includes a sendNotification function, this action is disabled, because it's commented out in line 834 of the default user workflow:

<!-- <common-action id="19" /> -->

With this reserved action in use, by un-commenting the comment-action-id line in the default user workflow, when the Site Admin activates the account, an email notification is sent to the user to notify the user that the account is now activated.

Examples/Notes/Additional Information

If you remove the comment from line 834, as shown above, the workflow action below comes into effect. Then, if the Site Admin activates a user, the notification com.soa.notification.type.user.activated is sent to the user.

The example below shows the segment of the default user workflow that implements the @UserActivated reserved action when the comment tag is removed from line 834.

Note: The section of the workflow doc below also shows an additional function to send a notification to the Site Admin. To activate this function, you would also need to add the notification, which is not currently part of the product.

<action id="19" name="@UserActivated">
  <results>
    <unconditional-result old-status="${workflow.step.name}" status="${workflow.step.name}" step="-1">
      <post-functions>
        <function type="sendNotification">
          <arg name="role">User</arg>
          <arg name="notificationType">com.soa.notification.type.user.activated</arg>
        </function>
        <!--<function type="sendNotification">
          <arg name="role">SiteAdmin</arg>
          <arg name="notificationType">com.soa.notification.type.user.account.state.change</arg>
        </function>-->
      </post-functions>
    </unconditional-result>
  </results>
</action>

To implement this workflow action

  1. Download the default user workflow. See To download a workflow document from the Community Manager developer portal.
  2. Remove the comment tag from line 834.
  3. Rename the workflow (for example, to default-user-workflow-v2-useractivated.xml).
  4. Upload the modified user workflow. See To upload a custom workflow.
  5. Assign the updated workflow as the default for user workflow. See How do I specify a custom workflow for platform users?

User Workflow: Functions

The following functions are available for the user workflow:

markUserPermanent

This function marks the user as permanent in the database. Until a user logs in for the first time, the user is not marked as permanent; the user's invitation expires after a pre-set time period, and invited users who did not complete the process are periodically purged from the database. This function marks the record as permanent so that it does not get purged.

Note: Two separate functions exist, markUserPermanent and markUserPermanentIfFirstLogin, to offer flexibility in implementation. In the out-of-the box user workflow there is no difference in implementation between these two, but a custom workflow could be designed to differentiate between a user's first login and subsequent logins. For example, a custom workflow could require users to renew the account every year, and could enforce the account renewal process.

markUserPermanentIfFirstLogin

If this is the first login by the user, this function marks the user as permanent in the database. Until a user logs in for the first time, the user is not marked as permanent; the user's invitation expires after a pre-set time period, and invited users who did not complete the process are periodically purged from the database. This function marks the record as permanent so that it does not get purged.

The default user workflow uses this function to mark the user as permanent when the user logs in for the first time.

Note: Two separate functions exist, markUserPermanent and markUserPermanentIfFirstLogin, to offer flexibility in implementation. In the out-of-the box user workflow there is no difference in implementation between these two, but a custom workflow could be designed to differentiate between a user's first login and subsequent logins. For example, a custom workflow could require users to renew the account every year, and could enforce the account renewal process.

Examples/Notes/Additional Information

In the example below, the user has completed the registration process. LoginState is set to LoginComplete and the user is therefore marked as permanent. The notification is currently commented out.

<unconditional-result old-status="registered" status="registered" step="400">
  <pre-functions>
    <function type="setProperty">
      <arg name="LoginState">&LoginComplete;</arg>
    </function>
    <function type="markUserPermanentIfFirstLogin"/>
    <!--  invoke send Notification on first time login.  -->
  </pre-functions>
</unconditional-result>

addBoardItem

Adds a Board item to one or more boards for the user.

This function can load a message template and dynamically fill in some values into the template with the help of a parameter resolver. When the workflow action is invoked, a comment is added by the user. The parameter resolver can be used to include this comment in the board item title/description, email message subject/body, and/or a dashboard notification. To use this feature, reference the parameter resolver {action.comment} in the notification template. When the action is performed, this parameter resolver will be replaced by the comment entered by the user performing the workflow action.

Parameters

Name Description/Values
boardItemTemplateId

The ID of the Board item notification template, from the database, to be used for the Board item title and description. For example:

  • com.soa.board.item.user.logged.in.first.time
Visibility

The visibility of the Board item. Valid values:

  • Public
  • Limited
  • RegisteredUsers
Type The Board item type. Currently, the only valid value is Discussion.
targetBoard

Used to specify that one item could be added to multiple boards.

There are two ways to add the targetBoard information:

  • targetBoard.{parametername}, listing separately each parameter to be added to the target board.
  • targetBoards, plural parameter, with a comma-separated list of Board items to be added.
viewers

Indicates who can view the Board item. For example, providing the App ID as a value indicates that anyone who can administer the app can view the Board item. There are two ways to specify the viewers:

  • viewer.{parametername}, listing separately each applicable ID to indicate that users who have view of that resource have view of the Board item.
  • viewers, plural parameter, with a comma-separated list of Board items to be added.

Examples of values:

  • app.team.group.dn: app team
  • connected.apis.id: API Admins for connected APIs
  • business.dn: Business Admins for the business

Examples/Notes/Additional Information

The example below shows adding a Board item as a post-function, to announce that a user added by the Site Admin has logged in for the first time. In this example, targetBoard is a set of separate arguments, and viewers is a plural parameter.

<post-functions>
  <function type="markUserPermanent"/>
  <function type="addBoardItem">
    <arg name="boardItemTemplateId">com.soa.board.item.user.logged.in.first.time</arg>
    <arg name="visibility">Limited</arg>
    <arg name="type">Discussion</arg>
    <arg name="author">${site.admin.dn}</arg>
    <arg name="targetBoard.apiversion">${connected.apiversion.ids}</arg>
    <arg name="targetBoard.api">${connected.apis.id}</arg>
    <arg name="viewers">${connected.apis.id},${business.dn},${site.admin.dn}</arg>
  </function>
  <function type="sendNotification">
    <arg name="role">ApiAdmins,SiteAdmin,BusinessAdmin</arg>
    <arg name="notificationType">com.soa.notification.type.user.logged.in.first.time</arg>
  </function>

sendNotification

Triggers the specified email/Dashboard notification based on an event relating to a user.

Note: The email isn't sent instantly; it is queued to be sent. It goes to the notifications queue, and the job runs every 60 seconds. There might be a short delay before the user receives the email.

This function can load a message template and dynamically fill in some values into the template with the help of a parameter resolver. When the workflow action is invoked, a comment is added by the user. The parameter resolver can be used to include this comment in the board item title/description, email message subject/body, and/or a dashboard notification. To use this feature, reference the parameter resolver {action.comment} in the notification template. When the action is performed, this parameter resolver will be replaced by the comment entered by the user performing the workflow action.

Parameters

Name Description/Values
notificationType

The type of notification being sent. Can be any valid notification existing in the platform. For example:

  • com.soa.notification.type.user.admin.added
  • com.soa.notification.type.user.2fa.verification.code
role

The role to which the notifications will be sent. Valid values:

  • ApiAdmins
  • SiteAdmin
  • BusinessAdmin
  • Self
  • User

Examples/Notes/Additional Information

In the example below, a notification is sent as a post-function when the Site Admin adds a user.

<step id="20" name="Route Admin Add" >
  <actions>
    <action id="21" name="init-admin-add" auto="TRUE">
      <results>
        <result old-status="none" status="registered" step="400" >
          <conditions type="AND">
            <condition type="isLocalDomainUser"/>
          </conditions>
          <post-functions>
            <function type="sendNotification">
              <arg name="notificationType">com.soa.notification.type.user.admin.added</arg>
              <arg name="role">ApiAdmins,SiteAdmin,BusinessAdmin</arg>
            </function>
          </post-functions>
        </result>
        <unconditional-result old-status="none" status="unknown" step="-1"/>
      </results>
    </action>
  </actions>
</step>

setProperty

Sets any property defined by the workflow, allowing the workflow to communicate back to the application or to the response stream by setting a property based on the workflow. The property can be used inside the workflow to give feedback to the application and thus guide the process flow.

setProperty can be used to set any property in any workflow document.

For example, in the default user workflow, the @Login reserved action, invoked at login, uses setProperty to determine the next step—based on the workflow and existing conditions/information, whether the login action is complete or whether one or more required actions must be completed first.

In this scenario, the argument is PendingTask and there are three possible values (see User Workflow: Reserved Actions.

Examples/Notes/Additional Information

In the example below, this function is used to evaluate whether the user logging in is a local domain user and, if so, whether a password change is required. If a password change is required, it is set as a pending task that must be completed prior to login.

<step id="400" name="managed">
  <actions>
    <action id="401" name="@Login">
      <results>
        <result old-status="registered" status="registered" step="400">
          <conditions type="AND">
            <condition type="isLocalDomainUser"/>
            <condition type="isChangePasswordRequired"/>
          </conditions>
          <pre-functions>
            <function type="setProperty">
              <arg name="PendingTask">&ChangePassword;</arg>
            </function>
          </pre-functions>

setTwofaDeliveryOptions

When two-factor authentication is in use, indicates the delivery options supported for the 2FA verification code.

Note: In the platform's out-of-the-box 2FA use workflow, if more than one delivery option is defined as valid in the workflow, the platform does not generate the verification code immediately. Instead, it returns the valid delivery options. The user can then select an option; based on that, the verification code is generated.

Parameters

Name Description/Values
VoiceSupported Indicates whether a voice option is supported for the 2FA verification code. Boolean true/false.
TextSupported Indicates whether text messaging is supported for the 2FA verification code. Boolean true/false
EmailSupported Indicates whether email is supported for the 2FA verification code. Boolean true/false.

Examples/Notes/Additional Information

In the example below, setTwofaDeliveryOptions is invoked as the first pre-function to specify the supported delivery options. Depending on the delivery options supported, subsequent workflow actions guide the process.

<pre-functions>
  <function type="setTwofaDeliveryOptions">
    <arg name="VoiceSupported">true</arg>
    <arg name="TextSupported">true</arg>
    <arg name="EmailSupported">true</arg>
  </function>
  <function type="handle2FATask">
    <arg name="PropertyName">2FAData</arg>
    <arg name="2FAData">${authtoken.property.2FAData}</arg>
    <arg name="2FACode">${arg.2fa.code}</arg>
    <arg name="2FASalt">${authtoken.property.2FASalt}</arg>
  </function>
</pre-functions>

setTwofaDeliveryTarget

When two-factor authentication is in use, indicates the delivery option that the user has chosen for the 2FA verification code.

Parameters

Name Description/Values
DeliveryOptions ${arg.2fa.task.data}. This resolves to the value of 2fa.task.data. See ${arg.xxxx}.
DeliveryMechanism

Indicates the way the verification code will be delivered. Valid values, if defined as true in arguments for the setTwofaDeliveryOptions function:

  • Email
  • Voice
  • Text

Examples/Notes/Additional Information

In the example below, setTwofaDeliveryTarget is set to text.

<pre-functions>
  <function type="setTwoFADeliveryTarget">
    <arg name="DeliveryOptions">${arg.2fa.task.data}</arg>
    <arg name="DeliveryMechanism">Text</arg>
    <arg name="DeliveryTargetAddress">${arg.twofa.delivery.target.address}</arg>
    <!--<arg name="Index">0</arg>--><!-- Either or -->
  </function>
  <function type="generate2FACode"/>
  <function type="setAuthTokenProperty">
    <arg name="PropertyName">2FAData</arg>
    <arg name="PropertyValue">${arg.2fa.authtoken.property}</arg>
  </function>

send2FACodeToEmail

When two-factor authentication is in use, indicates that the delivery mechanism by which the 2FA verification code will be sent to the user is email, and conveys the applicable information.

The platform's out-of-the-box user workflow includes support for sending the 2FA code to an email address.

Note: The email isn't sent instantly; it is queued to be sent. It goes to the notifications queue, and the job runs every 60 seconds. There might be a short delay before the user receives the email.

Parameters

Name Description/Values
DeliveryTargetAddress

${arg.twofa.delivery.target.address}: the specific email address the verification code will be sent to.

If this parameter is used in a custom function to send the code to a phone, it could be the phone number, including country code.

DeliveryMechanism

${arg.DeliveryMechanism}: indicates the way the verification code will be sent to the user. The only valid value for this function is Email.

If this parameter is used in a custom function to send the code to a phone, the value could be Voice or Text.

2FAData A value used internally. Not currently needed.
2FAVerificationCode ${arg.verificationCode}: the specific verification code sent to the user.
2FAVerificationCodeValidMinutes ${arg.2fa.verification.code.valid.minutes}: the validity period for the 2FA code, as set up in the platform settings.

Examples/Notes/Additional Information

The example below is a template for this function.

<function type="send2FACodeToEmail">
  <arg name="DeliveryTargetAddress">${arg.twofa.delivery.target.address}</arg>
  <arg name="DeliveryMechanism">${arg.DeliveryMechanism}</arg>
  <arg name="2FAData">${arg.2fa.task.data}</arg>
  <arg name="2FAVerificationCode">${arg.verificationCode}</arg>
  <arg name="2FAVerificationCodeValidMinutes">${arg.2fa.verification.code.valid.minutes}</arg>
</function>

generate2FACode

Generates the code to be used in the two-factor authentication process, and:

  • Sets the generated 2FA code details into the argument properties.
  • Sets an argument property with an opaque token that must be saved so that it can be used later for validating the 2FA code.

setPendingTask

Specifies a pending task, and allows some associated data relating to the pending tasks to be included. For example, this can be used to specify that two-factor authentication is required and is a pending task that must be completed before user login is complete; in this scenario, this could then give the 2FA data.

unmarshall2FACode

Extracts the 2FA object from the auth token data, and converts the opaque 2FA token into a structure that other 2FA functions can use.

This function allows the 2FA data to be read if needed; for example, by the GET /api/login/status operation that returns information about the user's current login status.

handle2FATask

Relating to two-factor verification codes generated by the Generate2FACode function, handle2FATask takes care of any of the following:

  • Generate the verification code
  • Validate the verification code

Examples/Notes/Additional Information

In the example below, the handle2FATask function sets a value for the opaque code, 2FACode, and sets the structure into the auth token property, 2FAData.

<pre-functions>
  <function type="handle2FATask">
    <arg name="PropertyName">2FAData</arg>
    <arg name="2FAData">${authtoken.property.2FAData}</arg>
    <arg name="2FACode">${arg.2fa.code}</arg>
  </function>
</pre-functions>

validate2FACode

Validates the two-factor verification code. The session must be in login process for this function to be valid.

terminateSession

Allows the workflow to force logout and end the current user's session. This function removes the browser cookie, which means the session is terminated.

There might be a short delay before it's apparent to the user that the UI session has ended. However, if this is used as part of the ResolveLoginPendingTask action, the UI will recognize it immediately and give the user a notification message.

Examples/Notes/Additional Information

In the example below, the workflow checks whether the code is expired or max attempts is exceeded. If either of these conditions is met, 2FA required is set as a pending task and the user's cookie is removed. This means that the user must start the login process from the beginning.

<conditions type="OR">
  <condition type="is2FACodeExpired"/>
  <condition type="is2FAMaxAttemptsExceeded"/>
</conditions>
<pre-functions>
  <function type="setPendingTask">
    <arg name="PendingTask">2fa.required</arg>
    <arg name="TaskData">${arg.2fa.task.data}</arg>
  </function>
  <function type="removeAuthTokenProperty">
    <arg name="PropertyName">2FAData</arg>
  </function>
  <function type="terminateSession"/>
</pre-functions>

acceptAgreementForDomainUsers

By default, the platform required acceptance of the platform legal agreement, if one is uploaded to the Community Manager developer portal.

The acceptAgreementForDomainUsers function allows customization of the workflow so that the mandatory acceptance of the platform legal agreement is skipped for users belonging to a specific login domain.

If this custom workflow function is in place, new users who match the specified condition do not see the developer agreement. For these users, the login process just skips that step.

Examples/Notes/Additional Information

In the example below, this function is added in the workflow document, in the "@Login" section, to skip requiring acceptance of the Community Manager developer portal agreement for users of the acmepaymentscorp domain. This argument also supports multiple domains with comma separator.

<function type="acceptAgreementForDomainUsers">
  <arg name="DomainName">acmepaymentscorp</arg>
</function>

Note: To see where this fits into the workflow document, you can download the user workflow from the Community Manager developer portal. The new function is in the default workflow, but is commented out.

acceptAgreementForUsersBelongToRoles

By default, the platform required acceptance of the platform legal agreement, if one is uploaded to the Community Manager developer portal.

The acceptAgreementForUsersBelongToRoles function allows customization of the workflow so that the mandatory acceptance of the platform legal agreement is skipped for users with a specific role.

If this custom workflow function is in place, new users who match the specified condition do not see the developer agreement. For these users, the login process just skips that step.

Examples/Notes/Additional Information

In the example below, this function is added in the workflow document, in the "@Login" section, to skip requiring acceptance of the Community Manager developer portal agreement for users who have either the Business Administrator or API Developer roles. This argument supports multiple roles with comma separator, as shown below.

<function type="acceptAgreementForUsersBelongToRoles">
  <arg name="role">Business Administrator,API Developer</arg>
</function>

Note: To see where this fits into the workflow document, you can download the user workflow from the Community Manager developer portal. The new function is in the default workflow, but is commented out.

addRoleToUser

Valid in Version: 2019.1.34 and later

By default, when a new user logs in for the first time using a login domain set up in the Community Manager developer portal, the user starts without a role assignment.

The addRoleToUser function allows customization of the workflow to modify this default behavior so that a new user, logging in for the first time with a specific login domain, is automatically assigned to a specific role.

To implement this custom workflow function, add the addRoleToUser function to the workflow. You'll also need to specify valid values for the login domain and the role to assign to users logging in with that domain. For instructions and an example, see User Workflow: Assigning a default role to users of a specific login domain.

Examples/Notes/Additional Information

In the example below, users of an OpenID Connect Relying Party domain set up in the Community Manager developer portal, with the name Google OIDC RP Domain, are automatically assigned the API Developer role.

This example shows a snippet from the full workflow document (for download information, see User Workflow: Assigning a default role to users of a specific login domain). Line numbers have been added for reference purposes.

49) <action id="4" name="@Setup">
50)   <results>
51)
52)
53)     <!--<result old-status="none" status="unknown" step="30">
54)       <conditions type="AND">
55)         <condition type="isDomainType">
56)           <arg name="DomainType">com.soa.securitydomain.openidconnect.relyingparty</arg>  <!– e.g. com.soa.securitydomain.openidconnect.relyingparty –>
57)         </condition>
58)         <condition type="isDomainName">
59)           <arg name="DomainName">Google OIDC RP Domain</arg>  <!– e.g. Google OIDC RP Domain –>
60)         </condition>
61)       </conditions>
62)       <post-functions>
63)         <function type="addRoleToUser">
64)           <arg name="role">API Developer</arg>  <!– e.g. BUSINESS ADMINISTRATOR –>
65)         </function>
66)       </post-functions>
67)     </result>-->
68)     <unconditional-result old-status="none" status="registered" step="30"/>
69)   </results>
70) </action>

In the above:

Note: To see where this fits into the workflow document, you can download the user workflow from the Community Manager developer portal. The example above is taken from the default-user-workflow-v2 user workflow document available within the Community Manager developer portal. The new function is in the default workflow, but is commented out.

User Workflow: Conditions

The following conditions apply to the user workflow:

isLocalDomainUser

Tests to see if the user is a local domain user.

IsRegisteredUser

Tests to see that the user is a registered platform user.

IsLocalRegisteredUser

Tests to see that the user is a registered platform user with a local account.

IsLastLoginEmpty

Tests to see whether this is the first login.

IsChangePasswordRequired

Tests to see if a change of password for the user is required; for example, at first login when the Site Admin has provided the user with a temporary password.

AgreementsAccepted

Tests to see whether the user has accepted the platform legal agreement, if required.

IsForceChallengeQuestionsAnsweredOnLoginSetup

Tests to see whether the platform user setting, EnforceChallengesSetupOnLogin, is enabled. If the setting is enabled, the user must provide answers to the challenge questions as part of login.

SecurityQuestionsAnswered

Tests to see if the user has provided answers to the required number of security questions.

IsSelfSignupAllowed

Tests to see whether the platform user setting, SelfSignup, is enabled. If the setting is disabled, the user cannot sign themselves up for the platform.

UserSettingsAllowModifyProfile

Tests to see whether the platform business security setting, UserModifyEmail, is enabled. If the setting is disabled, the user cannot modify his/her profile.

Examples/Notes/Additional Information

In the example below, the workflow for the @ModifyProfile workflow action checks to make sure that either the setting to allow users who are not Managed Users to modify their own profiles is turned on (condition userSettingsAllowModifyProfile), or else the current user is a Site Admin. If one of these conditions is met, the action can go forward.

Note that this condition only applies to users who signed up for the platform themselves (whether by invitation, by setting up a platform account, or by setting up an account with a third-party identity provider such as Google). Managed Users, added by the Site Admin, cannot modify their own profiles, even if the platform setting is turned on.

If the setting to allow users to modify their own profiles is disabled, a user who is a Site Admin cannot modify his/her own profile. In this case, only another Site Admin can modify this Site Admin's profile.

For more information, refer to @ModifyProfile.

<action id="456" name="@ModifyProfile">
  <restrict-to>
    <conditions type="OR">
      <conditions type="AND">
        <condition type="authorizeSelf"/>
        <condition type="userSettingsAllowModifyProfile"/>
      </conditions>
      <conditions type="AND">
        <condition type="authorizeByAtmosphereRole">
          <arg name="role">&RoleSiteAdmin;</arg>
        </condition>
        <condition negate="true" type="authorizeSelf"/>
      </conditions>
    </conditions>
  </restrict-to>
  <results>
    <unconditional-result old-status="registered" status="registered" step="450"/>
  </results>
</action>

IsInviteUnRegisteredUserAllowed

Tests to see whether the platform user setting, InviteUnregisteredUsers, is enabled; if so, returns true. If the setting is disabled, group members cannot invite unregistered users to join platform groups or teams.

authorizeSelf

Tests to see whether the user running this action is the user whose workflow document is being used. For example, it is used at login by default. It is also used for modify profile. If the Site Admin is modifying the user's profile, authorizeSelf is false. If the user is modifying his/her own profile, it is true.

Is2FAEnabled

Checks the platform settings to see whether two-factor authentication is enabled for user login; returns Boolean true or false.

<condition type="is2FAEnabled"/>

is2FARequired

Checks to see whether two-factor authentication is required for login of the current user, based on the recurrence mode specified in the platform's 2FA settings and the user's history of completing the 2FA process. Returns Boolean true or false.

<condition type="is2FARequired"/>

isNoDeliveryOption

When used with negative=true, as shown below, checks to make sure that there is at least one 2FA delivery option specified. Returns Boolean true or false.

Arguments

Name Description/Values
PropertyName ${arg.2fa.task.data}. This resolves to the value of 2fa.task.data. See ${arg.xxxx}.

Examples/Notes/Additional Information

<condition negate="true" type="isNoDeliveryOption">
  <arg name="PropertyName">${arg.2fa.task.data}</arg>
</condition>

isDeliveryOptionsOnlyOne

Tests to see if there is only one delivery option defined for the 2FA verification code. If there is only one delivery option, the user is not offered a choice. Instead, the code is generated and issued to the user by the specified delivery option and the user is directed to the UI page for entering the verification code.

Returns Boolean true or false.

Arguments

Name Description/Values
PropertyName ${arg.2fa.task.data}. This resolves to the value of 2fa.task.data. See ${arg.xxxx}.

Examples/Notes/Additional Information

<condition type="is2FAEnabled"/>
<condition type="is2FARequired"/>
<condition type="isDeliveryOptionsOnlyOne">
  <arg name="PropertyName">${arg.2fa.task.data}</arg>
</condition>

isDeliveryOptionsOnlyEmail

Checks to see if the only valid delivery option for the 2FA verification code is email (this is the platform default).

Returns Boolean true or false.

Arguments

Name Description/Values
PropertyName ${arg.2fa.task.data}. This resolves to the value of 2fa.task.data. See ${arg.xxxx}.

Examples/Notes/Additional Information

<condition type="isDeliveryOptionsOnlyEmail"><!-- out of the box support -->
  <arg name="PropertyName">${arg.2fa.task.data}</arg>
</condition>

isDeliveryTypeEmail

Checks to see if the only valid delivery option for the 2FA verification code is email (this is the platform default).

Arguments

Name Description/Values
PropertyName ${arg.2fa.task.data}. This resolves to the value of 2fa.task.data. See ${arg.xxxx}.

Examples/Notes/Additional Information

<condition type="isDeliveryOptionsOnlyEmail"><!-- out of the box support -->
  <arg name="PropertyName">${arg.2fa.task.data}</arg>
</condition>

isDeliveryTypeVoice

Checks to see if the only valid delivery option for the 2FA verification code is email (this is the platform default).

Arguments

Name Description/Values
PropertyName ${arg.2fa.task.data}. This resolves to the value of 2fa.task.data. See ${arg.xxxx}.

Examples/Notes/Additional Information

<condition type="isDeliveryOptionsOnlyEmail"><!-- out of the box support -->
  <arg name="PropertyName">${arg.2fa.task.data}</arg>
</condition>

isDeliveryTypeText

Checks to see if the only valid delivery option for the 2FA verification code is email (this is the platform default).

Arguments

Name Description/Values
PropertyName ${arg.2fa.task.data}. This resolves to the value of 2fa.task.data. See ${arg.xxxx}.

Examples/Notes/Additional Information

<condition type="isDeliveryOptionsOnlyEmail"><!-- out of the box support -->
  <arg name="PropertyName">${arg.2fa.task.data}</arg>
</condition>

is2FACodeValid

Tests to see if the verification code that the user provided is valid. Returns true if the code is valid.

In the example below, is2FACodeValid is the second condition. Not shown: if both conditions evaluate to true, the workflow sets the auth token cookie and checks if there are any other pending login steps to complete the user's login.

<result old-status="registered" status="registered" step="450">
  <conditions type="AND">
    <condition type="argumentValueEquals">
      <arg name="ArgName">Action</arg>
      <arg name="Value">validate</arg>
    </condition>
    <condition type="is2FACodeValid"/>
  </conditions>

is2FATerminated

Checks to make sure that the 2FA session has not been terminated. 2FA is terminated if conditions are violated such as if the number of attempts with an invalid code has been exceeded or if the code has expired. If the session has been terminated, this condition returns true.

Note: The valid number of attempts for a specific instance of the platform, and the timeout period for the cookie, are determined by the Site Admin in the 2FA settings.

Examples/Notes/Additional Information

In the example below, if is2FATerminated resolves to Yes, 2fa.required is set as a pending task, the current auth token property is removed, and the user's session is terminated. The user must start the login process from the beginning.

<result old-status="registered" status="registered" step="400">
  <conditions type="AND">
    <condition type="is2FATerminated"/>
  </conditions>
  <pre-functions>
    <function type="setPendingTask">
      <arg name="PendingTask">2fa.required</arg>
      <arg name="TaskData">${arg.2fa.task.data}</arg>
    </function>
    <function type="removeAuthTokenProperty">
      <arg name="PropertyName">2FAData</arg>
    </function>
    <function type="terminateSession"/>
  </pre-functions>
</result>

doesUserBelongToDomain

Checks whether the user belongs to one or more specified domains so that further actions can be taken depending on the result of the condition.

This argument also supports multiple domains with a comma separator.

Using doesUserBelongToDomain in combination with the user workflow initial action, @UserPhoneNotRequired, customizes the user workflow so that users belonging to a specific login domain are not required to provide a telephone number. If this functionality is in place, and the condition is met, the phone number field is not required, and is not shown to the user.

Examples/Notes/Additional Information

In the example below, doesUserBelongToDomain is used in combination with @UserPhoneNotRequired. If the user belongs to the specified domain, the condition is met, and the new user is allowed to log in without providing a phone number on the user profile.

Note: during domain configuration, the mail attribute must be mapped in the setting for the referenced domain so that the user can be identified. In the OpenID Connect Relying Party domain in the Community Manager developer portal (Admin > Domains), it's Tab 7. See Tab 7: User (Site Admin help).

<action id="25" name="@UserPhoneNotRequired">
  <restrict-to>
    <conditions type="AND">
      <condition type="doesUserBelongToDomain">
        <arg name="DomainName">acmepaymentscorpLDAP</arg>
      </condition>
    </conditions>
  </restrict-to>
  <results>
    <unconditional-result old-status="none" status="init-phone-check" step="-1"/>
  </results>
</action>

To see where these fit into the workflow document, you can download the user workflow from the Community Manager developer portal. The new initial action and condition are in the default workflow, but commented out.

isDomainType

Valid in Version: 2019.1.34 and later

Checks if the domain type that a new user is logging in on matches the domain specified in the custom workflow, so that further actions can be taken depending on the result of the condition.

The domain type is the back-end value for the domain type; for example, com.soa.securitydomain.openidconnect.relyingparty.

Use isDomainType in combination with isDomainName (see below) and the user workflow function, addRoleToUser, so that a new user, logging in to the Community Manager developer portal for the first time with a specific login domain, is automatically assigned to a specific role.

For an example, see addRoleToUser.

isDomainName

Valid in Version: 2019.1.34 and later

Checks if the domain name that a new user is logging in on matches the domain name specified in the custom workflow, so that further actions can be taken depending on the result of the condition.

The domain name value is the text name for the domain type as displayed in the Community Manager developer portal (More > Admin > Domains, Type column); for example, Google OIDC RP Domain.

Use isDomainName in combination with isDomainType (see above) and the user workflow function, addRoleToUser, so that a new user, logging in to the Community Manager developer portal for the first time with a specific login domain, is automatically assigned to a specific role.

For an example, see addRoleToUser.

User Workflow: Variable Resolvers

The following variable resolvers are available for the user workflow.

${arg.xxxx}

A dynamic variable resolver that can take any value as the argument. It resolves to a parameter provided in place of the xxx value, and returns the value of the parameter. For example, ${arg.2fa.task.data} would resolve to the 2fa.task.data parameter. If the parameter is found, the value is returned.

${authtoken.property.xxx}

A dynamic variable resolver that can take any value as the argument. It resolves to a function provided in place of the xxx value, and returns the result of the function. For example, ${authtoken.property.2FAData} would resolve to the 2FA data.

${cookie.xxx}

A dynamic variable resolver that can take any value as the argument. It looks for the cookie specified in the xxx value, and returns the value of the cookie.

${sessionuser.xxx}

A dynamic variable resolver that looks for the specified sessionuser information and returns the value of it. It looks for the specific value provided in place of the xxx, and returns the value of it.

The valid values for xxx in the above—the sessionuser information that can be checked in this variable resolver—are:

  • ${sessionuser.phone}

    Looks for the user's phone number and returns the value of it.

  • ${sessionuser.firstname}

    Looks for the user's first name and returns the value of it.

  • ${sessionuser.lastname}

    Looks for the user's last name and returns the value of it.

  • ${sessionuser.email}

    Looks for the user's email address and returns the value of it.

  • ${sessionuser.username}

    Looks for the username and returns the value of it.

  • ${sessionuser.name}

    Looks for the user's profile name (same as username) and returns the value of it.

  • ${sessionuser.domain}

    Looks for the user's domain and returns the value of it.

${business.twofa.maxattempts}

Checks the business settings for the platform tenant, as specified by the Site Admin in the 2FA settings, to determine the maximum number of attempts allowed with a single two-factor verification code.

${business.twofa.recurrence.mode}

Checks the business settings for the platform tenant, as specified by the Site Admin in the 2FA settings, to determine the recurrence mode—that is, the frequency with which the user must perform two-factor authentication. There are three options for recurrence mode:

  • twofa.each.login: each time the user logs in.
  • twofa.login.new.device: the first time the user logs in on a different device (includes cookie name).
  • twofa.once.each.interval: after the specified time interval has passed (cookie name and also time interval setting).

${business.twofa.recurrence.interval}

Checks the business settings for the platform tenant, as specified by the Site Admin in the 2FA settings, to determine the recurrence interval. Only applicable when the setting for recurrence mode is twofa.once.each.interval. For example, the interval might be 15 days.

${business.twofa.device.cookie.name}

Checks the business settings for the platform tenant, as specified by the Site Admin in the 2FA settings, to determine the cookie name. Only applicable when the setting for recurrence mode is twofa.once.each.interval or twofa.login.new.device.

${business.twofa.code.validity}

Checks the business settings for the platform tenant, as specified by the Site Admin in the 2FA settings, to determine how long the 2FA verification code is valid for. Code validity period is specified in milliseconds.

User Workflow: Implementing two-factor authentication

The platform supports setting up a second factor, a verification code generated and sent to the user, as an additional security feature on the login process.

There is a specific version of the user workflow that is provided out of the box to help get you started on setting up two-factor authentication for login.

Two-factor authentication is also supported by additional platform settings specified by the Site Admin (Administration > Settings > User 2FA).

Workflow Example Implementing Two-Factor Authentication

The example below shows the first action of a step in a custom User Workflow that implements two-factor authentication for registered users. This workflow:

  • Sets the 2FA delivery options that will be available to users.

    Note: If you want to use the platform default, leave EmailSupported set to true and set the other two options to false.

  • Tests to see whether the authentication cookie already exists. If so, the workflow step ends.
  • Tests to make sure that the custom property 2FAComplete does not exist in the user's auth token; that is, the two-factor authentication process is not complete. If this property does exist, the workflow step ends.
  • Generates the verification code for the second factor of the authentication process.
  • Generates a user notification.
  • Sets "2FA required" as a pending task. The user must enter the code as part of the login process in order for login to be complete.
<action id="451" name="@Login">
  <pre-functions>
    <function type="setTwofaDeliveryOptions">
      <arg name="VoiceSupported">true</arg>
      <arg name="TextSupported">true</arg>
      <arg name="EmailSupported">true</arg>
    </function>
  </pre-functions>
  <results>
    <result old-status="registered" status="registered" step="450">
      <conditions type="AND">
        <condition type="is2FAEnabled"/>
        <condition type="is2FARequired"/>
        <condition type="isDeliveryOptionsOnlyOne">
          <arg name="PropertyName">${arg.2fa.task.data}</arg>
        </condition>
        <condition negate="true" type="isNoDeliveryOption">
          <arg name="PropertyName">${arg.2fa.task.data}</arg>
        </condition>
        <condition type="isDeliveryOptionsOnlyEmail"><!-- out of the box support -->
          <arg name="PropertyName">${arg.2fa.task.data}</arg>
        </condition>
        <condition negate="true" type="authTokenPropertyMatches">
            <arg name="PropertyName">2FAComplete</arg>
            <arg name="PropertyValue">Yes</arg>
        </condition>
        <condition negate="true" type="authTokenPropertyMatches">
            <arg name="PropertyName">2FASkipped</arg>
            <arg name="PropertyValue">Yes</arg>
        </condition>
        <condition negate="true" type="authTokenPropertyExists">
            <arg name="PropertyName">2FAData</arg>
        </condition>
      </conditions>
      <pre-functions>
        <function type="setTwoFADeliveryTarget">
          <arg name="DeliveryOptions">${arg.2fa.task.data}</arg>
          <arg name="DeliveryMechanism">Email</arg>
          <!--<arg name="Index">0</arg>--><!-- Either or -->
        </function>
        <function type="generate2FACode"/>
        <function type="setAuthTokenProperty">
          <arg name="PropertyName">2FAData</arg>
          <arg name="PropertyValue">${arg.2fa.authtoken.property}</arg>
        </function>
        <!-- Implement sendToPhone custom function similar to this function -->
        <function type="send2FACodeToEmail">
          <arg name="DeliveryTargetValue">${arg.twofa.delivery.target.val}</arg>
          <arg name="DeliveryMechanism">${arg.DeliveryMechanism}</arg>
          <arg name="2FAData">${arg.2fa.task.data}</arg>
          <arg name="2FAVerificationCode">${arg.verificationCode}</arg>
          <arg name="2FAVerificationCodeValidMinutes">${arg.2fa.verification.code.valid.minutes}</arg>
        </function>
        <!-- <function type="sendNotification">
          <arg name="notificationType">com.soa.notification.type.user.2fa.verification.code</arg>
          <arg name="role">Self</arg>
        </function> -->
        <function type="setPendingTask">
          <arg name="PendingTask">2fa.required</arg>
          <arg name="TaskData">${arg.2fa.task.data}</arg>
        </function>
      </pre-functions>
    </result>
    <result old-status="registered" status="registered" step="450">
      <conditions type="AND">
        <condition type="is2FAEnabled"/>
        <condition type="is2FARequired"/>
        <condition type="isDeliveryOptionsOnlyOne">
          <arg name="PropertyName">${arg.2fa.task.data}</arg>
        </condition>
        <condition negate="true" type="isNoDeliveryOption">
          <arg name="PropertyName">${arg.2fa.task.data}</arg>
        </condition>
        <condition type="isDeliveryOptionsOnlyText"><!-- out of the box support -->
          <arg name="PropertyName">${arg.2fa.task.data}</arg>
        </condition>
        <condition negate="true" type="authTokenPropertyMatches">
            <arg name="PropertyName">2FAComplete</arg>
            <arg name="PropertyValue">Yes</arg>
        </condition>
        <condition negate="true" type="authTokenPropertyMatches">
            <arg name="PropertyName">2FASkipped</arg>
            <arg name="PropertyValue">Yes</arg>
        </condition>
        <condition negate="true" type="authTokenPropertyExists">
            <arg name="PropertyName">2FAData</arg>
        </condition>
      </conditions>
      <pre-functions>
        <function type="setTwoFADeliveryTarget">
          <arg name="DeliveryOptions">${arg.2fa.task.data}</arg>
          <arg name="DeliveryMechanism">Text</arg>
          <!--<arg name="Index">0</arg>--><!-- Either or -->
        </function>
        <function type="generate2FACode"/>
        <function type="setAuthTokenProperty">
          <arg name="PropertyName">2FAData</arg>
          <arg name="PropertyValue">${arg.2fa.authtoken.property}</arg>
        </function>
        <!-- <function type="send2FACodeToPhone"> custom workflow function to send text
          <arg name="DeliveryTargetValue">${arg.twofa.delivery.target.val}</arg>
          <arg name="DeliveryMechanism">${arg.DeliveryMechanism}</arg>
          <arg name="2FAData">${arg.2fa.task.data}</arg>
          <arg name="2FAVerificationCode">${arg.verificationCode}</arg>
          <arg name="2FAVerificationCodeValidMinutes">${arg.2fa.verification.code.valid.minutes}</arg>
        </function> -->
        <function type="setPendingTask">
          <arg name="PendingTask">2fa.required</arg>
          <arg name="TaskData">${arg.2fa.task.data}</arg>
        </function>
      </pre-functions>
    </result>
    <result old-status="registered" status="registered" step="450">
      <conditions type="AND">
        <condition type="is2FAEnabled"/>
        <condition type="is2FARequired"/>
        <condition type="isDeliveryOptionsOnlyOne">
          <arg name="PropertyName">${arg.2fa.task.data}</arg>
        </condition>
        <condition negate="true" type="isNoDeliveryOption">
          <arg name="PropertyName">${arg.2fa.task.data}</arg>
        </condition>
        <condition type="isDeliveryOptionsOnlyVoice"><!-- out of the box support -->
          <arg name="PropertyName">${arg.2fa.task.data}</arg>
        </condition>
        <condition negate="true" type="authTokenPropertyMatches">
            <arg name="PropertyName">2FAComplete</arg>
            <arg name="PropertyValue">Yes</arg>
        </condition>
        <condition negate="true" type="authTokenPropertyMatches">
            <arg name="PropertyName">2FASkipped</arg>
            <arg name="PropertyValue">Yes</arg>
        </condition>
        <condition negate="true" type="authTokenPropertyExists">
            <arg name="PropertyName">2FAData</arg>
        </condition>
      </conditions>
      <pre-functions>
        <function type="setTwoFADeliveryTarget">
          <arg name="DeliveryOptions">${arg.2fa.task.data}</arg>
          <arg name="DeliveryMechanism">Voice</arg>
          <!--<arg name="Index">0</arg>--><!-- Either or -->
        </function>
        <function type="generate2FACode"/>
        <function type="setAuthTokenProperty">
          <arg name="PropertyName">2FAData</arg>
          <arg name="PropertyValue">${arg.2fa.authtoken.property}</arg>
        </function>
        <!-- <function type="send2FACodeToPhone"> custom workflow function to send voice
          <arg name="DeliveryTargetValue">${arg.twofa.delivery.target.val}</arg>
          <arg name="DeliveryMechanism">${arg.DeliveryMechanism}</arg>
          <arg name="2FAData">${arg.2fa.task.data}</arg>
          <arg name="2FAVerificationCode">${arg.verificationCode}</arg>
          <arg name="2FAVerificationCodeValidMinutes">${arg.2fa.verification.code.valid.minutes}</arg>
        </function> -->
        <function type="setPendingTask">
          <arg name="PendingTask">2fa.required</arg>
          <arg name="TaskData">${arg.2fa.task.data}</arg>
        </function>
      </pre-functions>
    </result>
    <result old-status="registered" status="registered" step="450">
      <conditions type="AND">
        <condition type="is2FAEnabled"/>
        <condition type="is2FARequired"/>
        <condition type="isDeliveryOptionsMultiple">
          <arg name="PropertyName">${arg.2fa.task.data}</arg>
        </condition>
        <condition negate="true" type="authTokenPropertyMatches">
            <arg name="PropertyName">2FAComplete</arg>
            <arg name="PropertyValue">Yes</arg>
        </condition>
        <condition negate="true" type="authTokenPropertyMatches">
            <arg name="PropertyName">2FASkipped</arg>
            <arg name="PropertyValue">Yes</arg>
        </condition>
        <condition negate="true" type="authTokenPropertyExists">
            <arg name="PropertyName">2FAData</arg>
        </condition>
      </conditions>
      <pre-functions>
        <!-- returns delivery options -->
        <function type="setPendingTask">
          <arg name="PendingTask">2fa.required</arg>
          <arg name="TaskData">${arg.2fa.task.data}</arg>
        </function>
      </pre-functions>
    </result>
    <result old-status="registered" status="registered" step="450">
      <conditions type="AND">
        <condition type="is2FAEnabled"/>
        <condition type="is2FARequired"/>
        <condition type="isNoDeliveryOption">
          <arg name="PropertyName">${arg.2fa.task.data}</arg>
        </condition>
        <condition negate="true" type="authTokenPropertyMatches">
            <arg name="PropertyName">2FAComplete</arg>
            <arg name="PropertyValue">Yes</arg>
        </condition>
        <condition negate="true" type="authTokenPropertyMatches">
            <arg name="PropertyName">2FASkipped</arg>
            <arg name="PropertyValue">Yes</arg>
        </condition>
        <condition negate="true" type="authTokenPropertyExists">
            <arg name="PropertyName">2FAData</arg>
        </condition>
      </conditions>
      <pre-functions>
        <function type="generate2FACode"/>
        <function type="setAuthTokenProperty">
          <arg name="PropertyName">2FAData</arg>
          <arg name="PropertyValue">${arg.2fa.authtoken.property}</arg>
        </function>
        <!-- Add a default delivery option here as there are no delivery options defined, this has to be custom -->
        <!-- <function type="send2FACodeToPhone"> custom workflow function
          <arg name="userPhoneNumber">${sessionuser.phone.number}</arg>
          <arg name="2FAData">${arg.2fa.task.data}</arg>
          <arg name="2FAVerificationCode">${arg.verificationCode}</arg>
          <arg name="2FAVerificationCodeValidMinutes">${arg.2fa.verification.code.valid.minutes}</arg>
        </function> -->
        <function type="setPendingTask">
          <arg name="PendingTask">2fa.required</arg>
          <arg name="TaskData">${arg.2fa.task.data}</arg>
        </function>
      </pre-functions>
    </result>
    <result old-status="registered" status="registered" step="450">
      <conditions type="AND">
        <condition type="is2FAEnabled"/>
        <condition negate="true" type="authTokenPropertyMatches">
            <arg name="PropertyName">2FAComplete</arg>
            <arg name="PropertyValue">Yes</arg>
        </condition>
        <condition negate="true" type="authTokenPropertyMatches">
            <arg name="PropertyName">2FASkipped</arg>
            <arg name="PropertyValue">Yes</arg>
        </condition>
        <condition type="authTokenPropertyExists">
            <arg name="PropertyName">2FAData</arg>
        </condition>
      </conditions>
      <pre-functions>
        <function type="unmarshall2FACode"/>
        <function type="setPendingTask">
          <arg name="PendingTask">2fa.required</arg>
          <arg name="TaskData">${arg.2fa.task.data}</arg>
        </function>
      </pre-functions>
    </result>
    <result old-status="registered" status="registered" step="450">
      <conditions type="AND">
        <condition type="isLocalDomainUser"/>
        <condition type="isChangePasswordRequired"/>
      </conditions>
      <pre-functions>
        <function type="setProperty">
          <arg name="PendingTask">&ChangePassword;</arg>
        </function>
      </pre-functions>
    </result>
    <result old-status="registered" status="registered" step="450">
      <conditions type="AND">
        <condition negate="true" type="isChangePasswordRequired"/>
        <condition negate="true" type="agreementsAccepted"/>
      </conditions>
      <pre-functions>
        <function type="setProperty">
          <arg name="PendingTask">&ForceAcceptAgreements;</arg>
        </function>
      </pre-functions>
    </result>
    <result old-status="registered" status="registered" step="450">
      <conditions type="AND">
        <condition type="isLocalDomainUser"/>
                 <condition negate="true" type="isChangePasswordRequired"/>
        <condition type="agreementsAccepted"/>
        <condition negate="true" type="securityQuestionsAnswered"/>
        <condition type="isForceChallengeQuestionsAnsweredOnLoginSetup"/>
      </conditions>
      <pre-functions>
        <function type="setProperty">
          <arg name="PendingTask">&CollectSecurityQuestionAnswers;</arg>
        </function>
      </pre-functions>
    </result>
    <unconditional-result old-status="registered" status="registered" step="450">
      <pre-functions>
        <function type="setProperty">
          <arg name="LoginState">&LoginComplete;</arg>
        </function>
        <!--  invoke send Notification on first time login.  -->
        <function type="markLoginComplete"/>
      </pre-functions>
    </unconditional-result>
  </results>
</action>

User Workflow: Assigning a default role to users of a specific login domain

Valid in Version: 2019.1.34 and later

You can use the addRoleToUser user workflow function to automatically assign a specific role to a user logging in with a specific login domain.

To implement this custom workflow

Note: If you already have a custom workflow in place, follow Step 1, use your own custom workflow in Step 2 below, and add the new custom section shown below in Step 3, updating the values as shown in Step 4, into your custom workflow. Then follow the other steps.

  1. In the Community Manager developer portal, go to More > Admin > Domains and get these two values for the login domain that you want to use:
    • DomainName: The plain text name in the Type column. For example, OpenID Connect Relying Party.
    • DomainType: The back-end value for the type of domain. For an OpenID Connect Relying Party domain, the value is com.soa.securitydomain.openidconnect.relyingparty. To be sure, you can, turn on developer tools and click the Modify button for the domain. In the developer tools you'll see a GET call with the DomainName value in it. In the response, take the IdentitySystemType value.

      Copy both values so that you can use them in the custom workflow.

  2. Download this user workflow: default-user-workflow-v2. See To download a workflow document from the Community Manager developer portal.
  3. Remove the comment tag from lines 53–67 (beginning with <result old-status="none" status="unknown" step="30">).
  4. Update the new section with your DomainName and DomainType values, and change the role if you want to assign a different role.
  5. Rename the workflow (for example, to default-user-workflow-v2-customloginrole.xml).
  6. Upload the modified user workflow. See To upload a custom workflow.
  7. Assign the modified workflow as the default user workflow for users. See How do I specify a custom workflow for platform users?

The example below, a portion of a custom workflow, includes a custom section that implements this functionality. This section of the workflow:

  • Uses the isDomainType and isDomainName conditions to test whether the DomainType and DomainName values match a valid login domain.
  • If the conditions are met, assigns the specified role to the user who is logging in for the first time from that login domain.
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE workflow PUBLIC 
         "-//OpenSymphony Group//DTD OSWorkflow 2.8//EN"
         "http://www.opensymphony.com/osworkflow/workflow_2_8.dtd" [
<!-- "http://www.opensymphony.com/osworkflow/workflow_2_8.dtd" -->

<!ENTITY LoginComplete "login.complete" >
<!ENTITY ChangePassword "change.password" >
<!ENTITY ForceAcceptAgreements "force.accept.agreements" >
<!ENTITY CollectSecurityQuestionAnswers "collect.security.question.answers" >
<!ENTITY CollectRequiredProperties "collect.required.properties" >
<!ENTITY RoleSiteAdmin "SiteAdmin" >
<!ENTITY RoleTwoFADisabled "2FA-exempt users" >
]>



<workflow>
  <initial-actions>
    <action id="1" name="@Signup">
      <restrict-to>
        <conditions type="AND">
          <condition type="isSelfSignupAllowed" />
        </conditions>
      </restrict-to>
      <results>
        <unconditional-result old-status="none" status="init-signup" step="-1"/>
      </results>
    </action>
    <action id="2" name="@Add">
      <results>
        <unconditional-result old-status="none" status="init-admin-add" step="20"/>
      </results>
    </action>
    <action id="3" name="@Invite">
      <restrict-to>
        <conditions type="AND">
          <condition type="isInviteUnRegisteredUserAllowed" />
        </conditions>
      </restrict-to>
      <results>
        <unconditional-result old-status="none" status="unknown" step="-1"/>
      </results>
    </action>
    <action id="4" name="@Setup">
      <results>

        <result old-status="none" status="unknown" step="30">
          <conditions type="AND">
            <condition type="isDomainType">
              <arg name="DomainType">com.soa.securitydomain.openidconnect.relyingparty</arg>  <!– e.g. com.soa.securitydomain.openidconnect.relyingparty –>
            </condition>
            <condition type="isDomainName">
              <arg name="DomainName">Google OIDC RP Domain</arg>  <!– e.g. Google OIDC RP Domain –>
            </condition>
          </conditions>
          <post-functions>
            <function type="addRoleToUser">
              <arg name="role">API Developer</arg>  <!– e.g. BUSINESS ADMINISTRATOR –>
            </function>
          </post-functions>
        </result>
        <unconditional-result old-status="none" status="registered" step="30"/>
      </results>
    </action>
...