Access Management 7.3.1

Transactional authorization

Transactional authorization requires a user to authorize every access to a resource. It is part of an AM policy that grants single-use or one-shot access.

For example, a user might approve a financial transaction with a one-time password (OTP) sent to their device, or respond to a push notification to confirm that they have indeed signed on from an unexpected location.

Performing the additional action successfully grants access to the protected resource but only once. Additional attempts to access the resource require the user to perform the configured actions again.

Transactional authorization is implemented as an environment condition type in an authorization policy, and affects the authorization decision.

Transactional authorization is not designed to work with account lockout and does not increment lockout counters. As such, don’t use transactional authorization with authentication mechanisms that are susceptible to brute force attacks, such as simple username/password authentication, or OTP authentication. Instead, configure transactional authorization if you are using a strong authentication mechanism, such as MFA: Push authentication, which is not susceptible to brute force attacks. If you do use transactional authorization with an authentication mechanism, such as OTP authentication, make sure that you manage rate-limiting in some other way.

Understand transactional authorization

The following diagram describes the sequence of events that occur when accessing a resource that is protected by a REST application, and an AM policy containing a transactional environment condition:

Accessing resources with transactional authorization
Figure 1. Accessing resources with transactional authorization

The sequence of events for a transaction authorization is as follows:

  1. An authenticated user attempts to access a resource that is protected by an AM server.

  2. The REST application, a resource server, contacts AM to evaluate the policies that apply.

  3. Because the policy contains a transaction environment condition, AM creates a transaction token in the Core Token Service (CTS) store.

    The initial transaction token state is set to CREATED.

    The transaction token contains information about the policy evaluation, including the:

    • Realm

    • Resource

    • Subject

    • Audit tracking ID

    • Authentication method

    To protect against tampering, AM verifies that these details do not change and match those in the incoming requests for the duration of the transaction.

    The transaction token has a time-to-live (default 180 seconds) defined in the Transaction Authentication Service. If the transaction is not completed within this time, the token is deleted, and the flow must be restarted. Alter the default time-to-live if the transaction includes authentication actions that take more time to complete; for example, using HOTP authentication for an OTP over email.

    The time-to-live can be configured globally, or per-realm. For details, refer to Transaction Authentication Service.

  4. In the JSON response to the policy evaluation request, AM returns the transaction ID—​the unique ID of the newly created transaction token—​in the TransactionConditionAdvice array of the advices object; for example:

    {
        "resource": "https://bank.example.com:443/withdraw?amount=100.00",
        "actions": {},
        "attributes": {},
        "advices": {
            "TransactionConditionAdvice": [
                "7b8bfd4c-60fe-4271-928d-d09b94496f84"
            ]
        },
        "ttl": 0
    }
  5. Because the response to the evaluation request does not grant any actions but contains advices, the REST application extracts the transaction ID and returns it to the authentication service to start the authentication.

    The transaction ID is included in the TransactionConditionAdvice attribute value pair in the composite advice query parameters sent as part of the request for actions.

  6. AM extracts the transaction ID from the composite advice, verifies the corresponding transaction token, and changes the state to IN_PROGRESS.

    If the transaction ID is not in the expected state or does not exist AM returns a 401 Unauthorized error; for example:

    {
        "code": 401,
        "reason": "Unauthorized",
        "message": "Unable to read transaction.",
        "detail": {
            "errorCode": "128"
        }
    }
  7. AM responds with the callbacks necessary to satisfy any environment conditions.

    The advices returned by transaction environment conditions have the lowest precedence when compared to the other condition advices. End users must respond to non-transactional condition advices before they respond to the transactional condition advices.

  8. The REST application renders the callbacks and presents them to the user.

  9. The user completes the required actions.

    For example, the user completes the tree specified in the policy by responding to a push notification on their registered mobile device to confirm a transaction.

    If the user does not complete the required actions, AM returns an HTTP 200 message and the user is redirected to the protected resource. Policy evaluation fails because the transactional authorization process failed.

    To return an HTTP 401 message and redirect the user to the failure URL, configure the org.forgerock.openam.auth.transactionauth.returnErrorOnAuthFailure advanced server property.

  10. The REST application completes the callbacks and returns the result to AM.

  11. AM verifies the transaction token and changes the state to COMPLETED.

  12. With the transaction now complete, AM returns the original token.

    Authentication performed as part of an authorization flow does not behave the same as a standard authentication. The differences are as follows:

    • The user’s original session is not upgraded or altered in any way.

    • Failing the authentication during the authorization flow does not increment account lockout counters.

  13. The REST application requests the policy decision again, including the ID of the completed transaction as a value in the TxId array of the environment object:

    {
        "resources" : ["https://bank.example.com:443/withdraw?amount=100.00"],
        "application" : "iPlanetAMWebAgentService",
        "subject" : {
            "ssoToken" : "AQIC5w....*AJTMQAA*"
        },
        "environment": {
            "TxId": ["7b8bfd4c-60fe-4271-928d-d09b94496f84"]
        }
    }
  14. AM verifies the transaction was authorized and that the transaction token is in the COMPLETED state.

  15. If the transaction was completed successfully, the authorization continues.

    The transaction token is marked for deletion so that it cannot be used to grant more than a single access.

  16. Because the authentication required to complete the transaction was successful, AM returns the result of the policy reevaluation.

    For example, the following response grants the POST and GET actions to the resource https://bank.example.com:443/withdraw?amount=100.00:

    {
        "resource": "https://bank.example.com:443/withdraw?amount=100.00",
        "actions": {
            "POST": true,
            "GET": true
        },
        "attributes": {},
        "advices": {},
        "ttl": 0
    }

    Successful transactional authorization responses set the time-to-live (ttl) value to zero to ensure that the policy decision is not cached and cannot be used more than once.

    ForgeRock agents prior to version 5 do not support a time-to-live value of zero and cannot be used for transactional authorization.

  17. The user is able to access the protected resource once.

    Additional attempts to access a resource protected by a policy containing a transactional environment condition require a new transaction to be completed.

Configure transactional authorization

To use transactional authorization, you must coordinate the configuration of the following elements:

  • An appropriate authentication tree for the transaction.

  • The AM policy or policies that use the tree.

  • Support for transactional authorization in your applications.

The authentication tree

The tree communicates to the user what they are authorizing when they approve the transaction, and gives them the means to approve (or reject) the operation.

For example, if the transaction involves a withdrawal from the user’s bank account, you could configure a multi-factor authentication tree that displays "Confirm $100 withdrawal from Online Bank?" with Yes and No options for a push notification to a registered device, or sends a one-time password to a registered device with a similar message.

Configure the tree in the same realm as the policy.

The policy

A transactional authorization policy specifies the conditions that trigger the required authorization. It also specifies a tree the end user must complete to authorize the transaction. In other respects, it is a normal AM authorization policy.

You configure the policy in the AM admin UI. For example, this completed policy applies to the /withdraw endpoint. Its transactional authorization environment condition specifies that the user must complete the AuthorizeTransaction tree to authorize access to the endpoint:

Policy to approve withdrawls

To add the environment condition, set:

Type

Transaction

Authentication Strategy

Authenticate to Tree

Strategy Specifier

The name of the tree

Your application

A quick way to protect your application is to use ForgeRock software. ForgeRock Identity Gateway (IG), SDKs, and agents all support transactional authorization.

  • When using IG to protect your application, configure it for use with AM.

    Refer to the installation pages in the IG documentation.

    Also refer to the IG documentation for policy enforcement. IG as a PEP transparently manages redirection when AM policy requires transactional authorization.

  • When using a ForgeRock SDK, refer to Perform transactional authorization in the SDKs docs.

  • When using a ForgeRock agent to protect your application, refer to the Java agent page on policy enforcement or the web agent page on policy enforcement.

Demonstrate transactional authorization

After configuring the tree, the policy, and your application, access the protected resource.

The example described here involves a bank withdrawal operation. The following steps put the end user interaction with AM in context:

  1. Barbara Jensen browses her online bank at https://bank.example.com and signs on to access her account:

    Signing on at the online bank
  2. Barbara prepares to withdraw $100 from her account using the online bank application.

    The application as PEP continues to contact AM for policy decisions as Barbara browses through the application.

  3. Barbara confirms her intention to complete the withdrawal, triggering the application to access the /withdraw endpoint.

    When requesting a policy decision for this endpoint, the application gets advices indicating transactional authorization.

    It redirects Barbara to the tree required for this transaction according to the policy. The important step of the tree is the decision to authorize the withdrawal:

    Confirming a withdrawal
  4. Barbara receives the money and a receipt page after the application finishes processing the transaction with AM.

The steps for transactional authorization are identical to those for session upgrade.

Transactional authorization over REST

  1. Obtain a session token from AM for user demo with password Ch4ng31t:

    $ curl \
    --request POST \
    --header "Content-Type: application/json" \
    --header "X-OpenAM-Username: demo" \
    --header "X-OpenAM-Password: Ch4ng31t" \
    --header "Accept-API-Version: resource=2.0, protocol=1.0" \
    'https://openam.example.com:8443/openam/json/realms/root/realms/alpha/authenticate'
    {
        "tokenId":"AQIC5wM…​TU3OQ*",
        "successUrl":"/openam/console",
        "realm":"/alpha"
    }
  2. Request a policy evaluation with the tokenId from the previous step as the subject, and a resource URL that is protected by the policy you edited.

    The authenticated user must have the privileges required to access the policy endpoints.

    For additional details, refer to Authenticate over REST.

    $ curl \
    --cookie "iPlanetDirectoryPro=AQIC5wM2L…​zEAAA.." \
    --request POST \
    --header "Content-Type: application/json" \
    --header "Accept-API-Version: resource=2.0" \
    --data '{
        "resources" : ["https://bank.example.com:443/withdraw?amount=100.00"],
        "subject" : {
            "ssoToken" : "AQIC5w…​NTcy"
        },
        "application": "iPlanetAMWebAgentService"
    }' \
    "https://openam.example.com:8443/openam/json/realms/root/policies/?_action=evaluate"
    {
       "resource": "https://bank.example.com:443/withdraw?amount=100.00",
       "actions": {},
       "attributes": {},
       "advices": {
           "TransactionConditionAdvice": [
               "9dae2c80-fe7a-4a36-b57b-4fb1271b0687"
           ]
       },
       "ttl": 0
    }

    The application is the name of your policy set, which defaults to iPlanetAMWebAgentService.

    AM returns an empty actions element, and a transaction ID in the TransactionConditionAdvice property, because a transactional authorization is required to access the resource.

  3. Initiate authentication, including the transaction ID in the composite advice.

    The policy enforcement point must return the transaction ID in a composite advice query parameter, wrapped in URL-encoded XML. The XML format is as follows:

    <Advices>
        <AttributeValuePair>
            <Attribute name="TransactionConditionAdvice"/>
            <Value>Transaction Id</Value>
        </AttributeValuePair>
    </Advices>

    Use the SSO token of the demo user for this request.

    The following command URL-encodes the XML values. The -G parameter appends them as query string parameters to the URL:

    $ curl -G \
    --cookie "iPlanetDirectoryPro=AQIC5w…​NTcy*" \
    --request POST \
    --header "Content-Type: application/json" \
    --header "Accept-API-Version: resource=2.0, protocol=1.0" \
    --data-urlencode 'authIndexType=composite_advice' \
    --data-urlencode 'authIndexValue=<Advices>
        <AttributeValuePair>
            <Attribute name="TransactionConditionAdvice"/>
            <Value>9dae2c80-fe7a-4a36-b57b-4fb1271b0687</Value>
        </AttributeValuePair>
    </Advices>' \
    'https://openam.example.com:8443/openam/json/realms/root/authenticate'
    Sample response
    {
      "authId": "eyJ0eXAiOi…​WLxJ-1d6ovYKHQ",
      "template": "",
      "stage": "AuthenticatorPush3",
      "header": "Authenticator Push",
      "callbacks": [
        {
          "type": "PollingWaitCallback",
          "output": [
            {
              "name": "waitTime",
              "value": "10000"
            }
          ]
        },
        {
          "type": "ConfirmationCallback",
          "output": [
            {
              "name": "prompt",
              "value": ""
            },
            {
              "name": "messageType",
              "value": 0
            },
            {
              "name": "options",
              "value": [
                "Use Emergency Code"
              ]
            },
            {
              "name": "optionType",
              "value": -1
            },
            {
              "name": "defaultOption",
              "value": 0
            }
          ],
          "input": [
            {
              "name": "IDToken2",
              "value": 100
            }
          ]
        }
      ]
    }

    The mobile device registered to the demo user receives a push notification message to authorize in the ForgeRock Authenticator app.

  4. Ensure that the time specified in the waitTime property in the callbacks has passed, in this case at least 10 seconds, and then complete and return the requested callbacks.

    Also return the value of the authId property and the URL-encoded transaction ID.

    Use the SSO token of the demo user for this request.

    In this example, the required XML parameters have been URL-encoded and added to the URL.

    The curl command is not able to use the --data-urlencode option for query-string parameters when sending a JSON payload.

    $ curl \
    --cookie "iPlanetDirectoryPro=AQIC5w…​NTcy*" \
    --request POST \
    --header "Content-Type: application/json" \
    --header "Accept-API-Version: resource=2.0, protocol=1.0" \
    --data '{
        "authId":"eyJ0eXAiOi…​WLxJ-1d6ovYKHQ",
        "template":"",
        "stage":"AuthenticatorPush3",
        "header":"Authenticator Push",
        "callbacks":[
            {
                "type":"PollingWaitCallback",
                "output":[
                    {
                        "name":"waitTime",
                        "value":"10000"
                    }
                ]
            },
            {
                "type":"ConfirmationCallback",
                "output":[
                    {
                        "name":"prompt",
                        "value":""
                    },
                    {
                        "name":"messageType",
                        "value":0
                    },
                    {
                        "name":"options",
                        "value":[
                            "Use Emergency Code"
                        ]
                    },
                    {
                        "name":"optionType",
                        "value":-1
                    },
                    {
                        "name":"defaultOption",
                        "value":0
                    }
                ],
                "input":[
                    {
                        "name":"IDToken2",
                        "value":100
                    }
                ]
            }
        ]
    }' \
    "https://openam.example.com:8443/openam/json/realms/root/authenticate\
    ?authIndexType=composite_advice\
    &authIndexValue=%3CAdvices%3E%0A\
    %3CAttributeValuePair%3E%0A%3CAttribute%20name%3D\
    %22TransactionConditionAdvice%22%2F%3E%0A\
    %3CValue%3E9dae2c80-fe7a-4a36-b57b-4fb1271b0687\
    %3C%2FValue%3E%0A%3C%2FAttributeValuePair\
    %3E%0A%3C%2FAdvices%3E"
    {
        "tokenId":"AQIC5w…​NTcy*",
        "successUrl":"https://bank.example.com:443/withdraw?amount=100.00",
        "realm":"/"
    }

    If the callbacks are correctly completed and the push was approved in the ForgeRock Authenticator application, AM returns the original tokenId value.

    If the push notification has not yet been responded to in the ForgeRock Authenticator application, AM returns the required callbacks again, as in the previous step. Wait until the amount of time specified in the waitTime element has passed and retry the request until the tokenId returns.

  5. Reevaluate the policy, including the transaction ID as the value of a TxId property in the environment element:

    $ curl \
    --cookie "iPlanetDirectoryPro=AQIC5wM2L…​zEAAA.." \
    --request POST \
    --header "Content-Type: application/json" \
    --header "Accept-API-Version: resource=1.0" \
    --data '{
        "resources" : ["http://www.example.com:8000/index.html"],
        "subject" : {
            "ssoToken" : "AQIC5w…​NTcy"
        },
        "environment": {
            "TxId": ["9dae2c80-fe7a-4a36-b57b-4fb1271b0687"]
        }
    }' \
    "https://openam.example.com:8443/openam/json/policies/?_action=evaluate"
    {
        "resource":"https://bank.example.com:443/withdraw?amount=100.00",
        "actions":{
            "POST":true,
            "GET":true
        },
        "attributes":{
    
        },
        "advices":{
    
        },
        "ttl":0
    }

    Because the authentication required by the transaction succeeded, the second policy evaluation returns the POST and GET actions as defined in the policy.

    Notice that the time-to-live (ttl) value of the policy evaluation result is set to 0. This means the policy must not be cached, as the policy grants access to the resource only once. The policy enforcement point must manage this.

    Performing the policy evaluation with the same subject and resource starts a new transactional authorization flow, requiring a repeat of all the steps to access the protected resource each time.