Proof of transaction approval

Airlock IAM's Transaction Approval module lets a delegating system, such as an e-banking application, securely verify transactions with end-users. For more information, see Transaction Approval module.

Sometimes a third party, such as an auditor or court, needs a confirmation that the end-user really approved the transaction. In this context, only Airlock IAM is considered as trustworthy, not the e-banking system.

This article describes how to configure such a proof of transaction approval. This is how it works: After a succesful transaction approval, Airlock IAM not only returns an OK/NOK status, but also a signed JWT that includes all the transaction data relevant for the approval, such as user ID, account number und transaction amount and currency. Because the JWT is cryptographically signed by Airlock IAM, third parties can independently verify its authenticity and integrity.

REST API flow

As the Transaction Approval module only exposes a REST API (and does not provide a UI), the article also contains examples of the relevant REST requests as well as the final REST response including the JWT token payload.

Initial thoughts

  • The signed JWT should only contain values that have been displayed to the end-user in the transaction approval message. These values are specified in the Message Provider plugin relevant to the respective 2FA method.
  • To ensure that the values in the JWT are the ones approved by the end-user, verify them against the configuration of the transaction approval flow valid at the time of the transaction:
    • Obtain the IAM configuration active at that specific point in time.
    • Check the settings of the Message Provider plugin relevant to the 2FA method used by the end-user in the approval flow.
  • For more information on the configuration of the Message Provider plugin, see Message provider configuration.

Transaction Approval flow with approval proof

To implement proof of transaction approval, you must add a No Operation Step at the end of your Transaction Approval flow in the Config Editor. The No Operation Step step can be used to add custom attributes to the REST response it already returns. In our case, the step is used to provide a signed JWT token with transaction details from the current transaction flow.

Together with the No Operation Step, the Transaction Approval flow will include the following steps:

  • The User Identification Step identifies the end-user that must approve the transaction. Currently, only the username attribute can be used to identify the end-user.
  • The Transaction Approval Parameter Step defines the parameters that the e-banking application sends to IAM to verify (e.g., account number, transaction amount). These parameters can also be used by the Message Provider plugin for the approval message sent to the end-user. For more information, see Parameter step and message providers.
  • The Selection Step selects the 2FA method to be used for approval (based on the end-user's login details). Possible 2FA methods are Airlock 2FA, Cronto, mTAN, or email one-time-password (OTP).
  • The No Operation Step returns the signed JWT with the details of the current transaction.

The following diagram represents a Transaction Approval flow that includes the No Operation Step. It uses Airlock 2FA as the second factor authentication for the transaction approval.

 
Notice

It is possible to compile an approval message dynamically, so that the text varies depending on the value of a parameter (for example, paymentType). In such cases, the content of the JWT must also vary. To implement this, add a Selection Step to your flow with multiple No Operation Steps, where each step generates a different JWT corresponding to the message variant.

Configuring the proof of approval

Follow the next steps to add a No Operation Step to your Transaction Approval flow that returns a signed JWT token with details from the current transaction flow.

In this use case, the JWT token will contain the username as well as the transaction account number, amount and currency. However, it is up to you which transaction details to include in the token, as long as the details have been displayed to the end-user in the corresponding transaction approval message (see also Initial thoughts).

Further below, you'll find a diagram that visually represents the steps.

In the Config Editor:

  1. Go to
    Transaction Approval >> Flows >> <your Transaction Approval Flow>
  2. In the Steps list, create a No Operation Step plugin as the last step of the flow. Edit the plugin as follows:
  3. Advanced Settings section, Custom Response Attributes property: Create a Value Provider Map plugin, as follows:
  4. Value Provider Map plugin configuration

    1. Value Providers property: Add a new row with the following details:
      - Key field: Enter a suitable key, such as “jwt”
      - Plugin field: Create a Ticket String Provider plugin. This plugin provides the JWT token as a string. It defines the signature details and content of the JWT token (which is basically a list of key-value pairs). Edit the plugin as follows:
    2. Ticket String Provider plugin configuration

      • Value Providers list: Add the two plugins User Identity Map and Transaction Approval Parameters Map. These plugins provide the values for the JWT token's key-value pairs.
      • The Key-Value Pairs list defines the key-value pairs that build the encoded content of the JWT token. Add four Ticket Key Value plugins to the list. Per plugin, specify the following properties (see also the table below):
        - The Ticket Key property: Defines the key under which the key-value pair is added to the JWT token.
        - The Value Selector property: Selects an entry from the previously defined value providers to obtain the value for this key-value pair.
        • Ensure that the values you enter here for the transaction details equal the names of the corresponding parameters defined in the Transaction Approval Parameter Step of your transaction flow. See also Transaction Approval flow with approval proof above.
      • Ticket Key Value plugins configuration

      • Plugin properties

        Ticket Key

        Value Selector

        username
        Keep this key in mind. You'll need it later on.

        user-id

        accountNumber

        See the name of the corresponding parameter in the Message Parameters list of the Transaction Approval Parameter Step

        amount

        See the name of the corresponding parameter in the Message Parameters list of the Transaction Approval Parameter Step

        currency

        See the name of the corresponding parameter in the Message Parameters list of the Transaction Approval Parameter Step

      • Ticket Encoder field: Add a JWT Ticket Encoder plugin. This plugin defines the signer and encoder settings of the JWT token. Edit the plugin as follows:
      • JWT Ticket Encoder plugin configuration

        • Claim Settings section, Username Ticket Key field: Defines the key used to find the username for the JWT token. The username will be written to the subject field sub of the JWT. Enter the key you previously defined for the “Username” key-value pair in the Ticket Key Value plugin, namely username.
        • Security Settings section, Signer field: Defines the settings used for signing the JWT. Create a JWT Ticket RSA Signer Settings plugin and edit it as shown in the following table:
        • JWT Ticket RSA Signer Settings plugin configuration

        • Field

          Description

          Value

          Keystore Path

          The keystore that holds the private key for JWT token signing.

          By default, the path to the keystore is instances/<instance name>/server.p12

          Keystore Password

          The password of the keystore.

          <your keystore password>

          Private Key Alias

          Alias for the private key in the keystore used for signing the JWT token. You can leave this field empty if your keystore only contains one private key.

          <your private key alias>

          Private Key Password

          Password of the private key in the keystore.

          <your private key password>

  5. Click Activate to activate your configuration.
  6. You have now configured a No Operation Step at the end of your Transaction Approval flow. This step returns a signed JWT token with details from the current transaction flow. The token can be used as proof of approval.

The diagram below visually represents the steps described above. It shows all required plugins, along with display names and property values, in the order they must be created (from left to right).

REST API requests and final response

The above configuration will prompt the REST API to add a signed JWT token with the transaction details after each succesful transaction approval.

This section lists the REST requests relevant for the entire transaction flow as depicted in the above flow diagram, resulting in the final response with the signed JWT token. The decoded JWT is also shown. Airlock 2FA is used as the second factor authentication for the transaction approval.

REST requests and final response

  1. Select the default transaction approval flow:
  2.  
    Example
    POST /transaction-approval/flows/default/select/
  3. Identify the respective end-user:
  4.  
    Example
    POST /transaction-approval/user/identify/ 
    { 
        "username": "itester"
    }
  5. Provide the transaction parameters to be approved by the end-user:
  6.  
    Example
    POST /transaction-approval/parameters/ 
    { 
         "messageParameters": { 
           "accountNumber": "12345", 
           "amount": "300", 
           "currency": "CHF" 
         } 
    }
  7. Check the status of the transaction approval (here with Airlock 2FA as second factor):
  8.  
    Example
    POST /transaction-approval/airlock-2fa/status/poll/
  9. When the end-user succesfully approved the transaction, IAM will return the following response, including the encoded JWT token:
  10.  
    Example
    { 
        "meta": { 
            "type": "jsonapi.metadata.document", 
            "timestamp": "2025-04-17T10:22:38.182+02:00" 
        }, 
        "data": { 
            "type": "transaction-approval.session", 
            "id": "592545411708722659", 
            "attributes": { 
                "customAttributes": { 
                    "jwt": "eyJraWQiOiI0MWNjMDhmZDYxNzc2MGZhZTc3ZTk1NTdhYTAzMDE4ZTJjOGIwMzM5IiwidHlwIjoiSldUIiwiYWxnIjoiUlM1MTIifQ.eyJzdWIiOiJpdGVzdGVyIiwiYW1vdW50IjoiMzAwIiwibmJmIjoxNzQ0ODc4MTUzLCJjdXJyZW5jeSI6IkNIRiIsImV4cCI6MTc0NDg3ODc1OCwiYWNjb3VudE51bWJlciI6IjEyMzQ1IiwiaWF0IjoxNzQ0ODc4MTU4LCJqdGkiOiIyMWU1MDM2Yi0zZjMyLTQxNjAtYjhkZS03ZjIyMThjZjE1Y2EifQ.cbM6bKsI_t-iPQao9OZsJq7pO6RJy2WKpRLp5vsmJQ-2WvBEiQlwan1vSN-h_VwKkWQO6oHELYStbFJhry4J4pUNzonhyUukQLiERysc72gqO9hwHruV9lvhP927TiBHmtNO3cjQInZy84leAgRy1kcD8nl5JJyd6OOIWikPZwYVTgFcLgu4uPv5ZDjWMXAGmhPF-3vlIK5yxHKy9gI0u_4j5NVL_IQuhyMjbeWQ4kH2D3SQ4Na3D3AiVWzhEKYydISvVLvM_VkcwbWnrtukLjL7YYiprxGav4yZQOOSOTjM0Zb-uxThya9utjMiwnXIKQaW7Q0ccBZcQUDLsgMG6fh_J0Ai6LCFF8i7hN94_Q-WPIw0WcQtI-mRiWtUDlq1tgg7YakubRmp-jKolAmlt1d8TtU8eJ1e1HCmnLpnYp-KzjBVxbuA8bTCgGcIDhu0yRL5JJNsWu6y6uNiZQwslzQoLPXQSSALobW5LFeS-stj2VGVL-kbW0QGBjefiDZnO4UeMOdmdmTSzEysf-1t7Y9SOM2AIrsYxOD3u6NcpTEUqim5HDX60rmXwCu1H2rlU-dNxMMVZddHIriNCGuBbZca1668qZ-TynBjCVr5pYK1l_uihCRzXz_AjF119qK9R0zGLzm0KUs15FKthobG2AzklpsHtfM84TaOFZR6LC8" 
                } 
            } 
        } 
    }
  11. The decoded JWT has the following content:

  12.  
    Example
    { 
      "sub": "itester", 
      "amount": "300", 
      "nbf": 1744878153, 
      "currency": "CHF", 
      "exp": 1744878758, 
      "accountNumber": "12345", 
      "iat": 1744878158, 
      "jti": "21e5036b-3f32-4160-b8de-7f2218cf15ca" 
    }