PingFederate Server

Specification of the plugin API

The first step in adding API support to your plugin is to implement the AuthnApiPlugin interface.

The AuthnApiPlugin interface has two methods: getApiSpec() and getApiPluginDescriptor(). You only need to implement the getApiSpec() method. The API specification this method returns defines the states, models, and actions that your plugin exposes in the API.

The API specification is defined by the *Spec classes in the SDK. These include AuthnStateSpec, AuthnActionSpec, AuthnErrorSpec, and AuthnErrorDetailSpec. The information in these classes lets the PingFederate authentication API Explorer provide documentation for API client developers. That documentation describes your plugin’s API and lets developers experiment with it.

You can access the API Explorer at https://PingFederate_host:9031/pf-ws/authn/explorer. To enable the API Explorer, go to the Authentication API Applications window and select the Enable API Explorer check box. An easy way to use the API Explorer is to create an authentication API application in PingFederate and set the URL for the application to the API Explorer’s URL.

When defining models for states and actions, use the @Schema annotation to describe each field in the model and show whether the field is required.

The rest of this document primarily uses the TemplateRenderAdapter as an example. The source for this adapter is in the PingFederate installation package’s sdk/plugin-src/template-render-adapter-example directory. This adapter is simple. It just prompts the user to enter their username and provide a set of string attributes. The administrator defines the list of attributes by extending the adapter contract. The attribute values are passed back in the SubmitUserAttributes model as a map. Representing field values using a map in the model is unusual. Usually a separate field in the model defines each allowed field, which provides better type safety in the code.

State model example

The following is an example of a state model used by the TemplateRenderAdapter.

/
 * This is the model for the USER_ATTRIBUTES_REQUIRED state. It defines the
 * fields that are returned to the API client in a GET response for this state.
 /
public class UserAttributesRequired
{
	private List<String> attributeNames = new ArrayList<>();

	/
	 * Get the list of user attributes supported by this adapter instance.
	 *
	 * It is recommended to annotate each getter with the @Schema annotation
	 * and provide a description. This description will be used in
	 * generating API documentation.
	 */
	@Schema(description="A list of user attribute names that are supported by this adapter instance.")
	public List<String> getAttributeNames()
	{
		return attributeNames;
	}

	/*
	 * Set the list of user attributes supported by this adapter instance.
	 */
	public void setAttributeNames(List<String> attributeNames)
	{
		this.attributeNames = attributeNames;
	}
}

Action model example

The following is the model for the submitUserAttributes action.

/
 * This is the model for the submitUserAttributes API action. It defines the
 * fields that may be included in the POST body for this action.
 /
public class SubmitUserAttributes
{
	private String username;
	private Map<String,Object> userAttributes = new HashMap<>();

	/
	 * Get the username.
	 *
	 * It is recommended to annotate each getter with the @Schema annotation
	 * and provide a description. The 'required' flag can also be specified. This
	 * information will be used in generating API documentation.
	 */
	@Schema(description="The user's username.", required=true)
	public String getUsername()
	{
		return username;
	}

	/
	 * Set the username.
	 */
	public void setUsername(String username)
	{
		this.username = username;
	}

	/
	 * Get the user attributes.
	 */
	@Schema(description="Additional user attributes, as name-value pairs.")
	public Map<String, Object> getUserAttributes()
	{
		return userAttributes;
	}

	/*
	 * Set the user attributes.
	 */
	public void setUserAttributes(Map<String, Object> userAttributes)
	{
		this.userAttributes = userAttributes;
	}
}

AuthnStateSpec and AuthnActionSpec objects

A fluent builder is provided for creating AuthnStateSpec and AuthnActionSpec objects.

Here is the definition of the AuthnStateSpec for the USER_ATTRIBUTES_REQUIRED state:

public final static AuthnStateSpec<UserAttributesRequired> USER_ATTRIBUTES_REQUIRED = new AuthnStateSpec.Builder<UserAttributesRequired>()
	.status("USER_ATTRIBUTES_REQUIRED")
	.description("The user's username and attributes are required.")
	.modelClass(UserAttributesRequired.class)
	.action(ActionSpec.SUBMIT_USER_ATTRIBUTES)
	.action(CommonActionSpec.CANCEL_AUTHENTICATION)
	.build();

Here is the specification for the submitUserAttributes action:

public final static AuthnActionSpec<SubmitUserAttributes> SUBMIT_USER_ATTRIBUTES = new AuthnActionSpec.Builder<SubmitUserAttributes>()
	.id("submitUserAttributes")
	.description("Submit the user's username and attributes.")
	.modelClass(SubmitUserAttributes.class)
	.error(CommonErrorSpec.VALIDATION_ERROR)
	.errorDetail(ErrorDetailSpec.INVALID_ATTRIBUTE_NAME)
	.build();

Error specifications

Action specifications can include a list of possible errors and error details. Each top-level error that an authentication API request returns can include one or more error detail objects underneath it.

Typically, in the API specification, your only top-level error will be CommonErrorSpec.VALIDATION_ERROR. However, you can include error detail specifications that can appear under that top-level error.

The following is how the TemplateRenderAdapter defines the specification for the INVALID_ATTRIBUTE_NAME error detail.

public final static AuthnErrorDetailSpec INVALID_ATTRIBUTE_NAME = new AuthnErrorDetailSpec.Builder()
	.code("INVALID_ATTRIBUTE_NAME")
	.message("An invalid attribute name was provided.")
	.parentCode(CommonErrorSpec.VALIDATION_ERROR.getCode())
	.build();

The error detail specification must reference the error code of its parent top-level error. This ensures that the authentication API Explorer correctly represents the error information.

INVALID_ATTRIBUTE_NAME is an example of an error that would be useful for API client developers but not for end users.

For more information about defining user-facing errors, see [pf_error_messages_localization].