External MFA

To enhance the security of user interactions in online banking, it is often necessary to implement Multi-Factor Authentication (MFA) mechanisms. While Q2 provides several default options for multi-factor authentication, we also provide the ability to create an external MFA extension that can be seamlessly integrated into login, transaction authorization, and event-driven validation (Patrol).

Let’s take a look the flow of an External MFA extension.

../../_images/external_mfa_flow.png
  1. The extension is loaded and your logic handles verification. When completed/successful, the front-end of your extension calls the back-end of your extension via requestExtensionData to generate and provide an External MFA token.

  2. The back-end of your extension generates a token and sets it with HQ.

  3. HQ stores the token in session and responds back to the back-end of your extension.

  4. If successful, your extension responds back to your front-end with the generated token.

  5. The front-end of your extension passes the token to the UUX platform via the Tecton verifyMFA action.

  6. UUX takes the token and passes it back to HQ. This token is passed back to HQ in different ways depending on the UUX workflow MFA is being initiated on.

  7. HQ verifies the token passed against the one set by your extension and will respond back to UUX to complete MFA.

  8. Once UUX has completed the MFA workflow, it will respond back to your verifyMFA call with a success or failure response.

Integration Options

Standard Integration

Our standard integration allows you to create a custom MFA experience within the online banking platform using a server-side or client-side rendered Tecton extension. This can be as simple as a custom form or as complex as a full multi-step authentication process.

Note

For an walkthrough of creating a Standard External MFA extension, see our External MFA tutorial.

Redirect-Based Authentication

Redirection-based authentication allows your extension to securely redirect out for user authentication and redirect back into your extension for final handling. This method is commonly used with OAuth 2.0 and OpenID Connect providers using PAR (Pushed Authorization Requests), PKCE (Proof Key for Code Exchange), or other secure methods.

At its simplest, redirection-based authentication works like this:

  1. When a user is prompted for MFA, we redirect them to an authorization endpoint:

  2. The identity provider authenticates the user.

  3. After successful authentication, the identity provider redirects back to your provided redirect_uri with an authorization code.

  4. The generated verify.html page in your extension captures the authorization code and sends it to your application server:

    tecton.connected.then(function () {
        const params = new URLSearchParams(window.location.search);
        tecton.sources.requestExtensionData({
            route: 'token_handler',
            body: {
                code: params.get('code')
            }
        })...
    
  5. Your application server exchanges this code for access and identity tokens:

    async def token_handler(self):
        verify_code = self.request_body.get("code")
    
        payload = {
            "grant_type": "authorization_code",
            "code": verify_code,
            "redirect_uri": f"{os.path.join(self.base_assets_url, 'verify.html')}",
            "client_id": client_id,
            "client_secret": client_secret,
        }
        response = await q2_requests.post("https://idp.example.com/access_token", data=payload)
    
  1. After validating the response, your extension sets a generated token with HQ and responds back to the front-end of your extension.

    async def token_handler(self):
        ...
    
        # If successful validation
        token = uuid.uuid4().hex
        return await self.set_external_mfa_token(token)
    
  2. The verify.html page passes the token to the UUX platform via the Tecton verifyMFA action to complete the workflow.

Creating a Redirection-Based Authentication Extension

$ q2 create_extension
New Extension Name: MyExternalMFA
What type of extension are you creating?

    1) Online (default) <---------
    2) SSO (Third Party Integration)
    3) Ardent (API)
    4) Q2Console (Backoffice)
    5) Central (Legacy Backoffice)
    6) Adapter
    7) Audit Action
    8) Custom Health Check
    9) Message Bus
    10) Caliper API Custom Endpoint
    11) Base Extension

Please make a selection and press Return [1]: 1

Select type of Online extension to generate

    1) Server Side Rendered (Default) <---------
    2) Client Side Rendered

Please make a selection and press Return: 1

Select type of Online integration to generate

    1) Authenticated (Default)
    2) Unauthenticated
    3) External MFA <---------

Please make a selection and press Return [1]: 3

Select type of MFA integration to generate

    1) Standard (Default)
    2) Redirect-Based Authentication  <---------

Please make a selection and press Return [1]: 2

This will generate a new extension with the necessary files and structure to implement redirection-based authentication.

Pre-authentication Access

One important thing to note for External MFA extensions is that when rendered in the authentication flow, the session is not yet fully authenticated. This means that WedgeOnlineBanking calls are not available to your extension and you will instead need to rely on Q2API. DBObjects can conditionally switch between WedgeOnlineBanking and Q2API based on the authentication state, but in the event you need to determine whether to use WedgeOnlineBanking or Q2API, you can check the is_authenticated attribute of your extension.

from q2_sdk.hq.hq_api.wedge_online_banking import AcceptDisclaimer
from q2_sdk.hq.hq_api.q2_api import DisclaimerFormMarkAsAccepted

async def disclaimer_example_method(self):
    if self.is_authenticated:
        # Use WedgeOnlineBanking
        params_obj =  AcceptDisclaimer.ParamsObj(
            self.logger,
            hq_credentials=self.hq_credentials,
            disclaimer_data_id=1234
        )

        response = await AcceptDisclaimer.execute(params_obj)
    else:
        # Use Q2API
        params_obj =  DisclaimerFormMarkAsAccepted.ParamsObj(
            self.logger,
            user_id=22
            disclaimer_form_data_id=1234
        )

        response = await AcceptDisclaimer.execute(params_obj)

    return response

For more information on the difference between WedgeOnlineBanking and Q2API, please visit our HQ API documentation.

Contextual MFA Information

When creating an External MFA extension, you may have a need for contextual information about the MFA request. This information is available on the self.mfa_info object in your extension.

The self.mfa_info object can contain the following attributes depending on the workflow in which MFA is being initiated:

  • mfa_context - The workflow context in which MFA is being initiated.

  • transaction_ids - A list of transaction IDs associated with the MFA request. Available when the MFA context is transaction authentication.

  • audit_action_id - The ID of the audit action associated with the MFA request. Available when the MFA context is event-driven validation.

  • audit_action_name - The name of the audit action associated with the MFA request. Available when the MFA context is event-driven validation.

  • registration_value - Current user registration value for the MFA provider. Present if registration value is required and present for the user in the Q2_MFARegistration table.

  • sso_identifier - Present if user has an SSOIdentifier in the Q2_SSOUserLogon table.