Skip to content

Hooks

Onegini Hooks enable developers and IT teams to modify flows or quickly integrate other systems with custom code and pages. Hooks enable an organization to customize:

  • creating additional views
  • sending emails
  • generating reset password codes
  • executing actions on the user profile, eg.: -- modify or add user's attributes -- add custom attributes -- change user status

Hook types

There are several flows in which a hook can be launched. Currently, we support:

  • Post Login Hook
  • Username and Password migration Hook
  • Reset Password Hook
  • Pre Link External Idp Hook

Post Login Hook

The Post Login Hook is launched after a user successfully authenticates but before the user is redirected to the target page which can be, e.g. origin URL or service provider page. It supports the following Action Types:

Username and Password Migration Hook

The Username and Password Migration Hook is launched after a user authenticates via the login page and the authentication fails. After the failure extension is informed about the credentials and can decide whether an account should be migrated or not.

It supports the following Action Types:

Reset Password Hook

The Reset Password Hook consists of two hooks: Pre-reset password and Reset password. The first one is executed when a user submits the form on the reset password page and does not exist in the system yet (based on the input data). Based on the input data (email or usernames) extension can decide how to proceed with the reset password flow (e.g., generate code and send an email with the link to reset a password).

The second hook is executed when a user enters a page with a reset password code, so the extension can add additional views and/or perform the UnP migration.

Pre-reset password hook supports the following Action Types:

Reset password hook supports the following Action Types:

The Pre Link External Idp Hook is currently launched in these two linking scenarios: 1. The user is logged in and decides to couple an external identity provider via the dashboard page. 2. In a migration scenario, when a user is already logged in with an external identity provider and then logs in with an existing account.

Pre Link External Idp hook supports the following Action Types:

Developing Hooks

With each Hook, a developer can execute multiple supported actions. Actions of the same type can also be repeated (e.g. it is possible to update a user's profile in multiple actions to achieve the desired state). To execute a Hook, the hook's configuration needs to be defined first. This can be done by implementing an appropriate Processing Service (as a Spring bean) in the Extension:

The table below specifies which interface implementation needs to be provided for the Hook to be enabled:

HookType Processing Service interface
Post Login PostLoginHookProcessingService
Username and Password Migration MigrationHookProcessingService
Reset Password PreResetPasswordHookProcessingService and ResetPasswordHookProcessingService
Pre Link External Idp PreLinkExternalIdpHookProcessingService

Each Processing Service can be constructed using a built-in builder where you can specify how to initialize the hook (usually with a function interface). Additionally, you can specify Action Type specific handlers that can control the Hook flow and contain logic defining how to process specific action.

Each Hook Action handler returns a HookResponse object which consists of the following data:

  • information on which Action Type should be executed next
  • data returned as a result of processing current action. The structure of this object depends on the Action Type.
  • session data of current Hook execution

After an action is chosen, it's being processed by the Onegini IdP.

Post Login Hook implementation example

The following code is an example of the PostLoginHookProcessingService configuration.

PostLoginHookProcessingService.builder()
    .initHandler(PostLoginHookInitHandlerConfiguration.builder()
(1)    .unpHandler(unpPostLoginInitHookHandler -> {
(2)         final Profile profile = unpPostLoginInitHookHandler.getProfile();
(3)         final ExtensionCustomParameters extensionCustomParameters = unpPostLoginInitHookHandler.getExtensionCustomParameters();
            final HookSessionData sessionData = HookSessionData.of("profile", profile);
(4)         final String clientIp = unpPostLoginInitHookHandler.getClientIp();
(5)         final LoginMethodEnum loginMethodType = unpPostLoginInitHookHandler.getLoginMethodType();
            return HookResponse.renderView("personal/hook/post-login-hook/welcome", sessionData);
        })
        .build())
(6) .actions(PostLoginHookActionHandlerConfiguration.builder()
(7)     .renderViewHookActionHandler((renderViewHookActionResultData, sessionData) -> {
(8)         final boolean isWelcomePage = renderViewHookActionResultData.getViewName().equals("personal/hook/post-login-hook/welcome");
            final boolean isUpdateMobilePage = renderViewHookActionResultData.getViewName().equals("personal/hook/post-login-hook/update-mobile");
            final boolean isContinueAction = renderViewHookActionResultData.getActionTriggered().equals("continue");
            final boolean isSkipAction = renderViewHookActionResultData.getActionTriggered().equals("skip");

(9)         if (isSkipAction) {
              return HookResponse.hookSkipped(sessionData);
(10)         } else if (isContinueAction) {
(11)             if (isWelcomePage) {
                  return HookResponse.renderView("personal/hook/post-login-hook/update-mobile");
                }
(12)             if (isUpdateMobilePage) {
                    final String newMobileNumber = (String) renderViewHookActionResultData.getData().getProp("mobile").orElse("+48000000000");
                    final Optional<ProfileImpl> profileFromSession = sessionData.get("profile", ProfileImpl.class);

                    final ProfileImpl attributesToDelete = new ProfileImpl();
                    attributesToDelete.setCustomAttributes(profileFromSession.get().getCustomAttributes());

                    final PhoneNumber phoneNumber = new PhoneNumberImpl(newMobileNumber);
                    final ProfileImpl attributesToUpdate = new ProfileImpl();
                    attributesToUpdate.setPhoneNumbers(List.of(phoneNumber));

                    return HookResponse.updateProfile(attributesToUpdate, attributesToDelete, sessionData);
                }
            }
(13)        return HookResponse.hookSkipped(sessionData);
        })
(14)    .updateProfileHookActionHandler((updateProfileHookActionResultData, sessionData) -> HookResponse.hookCompleted(sessionData))
        .build())
    .build();

The goal of the above implementation is to present two additional views to the user (a welcome page and a page where the user can enter their mobile number) and update the profile based on the new mobile number. Besides that, we remove all the custom attributes from the profile. The result is that the user has a new mobile phone number in its profile and no custom attributes. Thanks to the 'skip action' a user can omit these views, so no changes are applied (it's optional to update the profile).

From a technical point of view, the following is happening:

  1. Each hook starts with an initialization handler. Its responsibility is to handle the incoming request and decide what is next. For example, the Post Login Hook extension needs to use one of the PostLoginInitHookHandler implementations.
  2. For this example we used the UnpPostLoginInitHookHandler implementation. It means that the Hook is executed only for username & password logins. For other types of logins, e.g. SAML, OIDC, it won't be executed. To extend it for other types of identity providers please use another method of PostLoginHookInitHandlerConfiguration.builder().
  3. All the available custom extension parameters are retrieved here when required and take actions based on the provided parameters.
  4. The client's IP address is available. It is the original IP address from the end user's device.
  5. The login method, including action token login type is available and based which required actions can be taken.
  6. Actions that are going to be executed as part of this hook implementation are outlined here. We used two of them - rendered views and update profile.
  7. Action handlers are executed as a result of rendered views, eg. it is executed when the user pressed some button on the custom view.
  8. Set of flags to be used in the next steps to check what next action should be executed.
  9. If the user presses the skip button then the hook is skipped and the user is authenticated. No profile changes are applied.
  10. If the user presses the continue button then go further and check which view has been submitted so we know what data to process and which next action to execute.
  11. If the user presses the continue button on the welcome page then render the next page (update mobile number page) as the welcome page is only informational.
  12. If the user presses the continue button on the update mobile number page then get the data from the view (mobile number) and based on that prepare the next action (update profile).
  13. It'HookActionResultDatas just for safety. I none of the conditions matches then skip the hook.
  14. Action to be executed as a response to the profile update. The extension is informed about the new profile structure. At this point, there are no profile changes yet.
  15. To submit the changes, the extension needs to send the HOOK_COMPLETED action as a response.

UnP Migration Hook implementation example

The following code is an example of the UnpMigrationHookProcessingService configuration.

MigrationHookProcessingService.builder()
(1) .initHandler(MigrationHookInitHandlerConfiguration.builder()
(2)     .unpLoginMigrationHandler(unpMigrationHookRequest -> {
          final ProfileImpl profile = createProfileWithPrimaryEmailAddress(unpMigrationHookRequest.getUsername());
          return HookResponse.builder()
              .actionData(HookActionResponseData.builder()
                  .nextAction(HookAction.VALIDATE_UNP_MIGRATION_DATA)
                  .data(UnpMigrationHookActionData.builder()
                      .username(unpMigrationHookRequest.getUsername())
                      .password(hookPasswordHashingService.getPassword(unpMigrationHookRequest.getPassword()))
                      .profile(profile)
                      .build())
                  .build());
        }.build())
        .build())
(3) .actions(MigrationHookActionHandlerConfiguration.builder()
(4)     .migrateAccountActionHandler((unpMigrationHookActionResultData, hookSessionData) -> {
          if (unpMigrationHookActionResultData.isSuccessful()) {
            return HookResponse.hookCompleted(hookSessionData);
          } else {
        return HookResponse.hookCanceled(hookSessionData,
        HookMessage.builder()
        .messageType(HookMessageType.WARN)
        .messageKey(new MessageKey("unp-migration.cancelled"))
        .build());
          }
        })
        .build())
    .build();

The goal of the above code is to get the username and password from the initialization data (unpMigrationHookRequest) and validate whether the migration data is valid and migration is going to succeed (VALIDATE_UNP_MIGRATION_DATA). If the migration is possible, then migrate the account and if not, then cancel the hook. Hook cancellation ends with showing an error on the login page that credentials are invalid (the standard error is shown when the user enters the wrong credentials). When the HOOK_SKIP action is used then no error is shown on the login page.

From a technical point of view, the following is happening:

  1. Create the hook initialization handler that is executed when the Username and Password (UnP) migration starts.

  2. It has been chosen to implement the UnP migration handler that is executed when the user enters credentials via the login page. The implementation prepares data from the migration (profile, username and hashed password) that is then passed as part of the VALIDATE_UNP_MIGRATION_DATA action. The action's goal is to validate whether migration data is valid and can be used to migrate the account.

  3. The list of action handlers responsible for processing action results processed by the Onegini IdP.

  4. The only action that is defined is the UnP migration action. It is informed whether migration data is valid. In this case, if data is valid then the hook is marked as completed (as a result account is migrated). If migration validation didn't succeed, then the hook is canceled (ends up with an error shown on the login page informing that credentials are wrong).

Pre-reset Password Hook implementation example

The following code is an example of the PreResetPasswordHookProcessingService configuration.

PreResetPasswordHookProcessingService.builder()
    .initHandler(PreResetPasswordHookInitHandlerConfiguration.builder()
(1)      .preResetPasswordHandler(preResetPasswordHookRequest -> {
          final HookSessionData sessionData = new HookSessionData();
          final String userIdentifier = preResetPasswordHookRequest.getUserIdentifier();

          if (shouldProceed(userIdentifier)) {
            final String email = getEmailFromIdentifier(userIdentifier);
            sessionData.put("email", email);
            return HookResponse.generateCode(CodeFormat.UUID, CodeType.RESET_PASSWORD, getCodeValidityTime(userIdentifier), sessionData);
          }

          return HookResponse.hookCompleted(sessionData);
        }).build())
(2) .actions(PreResetPasswordHookActionHandlerConfiguration.builder()
(3)     .generateCodeActionHandler((generateCodeHookActionResultData, sessionData) -> {
          final Optional<String> email = sessionData.get("email", String.class);

          resetPasswordCodesStore.add(generateCodeHookActionResultData.getReferenceId(), email.orElse(null));

          final HookEmailData hookEmailData = HookEmailData.builder()
            .toAddress(email.orElse(null))
            .locale(Locale.ENGLISH)
            .templateName("reset-password-template")
            .subjectMsgKey("resetpassword.subject.msg.key")
            .parameters(Map.of("resetPasswordUrl", createResetPasswordUrl(generateCodeHookActionResultData.getCode()))
            .build());

            return HookResponse.sendEmail(hookEmailData, sessionData);
        })
(4)     .sendEmailHookActionHandler((sendEmailHookActionResultData, sessionData) -> {
          if (sendEmailHookActionResultData.isSuccessful()) {
            log.info("Send email action completed without errors.");
          } else {
            log.warn("Send email action completed with errors: {}.", sendEmailHookActionResultData.getErrorResponse());
          }
          return HookResponse.hookCompleted(sessionData);
        }).build())
    .build();

The goal of the above implementation is to send an email with a link (with generated code) to perform password reset migration.

From a technical point of view, the following is happening: 1. In the initialization handler, the extension checks if it can perform a reset password for provided username/email. If so, it executes GENERATE_CODE action. 2. Specifies actions supported by the hook, which are: generate code and send an email. 3. Handler for generating code action results. Extension receives generated code, together with the referenceId to connect it with the current username/email. 4. Handler for send email action result. Extension receives validation result for SEND_EMAIL action. If there are no errors, it means that the email will be sent when the hook is completed.

Reset Password Hook implementation example

The following code is an example of the ResetPasswordHookProcessingService configuration.

ResetPasswordHookProcessingService.builder()
    .initHandler(ResetPasswordHookInitHandlerConfiguration.builder()
(1)     .resetPasswordHandler(resetPasswordHookRequest -> {
          final HookSessionData hookSessionData = new HookSessionData();

          final String referenceId = hookInitRequest.getReferenceId();
          if (StringUtils.isNotBlank(referenceId) && resetPasswordCodesStore.contains(referenceId)) {
            hookSessionData.put("email", resetPasswordCodesStore.getAndRemove(referenceId));

            return HookResponse.renderView("personal/hook/reset-password/reset-password-welcome", hookSessionData);
          }

          return HookResponse.hookCompleted(hookSessionData);
        }).build())
(2) .actions(ResetPasswordHookActionHandlerConfiguration.builder()
(3)     .renderViewHookActionHandler((renderViewHookActionResultData, sessionData) -> {
          if ("cancel".equals(renderViewHookActionResultData.getActionTriggered())) {
            return HookResponse.hookCanceled(sessionData);
          } else if (isWelcomePage(hookActrenderViewHookActionResultDataionData.getViewName())) {
            return getHookResponseForWelcomePage(sessionData);
          } else if (isMobileNumberPage(renderViewHookActionResultData.getViewName())) {
            return getHookResponseForMobileNumberPage(renderViewHookActionResultData, sessionData);
          } else if (isPasswordPage(renderViewHookActionResultData.getViewName())) {
            return getHookResponseForPasswordPage(renderViewHookActionResultData, sessionData);
          }

          return HookResponse.hookCompleted(sessionData);
        })
(4)     .migrateAccountActionHandler((unpMigrationHookActionResultData, sessionData) -> {
          if (unpMigrationHookActionResultData.getErrorResponse() != null && !unpMigrationHookActionResultData.getErrorResponse().isEmpty()) {
            Map<String, String> errors = getErrors(unpMigrationHookActionResultData);
            return HookResponse.renderView("personal/hook/reset-password/reset-password-provide-password", new HookViewData(), errors, sessionData);
          }

          return HookResponse.hookCompleted(sessionData);
        }).build())
        .build();

The goal of the above implementation is to perform UnP migration (by gathering required attributes on additional pages).

From a technical point of view, the following is happening: 1. In the initialization handler extension receives referenceId previously saved after generating a reset password code. 2. Specifies actions supported by the hook, which are: render view and UnP migration. 3. Extension using render view action gathers all required data needed to perform UnP migration (mobile number, password). 4. If there are no errors, the user is migrated.

The following code is an example of the PreLinkExternalIdpHookProcessingService configuration.x

PreLinkExternalIdpHookProcessingService.builder()
    .initHandler(PreLinkExternalIdpHookInitHandlerConfiguration.builder()
      .initHandler(initHookHandler)
      .build()
    ).actions(PreLinkExternalIdpHookActionHandlerConfiguration.builder()
      .renderViewHookActionHandler(renderViewActionHandler)
      .updateProfileHookActionHandler((actionResultData, sessionData) -> HookResponse.hookCompleted(sessionData))
      .build())
    .build();

PreLinkExternalIdpHookProcessingService builder looks the same as other hook builders.

Action handler

To process actions of a given type, the extension needs to provide a handler that should be specified when constructing a Processing Service. This handler has access to a HookActionResultData. The structure of this object depends on Action Type:

Action Type HookActionResultData implementation
RENDER_VIEW RenderViewHookActionResultData
UPDATE_PROFILE UpdateProfileHookActionResultData
BLOCK_ACCOUNT BlockAccountHookActionResultData
CHANGE_PASSWORD ChangePasswordHookActionResultData
VALIDATE_UNP_MIGRATION_DATA ChangePasswordHookActionResultData
GENERATE_CODE GenerateCodeHookActionResultData
SEND_EMAIL SendEmailHookActionResultData
STEP_UP StepUpHookActionResultData
UPDATE_MOBILE_NUMBER UpdateMobileNumberActionResultData
ADD_RESPONSE_ATTRIBUTES AddResponseAttributesHookActionResultData

The Action Types - HOOK_COMPLETE, HOOK_SKIP and HOOK_CANCEL - do not have a corresponding implementation of HookActionResultData. The Action Types provides information for the Onegini IdP to finish the Hook.

Action Types

Currently, Hooks support the following action types:

Please note that some Hooks might not support all the above types. HOOK_COMPLETE, HOOK_SKIP, and HOOK_CANCEL action types are supported by all Hooks.

RENDER_VIEW

The RENDER_VIEW Action Type can be used to display a custom view. When RENDER_VIEW is being required by the Extension, the following information should be in the request:

  • view name which should be the path to an HTML template (usually placed in resources on the Extension side). The .html suffix does not need to be provided.
  • data needed for correctly displaying and handling the view. This data is specified in the props field. This data is always placed in ModelMap and is accessible by using the viewData key.

When the Onegini IdP processes the view (by displaying it to the user), and the user submits the form, the view data is sent back to the Extension. Based on that data the Extension decides what to do next. It can validate the data and render the same view along with errors that are shown to the user. For example, the extension can render a view where the user needs to enter a mobile number. As the number needs to be in a valid format, the Extension can validate the format and return a list of errors to the Onegini IdP.

Example:

new RenderViewActionHandler() {
  @Override
  public HookResponse handle(final RenderViewHookActionResultData hookActionData, final HookSessionData sessionData) {
    if (hookActionData.getData().getProp("mobile").isEmpty()) {
      return HookResponse.renderView("personal/hook/mobile-number", hookActionData.getData(), Map.of("mobile", "error.mobile.required"), sessionData);
    } else {
      HookResponse.updateProfile(...);
    }
  }
}

UPDATE_PROFILE

The UPDATE_PROFILE Action Type is used to send information to the Onegini IdP about attributes that should be updated or removed from a user's profile. When requesting the UPDATE_PROFILE Action Type, the Extension should send back two profiles - one that specifies which attributes should be updated and a second one that indicates the attributes which should be removed.

After the Onegini IdP processed the action, it will respond with the user's full profile including the requested changes. Please note that:

  • UPDATE_PROFILE action can be executed multiple times in a single Hook, all changes will be applied to the profile
  • changes made by the UPDATE_PROFILE action are not persisted until the whole Hook completes successfully
  • updating and deleting attributes with the UPDATE_PROFILE Action Type follows the same logic as processing responses in UpdateProfileAttributesExtension and PersonCreationPreProcessExtension

Example:

PostLoginHookActionHandlerConfiguration.builder()
    .renderViewHookActionHandler((renderViewActionData, sessionData) -> HookResponse.updateProfile(attributesToUpdate, attributesToDelete, sessionData))
    .updateProfileHookActionHandler((updateProfileActionData, sessionData) -> HookResponse.hookCompleted(sessionData))
    .build();

BLOCK_ACCOUNT

With the BLOCK_ACCOUNT action, it is possible to change the user's current status to BLOCKED. This action can be executed only once during a Hook. The only data that the Extension needs to provide is the reason explaining why the user is being blocked. Similar to the UPDATE_PROFILE action, the status is effectively changed when Hook completes. Blocking a user does not cancel the results of other actions (e.g. in case there was a UPDATE_PROFILE executed during the flow, changes in the profile are persisted).

Example:

PostLoginHookActionHandlerConfiguration.builder()
    .renderViewHookActionHandler((renderViewActionData, sessionData) -> HookResponse.blockAccount("Account has been blocked", sessionData))
    .blockAccountHookActionHandler((blockAccountHookActionResultData, sessionData) -> HookResponse.hookCompleted(sessionData))
    .build();

CHANGE_PASSWORD

This action shows a view to the user where the user's password can be changed. The view allows the user to take two actions: - submit the changed password - cancel the hook

When submitting the view, the hook proceeds with the following hook actions. At the end of the hook, the password is updated (similar to UPDATE_PROFILE and BLOCK_ACCOUNT). When the user clicks on the cancel button, the extension decides what to do next (continue, cancel or skip the hook).

Example:

PostLoginHookActionHandlerConfiguration.builder()
    .renderViewHookActionHandler((renderViewActionData, sessionData) -> HookResponse.changePassword(sessionData))
    .changePasswordActionHandler((changePasswordActionData, sessionData) -> {
      if (ChangePasswordAction.CANCEL == changePasswordActionData.getActionTriggered()) {
        return HookResponse.hookCanceled(sessionData);
      } else {
        return HookResponse.hookCompleted(sessionData);
      }
    }).build();

VALIDATE_UNP_MIGRATION_DATA

The VALIDATE_UNP_MIGRATION_DATA action is supported by migration hooks only. It allows the Extension to verify whether the account to be migrated is valid.

Example:

// Password needs to be hashed firstly as we don't want to send plain text passwords
final HashedPassword hashedPassword = hookPasswordHashingService.getPassword(unpMigrationHookRequest.getPassword())

MigrationHookActionHandlerConfiguration.builder()
    .renderViewHookActionHandler(
        (renderViewHookActionResultData, sessionData) -> HookResponse.builder()
            .actionData(HookActionResponseData.builder()
                .nextAction(HookAction.VALIDATE_UNP_MIGRATION_DATA)
                .data(UnpMigrationHookActionData.builder()
                    .username("username")
                    .password(hashedPassword)
                    .profile(new ProfileImpl())
                    .build())
                .build())
            .sessionData(sessionData)
            .build())
    .migrateAccountActionHandler((unpMigrationHookActionResultData, sessionData) -> {
      if (unpMigrationHookActionResultData.isSuccessful()) {
        return HookResponse.hookCompleted(sessionData);
      } else {
        // user errors to chack what data is missing in the migration data 
        final List<ErrorResponse> errors = unpMigrationHookActionResultData.getErrorResponse();
        return HookResponse.renderView("persona/enter-missing-profile-data");
      }
    })
    .build();

GENERATE_CODE

The GENERATE_CODE action creates a code based on input parameters (type, format, validity time) which the extension can use later to perform additional actions. This action can be executed many times during a Hook and code is generated and stored in the Onegini IdP immediately. Extension receives a code and unique identifier in the response.

Supported code formats:

  • UUID

Supported code types:

  • RESET_PASSWORD

Example:

PreResetPasswordHookActionHandlerConfiguration.builder()
    .renderViewHookActionHandler((renderViewHookActionResultData, sessionData) ->
        return HookResponse.generateCode(CodeFormat.UUID, CodeType.RESET_PASSWORD, getCodeValidityTime(), sessionData);
     })
    .generateCodeActionHandler((generateCodeHookActionResultData, sessionData) -> {
        resetPasswordCodesStore.add(generateCodeHookActionResultData.getReferenceId(), generateCodeHookActionResultData.getCode());
        return HookResponse.sendEmail(createEmail(generateCodeHookActionResultData.getCode()), sessionData);
     }).build());

SEND_EMAIL

The SEND_EMAIL action adds a possibility to send an email from the extension. The Onegini IdP verifies all parameters (e.g., recipient, template, subject) and returns errors in the response if any are present. This action can be executed many times during a Hook, but emails are sent only when the hook is completed without errors.

Example:

ResetPasswordHookActionHandlerConfiguration.builder()
    .generateCodeActionHandler((generateCodeHookActionResultData, sessionData) -> {
        resetPasswordCodesStore.add(generateCodeHookActionResultData.getReferenceId(), generateCodeHookActionResultData.getCode());
        return HookResponse.sendEmail(createEmail(generateCodeHookActionResultData.getCode()), sessionData);
     })
     .sendEmailHookActionHandler((sendEmailHookActionResultData, sessionData) -> {
        if (sendEmailHookActionResultData.isSuccessful()) {
          log.info("Send email action completed without errors.");
        } else {
          log.warn("Send email action completed with errors: {}.",sendEmailHookActionResultData.getErrorResponse());
        }
        return HookResponse.hookCompleted(sessionData);
     }).build());

STEP_UP

The STEP_UP action adds a possibility to change the required Authentication Assurance Level (AAL). The Onegini IdP checks if a user already has requested AAL, and if not, a view is shown to authenticate with the available authentication methods to reach the requested level. If the user does not have any applicable authentication methods, the Onegini IdP will show views to enrol new methods. You can execute this action multiple times in a single Hook implementation. When a lower AAL is requested, then the user already has, the AAL will not be lowered.

Example:

PostLoginHookActionHandlerConfiguration.builder()
    .renderViewHookActionHandler((renderViewHookActionResultData, sessionData) -> {
        return HookResponse.stepUp(3, sessionData);
    })
    .stepUpActionHandler((renderViewHookActionResultData, sessionData) -> {
        if (hookActionData.getReachedAuthenticationAssuranceLevel() >= 3) {
          return HookResponse.hookCompleted(sessionData);
        } else {
          // logic when requested AAL cannot be reached
        }
    })
    .build());

UPDATE_MOBILE_NUMBER

This action shows a view to the user where the user's mobile number can be updated. The view allows the user to take two actions: - submit the form with new (validated) mobile number - cancel the form

This view will also ensure that mobile number validation is executed similarly to the regular update mobile number view. The user can only end this action when the mobile number complies with requirements set in the configuration (e.g. mandatory validation).

When submitting the view, the hook proceeds with the following hook actions. At the end of the hook, the mobile number is updated (similar to UPDATE_PROFILE and BLOCK_ACCOUNT). When the user clicks on the cancel button, the extension should decide what to do next (continue, cancel or skip the hook).

Example:

PostLoginHookActionHandlerConfiguration.builder()
    .renderViewHookActionHandler((renderViewActionData, sessionData) -> HookResponse.updateMobileNumber(sessionData))
    .updateMobileNumberHandler((updateMobileNumberActionData, sessionData) -> {
      if (UpdateMobileNumberAction.CANCEL == updateMobileNumberActionData.getActionTriggered()) {
        return HookResponse.hookCanceled(sessionData);
      } else {
        return HookResponse.hookCompleted(sessionData);
      }
    }).build();

ADD_RESPONSE_ATTRIBUTES

The ADD_RESPONSE_ATTRIBUTES action allows adding attributes to the SAML assertion based on the information provided by the extension.

When multiple SAML assertions are created in the same session, the additional attributes provided in this action will be added to all responses.

When the regular attribute mapping already includes the same attribute key/name for a SAML assertion, the value returned through this action won’t overwrite the default attribute mapping value and will be ignored.

Example:

PostLoginHookInitHandlerConfiguration initHandlerConfiguration = PostLoginHookInitHandlerConfiguration.builder()
    .samlIdpHandler(hookInitRequest -> {
      Map<String, String> attributesToAdd = Map.of(
          "key", hookInitRequest.getExternalIdpAttributes().get("key")
      );
      HookSessionData hookSessionData = new HookSessionData();
      return HookResponse.addResponseAttributes(attributesToAdd, hookSessionData);
    })
    .build();

PostLoginHookActionHandlerConfiguration actionHandlerConfiguration = PostLoginHookActionHandlerConfiguration.builder()
    .addResponseAttributesHandler((addResponseAttributesHookActionResultData, sessionData) ->
        HookResponse.hookCompleted(sessionData)
    )
    .build();

HOOK_SKIP

The HOOK_SKIP action immediately finishes the current Hook execution. The changes to a user’s profile are omitted. And the user will continue with the regular flow. Using this action does not fail the Hook execution but simply allows the Hook to end as if the hook was never executed.

Optionally a HookMessage can be included in the HOOK_SKIP action next to the HookSessionData. See the HookMessage section for more details.

HOOK_CANCEL

The HOOK_CANCEL action works similarly as HOOK_SKIP does, i.e. the Hook is immediately finished, all other actions are not considered and no changes to the profile are committed. The main difference is that the original process, that led to triggering the Hook, is reverted. Therefore, the user will not continue the regular flow.

Optionally a HookMessage can be included in the HOOK_CANCEL action next to the HookSessionData. See the HookMessage section for more details.

HOOK_COMPLETE

The HOOK_COMPLETE is a special Action Type that indicates the end of the Hook. After this action is executed, the Onegini IdP commits the user's profile changes that were requested by other actions. The user will continue with the regular flow when the Hook is completed.

Optionally a HookMessage can be included in the HOOK_COMPLETE action next to the HookSessionData. See the HookMessage section for more details.

Hook message

A HookMessage contains a message key and message type that can be displayed to the user. The Message key and type inside the HookMessage will be added as a Flash Attribute. This means that the attribute is available in the template of the next page that is displayed by the Onegini IdP. Beware, that in some scenarios after a hook ends there are no actual pages displayed to the user, but only redirects. In this particular scenario, the hook message cannot be displayed anywhere.

Example code snippet to create a HookMessage:

    final HookMessage hookMessage = HookMessage.builder()
        .messageType(HookMessageType.WARN)
        .messageKey(new MessageKey("my-message.key")).build();

Managing session

To exchange data within the hook it is possible to use session data managed by the Onegini IdP. Each implementation of the HookActionProcessingService provides access to the HookSessionData object that can store data. It is stored for a single execution of a certain Hook.

Enabling Hooks

When a Hook is defined in the Extension, it is automatically enabled in the Onegini IdP. You can change this behavior by using the following properties:

Hook Property
Post login IDP_EXTENSION_HOOKS_POST_LOGIN_ENABLED
Username and password migration IDP_EXTENSION_HOOKS_UNP_MIGRATION_ENABLED
Reset Password IDP_EXTENSION_HOOKS_RESET_PASSWORD_ENABLED