Step-up Authentication

This solution document will guide you how to configure Step-up authentication for a SAML flow.

Prereqs

A SSO Authenticator should be configured.

The target authenticators (selectorLOA3, selectorLOA2 and selectorLOA1 in this case) will not be described in this guide.

Each authenticator used has to set the session property authLevel to the correct level (1, 2 or 3). The step-up authenticator will use this property to dispatch to the correct authenticator.

Dispatch to correct authenticator

The incoming request should be dispatched to the correct authenticator, depending on request parameters.

In this case we will dispatch based on "resolvedSPID", which would be the entityID of the SP.

The last expression in this example will match all requests and will be used if none of the previous expressions are matching.


{
  "name": "Dispatch",
  "id": "ssoDispatch",
  "alias": "ssoDispatch",
  "configuration": {
    "idpID": "<The ID of your IdP>",
    "mapping": [
      {
        "authenticator": "ssoDispatchLOA3",
        "description": "LOA3",
        "expression": "request.getParameter('resolvedSPID').equals('LOA3ServiceA')||request.getParameter('resolvedSPID').equals('LOA3ServiceB')"
      },
      {
        "authenticator": "ssoDispatchLOA2",
        "description": "LOA2",
        "expression": "request.getParameter('resolvedSPID').equals('LOA2ServiceX')||request.getParameter('resolvedSPID').equals('LOA2ServiceY')||request.getParameter('resolvedSPID').equals('LOA2ServiceZ')"
      },
      {
        "authenticator": "ssoDispatchLOA1",
        "description": "LOA1",
        "expression": "true"
      }
    ]
  }
}

Dispatch for LOA3 and LOA2

The dispatch for LOA2 and LOA3 will be based on the session property authLevel.
The user will be redirected to the SSO authenticator if the authLevel is larger than or equal to the required authentication level.
The user will be redirected to the selector authenticator for this particular level if the authLevel is less than the required authentication level.

{
		"name": "Dispatch",
		"id": "ssoDispatchLOA3",
		"alias": "ssoDispatchLOA3",
		"configuration": {
			"idpID": "<The ID of your IdP>",
			"mapping": [
				{
					"authenticator": "selectorLOA3",
					"description": "LOA3 Selector",
					"expression": "parseInt(session.properties().getValueOrDefault('authLevel','0')) < 3"
				},
				{
					"authenticator": "SSOAuthenticator",
					"description": "SSO",
					"expression": "parseInt(session.properties().getValueOrDefault('authLevel','0')) >= 3"
				}
			]
		}
	},
  {
		"name": "Dispatch",
		"id": "ssoDispatchLOA2",
		"alias": "ssoDispatchLOA2",
		"configuration": {
			"idpID": "<The ID of your IdP>",
			"mapping": [
				{
					"authenticator": "selectorLOA2",
					"description": "LOA2 Selector",
					"expression": "parseInt(session.properties().getValueOrDefault('authLevel','0')) < 2"
				},
				{
					"authenticator": "SSOAuthenticator",
					"description": "SSO",
					"expression": "parseInt(session.properties().getValueOrDefault('authLevel','0')) >= 2"
				}
			]
		}
	}

All other authentication requests

Request that doesn't match any of the other methods will be dispatched thru the LOA1 authenticator. 

The user will be redirected to the SSO authenticator if the users session already is authenticated.
The user will be redirected to the selector authenticator for this particular level if the users session NOT is authenticated.


{
		"name": "Dispatch",
		"id": "ssoDispatchLOA1",
		"alias": "ssoDispatchLOA1",
		"configuration": {
			"idpID": "<The ID of your IdP>",
			"mapping": [
				{
					"authenticator": "selectorRoll",
					"description": "LOA1 Selector",
					"expression": "!request.getParameter('authenticatedrequest').equals('true')"
				},
				{
					"authenticator": "SSOAuthenticator",
					"description": "SSO",
					"expression": "request.getParameter('authenticatedrequest').equals('true')"
				}
			]
		}
	}

Add authLevel to session

This is an example on how to add the authLevel to the user session. This has to be configured in EACH auth flow that should be used in the solution.

{
  				"name": "SessionLoadValve",
  				"config": {
  					"id": "{{request.session_id}}"
  				}
  			}, {
				"name": "SessionPropertyAddValve",
				"config": {
					"name": "authLevel",
					"value": "2"
				}
			}, {
				"name": "SessionPersistValve",
				"config": {}
			}

Stop authentication in SSO auth, if authLevel not strong enough

This is an example how the SSO auth could be modified in order to block authentication if the method used not is strong enough.

{
		"name": "FlowFailValve",
		"config": {
			"proceed_on_error": "false",
			"message": "Auth not strong enough",
			"skip_if_expr": "parseInt(session.get('authLevel'))>=parseInt(flow.getPropertyValue('requiredLOA'))"
		}
	}