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

  1. User Workflow: Initial Actions (none)
  2. User Workflow: Reserved Actions
  3. User Workflow: Functions
  4. User Workflow: Conditions
  5. User Workflow: Variable Resolvers
  6. User Workflow: Implementing Two-Factor Authentication

User Workflow: Initial Actions

There are currently no initial actions for Akana API Platform workflows relating to users.

Back to top

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 on docs.akana.com).

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 particular 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}] on docs.akana.com) 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>

Back to top

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
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>

Back to top

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>

Back to top

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.

Back to top

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>

Back to top