PingOne Advanced Identity Cloud

Session upgrade with MFA

When an authenticated user must present additional credentials to access sensitive resources, PingOne Advanced Identity Cloud provides session upgrade to perform step-up authentication.

This lets you require credentials only when you really need them. A user signs in quickly to access protected resources like their profile data or their shopping cart. When they are ready to make a purchase, the session upgrade mechanism requires authentication with another factor (MFA).

For single-use step-up authentication, refer to Authorize one-time access with transactional authz instead.

How it works

A client application, an authentication journey, or a policy enforcement point (PEP) initiates session upgrade:

  • A client application triggers session upgrade directly.

    The application redirects the authenticated user to the appropriate journey with the ForceAuth=true query string parameter in the URL.

    The following example reauthenticates the user in the alpha realm with the StrongAuthJourney:

    https://<tenant-env-fqdn>/am/XUI/?realm=alpha&ForceAuth=true&authIndexType=service&authIndexValue=StrongAuthJourney
  • An authentication journey with the Anonymous Session Upgrade node upgrades an anonymous session.

  • PingOne Advanced Identity Cloud advises a PEP to initiate session upgrade.

    In this case, ${ig.abbr}, a web agent, a Java agent, or a custom PEP protects sensitive resources.

    You configure authorization policies to require basic authentication for protected resources and session upgrade for sensitive protected resources.

    Session upgrade proceeds as follows:

    session-upgrade-flow
    1. An authenticated user requests a sensitive resource.

    2. The PEP requests a policy decision from PingOne Advanced Identity Cloud.

    3. PingOne Advanced Identity Cloud returns an authorization decision denying access to the resource with advice to specify a journey for reauthentication.

    4. The PEP redirects to PingOne Advanced Identity Cloud for session upgrade.

    5. The user reauthenticates with the specified journey; for example, with an additional factor.

    6. PingOne Advanced Identity Cloud authenticates the user and returns a new session allowing access to the sensitive resource.

Table 1. Session upgrade outcomes
Session upgrade Session type Outcome

Success

Client-side

Issue a new session.

Server-side

The outcome depends on what led to session upgrade:

ForceAuth=true

Issue a new session even if the existing session met the security requirements.

Anonymous Session Upgrade node

Issue a new session.

PEP used advice

Issue a new session with a copy of existing session properties.

Failure

Any

Retain existing user session.

If session upgrade failed because the login page timed out, redirect the user-agent to the success URL from the last successful authentication.

PEP-based session upgrade

Perform the following tasks to deploy session upgrade with a PEP:

  1. Set up a PEP to enforce PingOne Advanced Identity Cloud authorization policies.

    Further reading:

  2. Set up authorization policies.

    The following example policy grants any authenticated user access to *://*:*/sample* resources:

    Any authenticated user can access sample resources
  3. Set up an authentication journey to use for session upgrade.

    For example, configure a journey using a strong authentication mechanism such as MFA.

  4. Set up additional authorization policies with conditions to trigger session upgrade.

    The following example policy requires reauthentication with the StrongAuthJourney to access *://*:*/sensitive* resources:

    Access to sensitive resources requires session upgrade

    Authentication journey names are case-sensitive.

Working with advice

When you use ${ig.abbr}, a Java agent, or a web agent as a PEP, the PEP performs session upgrade when necessary.

If you create your own PEP, it must work with policy advice using the REST API. You can try the following demonstration without installing a PEP or an application to protect:

  1. Prepare by configuring the policies and journey described in PEP-based session upgrade.

    For the StrongAuthJourney, duplicate the default journey to keep authentication simple to demonstrate.

  2. Create a user profile and record the username and password.

  3. Grant the user access to evaluate policies.

  4. Authenticate as the user you created:

    $ curl \
    --request POST \
    --header 'Content-Type: application/json' \
    --header 'X-OpenAM-Username: <username>' \
    --header 'X-OpenAM-Password: <password>' \
    --header 'Accept-API-Version: resource=2.0, protocol=1.0' \
    'https://<tenant-env-fqdn>/am/json/realms/root/realms/alpha/authenticate'
    {"tokenId":"<tokenId>","successUrl":"/enduser/?realm=/alpha","realm":"/alpha"}
  5. Request a policy decision for an authorized resource, https://www.example.com/sample:

    $ curl \
    --request POST \
    --cookie '<session-cookie-name>=<tokenId>' \
    --header 'Content-Type: application/json' \
    --header 'Accept-API-Version: resource=2.0, protocol=1.0' \
    --data '{
      "resources": ["https://www.example.com/sample"],
      "application": "myPolicySet",
      "subject": {
        "ssoToken": "<tokenId>"
      }
    }' \
    'https://<tenant-env-fqdn>/am/json/realms/root/realms/alpha/policies?_action=evaluate'
    [{
      "resource": "https://www.example.com/sample",
      "actions": {
        "POST": true,
        "GET": true
      },
      "attributes": {},
      "advices": {},
      "ttl": <timestamp>
    }]

    GET and POST actions are allowed.

  6. Request a policy decision for a resource requiring session upgrade, https://www.example.com/sensitive:

    $ curl \
    --request POST \
    --cookie '<session-cookie-name>=<tokenId>' \
    --header 'Content-Type: application/json' \
    --header 'Accept-API-Version: resource=2.0, protocol=1.0' \
    --data '{
      "resources": ["https://www.example.com/sensitive"],
      "application": "myPolicySet",
      "subject": {
        "ssoToken": "<tokenId>"
      }
    }' \
    'https://<tenant-env-fqdn>/am/json/realms/root/realms/alpha/policies?_action=evaluate'
    [{
      "resource": "https://www.example.com/sensitive",
      "actions": {},
      "attributes": {},
      "advices": {
        "AuthenticateToServiceConditionAdvice": ["/alpha:StrongAuthJourney"]
      },
      "ttl": <timestamp>
    }]

    According to the policy, no actions are allowed. PingOne Advanced Identity Cloud returns advice instead.

  7. Format the advice as XML without spaces or line breaks.

    The following formatted example shows the structure of the XML:

    <Advices>
      <AttributeValuePair>
        <Attribute name="AuthenticateToServiceConditionAdvice"/>
        <Value>/alpha:StrongAuthJourney</Value>
      </AttributeValuePair>
    </Advices>

    Advice can include multiple attribute-value pairs.

  8. Remember to remove any spaces and blanks around tags and URL-encode the XML advice:

    %3CAdvices%3E%3CAttributeValuePair%3E%3CAttribute%20name%3D%22AuthenticateToServiceConditionAdvice%22%2F%3E%3CValue%3E%2Falpha%3AStrongAuthJourney%3C%2FValue%3E%3C%2FAttributeValuePair%3E%3C%2FAdvices%3E

  9. Use the advice to authenticate again with the existing session:

    In the authentication request set the following:

    • authIndexType=composite_advice

    • authIndexValue=URL-encoded-XML

    • The user’s current SSO token as the cookie

    $ curl \
    --request POST \
    --header 'Content-Type: application/json' \
    --cookie '<session-cookie-name>=<tokenId>' \
    --header 'Accept-API-Version: resource=2.1, protocol=1.0' \
    'https://<tenant-env-fqdn>/am/json/realms/root/realms/alpha/authenticate?authIndexType=composite_advice&authIndexValue=<URL-encoded-XML>'
    {
      "authId": "<auth-id>",
      "callbacks": [{
        "type": "NameCallback",
        "output": [{
          "name": "prompt",
          "value": "User Name"
        }],
        "input": [{
          "name": "IDToken1",
          "value": ""
        }],
        "_id": 0
      }, {
        "type": "PasswordCallback",
        "output": [{
          "name": "prompt",
          "value": "Password"
        }],
        "input": [{
          "name": "IDToken2",
          "value": ""
        }],
        "_id": 1
      }],
      "header": "Sign In",
      "description": "New here? <a href=\"#/service/Registration\">Create an account</a><br><a href=\"#/service/ForgottenUsername\">Forgot username?</a><a href=\"#/service/ResetPassword\"> Forgot password?</a>"
    }

    PingOne Advanced Identity Cloud returns an authentication callback as the response body. For details, refer to Authenticate using REST.

  10. Renew the request with the same parameters and cookie as before, responding to the callback with the details in the request body:

    $ curl \
    --request POST \
    --header 'Content-Type: application/json' \
    --cookie '<session-cookie-name>=<tokenId>' \
    --header 'Accept-API-Version: resource=2.1, protocol=1.0' \
    --data '{
      "authId": "<auth-id>",
      "callbacks": [{
        "type": "NameCallback",
        "output": [{
          "name": "prompt",
          "value": "User Name"
        }],
        "input": [{
          "name": "IDToken1",
          "value": "<username>"
        }],
        "_id": 0
      }, {
        "type": "PasswordCallback",
        "output": [{
          "name": "prompt",
          "value": "Password"
        }],
        "input": [{
          "name": "IDToken2",
          "value": "<password>"
        }],
        "_id": 1
      }]
    }' \
    'https://<tenant-env-fqdn>/am/json/realms/root/realms/alpha/authenticate?authIndexType=composite_advice&authIndexValue=<URL-encoded-XML>'
    {"tokenId":"<new-tokenId>","successUrl":"/enduser/?realm=/alpha","realm":"/alpha"}

    PingOne Advanced Identity Cloud returns a new SSO token on successful authentication. This represents the upgraded session.

  11. Request a policy decision for the resource again using the new SSO token:

    $ curl \
    --request POST \
    --cookie '<session-cookie-name>=<new-tokenId>' \
    --header 'Content-Type: application/json' \
    --header 'Accept-API-Version: resource=2.0, protocol=1.0' \
    --data '{
      "resources": ["https://www.example.com/sensitive"],
      "application": "myPolicySet",
      "subject": {
        "ssoToken": "<new-tokenId>"
      }
    }' \
    'https://<tenant-env-fqdn>/am/json/realms/root/realms/alpha/policies?_action=evaluate'
    [{
      "resource": "https://www.example.com/sensitive",
      "actions": {
        "POST": true,
        "GET": true
      },
      "attributes": {},
      "advices": {},
      "ttl": <timestamp>
    }]

    Notice that the user now has access.