---
title: Runtime behavior implementation
description: After you specify your plugin's API at least partially, you can start implementing the runtime behavior. Use the specification that you defined previously to implement the runtime functionality.
component: pingfederate
version: 13.1
page_id: pingfederate:sdk_developers_guide:pf_runtime_behavior_implement
canonical_url: https://docs.pingidentity.com/pingfederate/13.1/sdk_developers_guide/pf_runtime_behavior_implement.html
llms_txt: https://docs.pingidentity.com/pingfederate/llms.txt
docs_for_agents: https://developer.pingidentity.com/build-with-ai/docs-for-agents.md
revdate: July 5, 2022
section_ids:
  related-links: Related links
  checking-for-actions: Checking for actions
  extracting-models-from-requests: Extracting models from requests
  performing-additional-validation: Performing additional validation
  handling-invalid-action-ids: Handling invalid action IDs
  handling-authentication-error-exceptions: Handling authentication error exceptions
  sending-api-responses: Sending API responses
  returning-authentication-statuses: Returning authentication statuses
---

# Runtime behavior implementation

After you specify your plugin's API at least partially, you can start implementing the runtime behavior. Use the specification that you defined previously to implement the runtime functionality.

Follow this pattern in `lookupAuthN()`:

1. Check for the possible actions the adapter expects in the current state.

2. If an action is matched, then try to extract the expected model from the request and handle the action.

3. If an action is requested, but it does not match an action allowed for the current state, then return an `INVALID_ACTION_ID` error.

4. If no action is requested, render the response for the current state.

The `AuthnApiSupport` class provides much of the functionality for handling API requests and sending responses. The `TemplateRenderAdapter` stores a reference to this singleton in its `apiSupport` field.

```
private AuthnApiSupport apiSupport = AuthnApiSupport.getDefault();
```

## Related links

* [Developing authentication API-capable adapters and selectors](pf_develop_auth_api_capable_adapt_selec.html)

## Checking for actions

The following code shows the preferred approach for checking for the `submitIdentifiers` action.

The adapter performs this check two ways, depending on whether the current request is from the API endpoint. The `TemplateRenderAdapter` uses a slightly different but equivalent method.

```
/**
 * Determine if the user chose "Submit".
 */
private boolean isSubmitAttributesRequest(HttpServletRequest req)
{
	if (apiSupport.isApiRequest(req))
	{
		return ActionSpec.SUBMIT_USER_ATTRIBUTES.isRequested(req);
	}
	return StringUtils.isNotBlank(req.getParameter("pf.submit"));
}
```

## Extracting models from requests

The next step extracts the model from the request. This step varies depending on whether the request is from the API endpoint. For an API request, call the `AuthnApiSupport.deserializeAsModel()` method. For a non-API request, you must build the model from the parameters in the request.

```
private SubmitUserAttributes getSubmittedAttributes(HttpServletRequest req) throws AuthnErrorException, AuthnAdapterException
{
	if (apiSupport.isApiRequest(req))
	{
		try
		{
			return apiSupport.deserializeAsModel(req, SubmitUserAttributes.class);
		}
		catch (IOException e)
		{
			throw new AuthnAdapterException(e);
		}
	}
	else
	{
		SubmitUserAttributes result = new SubmitUserAttributes();
		result.setUsername(req.getParameter("username"));

		for (String key : extendedAttr)
		{
			result.getUserAttributes().put(key, req.getParameter(key));
		}
		return result;
	}
}
```

The `deserializeAsModel()` method also does some validation on the incoming JSON. This includes checking for fields flagged as `required` in the model, using the @Schema annotation. If a validation error occurs during this step, the method throws an `AuthnErrorException`, which the adapter can convert to an API error response. For more information, see [Handling authentication error exceptions](#_handling_authentication_error_exceptions).

## Performing additional validation

The `deserializeAsModel()` method performs some basic validation on the submitted JSON. Your adapter probably needs to perform more validation and send an `AuthnError` to the API client if it finds any errors. Here is how the `TemplateRenderAdapter` validates the names of the provided user attributes:

```
private void validateSubmittedAttributes(HttpServletRequest req, SubmitUserAttributes submitted) throws AuthnErrorException
{
	if (apiSupport.isApiRequest(req))
	{
		List<AuthnErrorDetail> errorDetails = new ArrayList<>();
		for (String attrName : submitted.getUserAttributes().keySet())
		{
			if (!extendedAttr.contains(attrName))
			{
				errorDetails.add(ErrorDetailSpec.INVALID_ATTRIBUTE_NAME.makeInstanceBuilder()
					.message("Invalid attribute name: " + attrName).build());
			}
		}
		if (!errorDetails.isEmpty())
		{
			AuthnError authnError = CommonErrorSpec.VALIDATION_ERROR.makeInstance();
			authnError.setDetails(errorDetails);
			throw new AuthnErrorException(authnError);
		}
	}
}
```

## Handling invalid action IDs

If a request from an API client includes an action ID that does not match any actions available in the current state, it is best practice to return an error to the client.

After checking all the possible actions, if none match and the request's action ID is not null, the adapter can throw an `AuthnErrorException`. The adapter catches this exception and writes an error to the API response.

```
if (apiSupport.getActionId(req) != null)
{
	// An action ID was provided but it does not match one of those expected in the current state.
	throw new AuthnErrorException(CommonErrorSpec.INVALID_ACTION_ID.makeInstance());
}
```

## Handling authentication error exceptions

If the `deserializeAsModel()` method detects an error while deserializing the model, it throws an `AuthnErrorException`. If the added validation checks in `validateSubmittedAttributes` detect an error, they also throw this exception.

The adapter should catch this exception and send an API error response using the method `AuthnApiSupport.writeErrorResponse()`.

```
try
{
	...
}
catch (AuthnErrorException e)
{
	// A validation error occurred while processing an API request, return an error response to the API client
	apiSupport.writeErrorResponse(req, resp, e.getValidationError());
	authnAdapterResponse.setAuthnStatus(AUTHN_STATUS.IN_PROGRESS);
	return authnAdapterResponse;
}
```

## Sending API responses

`AuthnApiSupport` provides several methods for writing API responses.

The following example shows how the `TemplateRenderAdapter` writes the response for the `USER_ATTRIBUTES_REQUIRED` state.

```
private void renderApiResponse(HttpServletRequest req, HttpServletResponse resp, Map<String, Object> inParameters) throws AuthnAdapterException
{
	UserAttributesRequired model = new UserAttributesRequired();
	model.setAttributeNames(new ArrayList<>(extendedAttr));
	AuthnState<UserAttributesRequired> authnState = apiSupport.makeAuthnState(req, StateSpec.USER_ATTRIBUTES_REQUIRED, model);
	try
	{
		apiSupport.writeAuthnStateResponse(req, resp, authnState);
	}
	catch (IOException e)
	{
		throw new AuthnAdapterException(e);
	}
}
```

The `makeAuthnState()` method takes an `AuthnStateSpec` and an instance of the model for that state and builds an `AuthnState` object. The `AuthnState` object can then be further modified. For example, you could remove an action that is not currently applicable using the `removeAction()` method. Then you write the `AuthnState` object to the response using the `writeAuthnStateResponse()` method.

## Returning authentication statuses

As with non-API requests, when the adapter finishes, it returns `AUTHN_STATUS.SUCCESS` or `AUTHN_STATUS.FAILURE` from `lookupAuthN()`.

If the adapter has not yet finished and has written something to the response, it should return `AUTHN_STATUS.IN_PROGRESS`.
