---
title: Config interface
description: The Config interface defines the configuration data for a node. A node can't have state, but it can have configuration data. Configuration is per node; different nodes of the same type in the same tree have their own configuration.
component: pingam
version: 7.3
page_id: pingam:auth-nodes:core-config
canonical_url: https://docs.pingidentity.com/pingam/8.1/auth-nodes/core-config.html
llms_txt: https://docs.pingidentity.com/pingam/llms.txt
docs_for_agents: https://developer.pingidentity.com/build-with-ai/docs-for-agents.md
keywords: ["Extensibility", "Nodes &amp; Trees", "Scripts"]
superseded_by: https://docs.pingidentity.com/pingam/8.1/auth-nodes/core-config.html
section_ids:
  define_node_properties: Define node properties
  attribute_annotation: Attribute annotation
  sharing-node-config: Share configuration between nodes
  creating-shared-aux-service: Create a shared auxiliary service
  referencing-shared-aux-service: Reference a shared auxiliary service instance
  reinstall-shared-aux-service: Reinstall a shared auxiliary service instance
  allowing-multiple-aux-service-instances: Allow multiple instances of an auxiliary service
  referencing-am-configs: Get configuration of built-in services
  referencing-am-configs-annotated: Get configuration from an annotated service
  referencing-am-configs-legacy: Get configuration from a legacy service
---

# Config interface

The `Config` interface defines the configuration data for a node. A node can't have state, but it can have configuration data. Configuration is per node; different nodes of the same type in the same tree have their own configuration.

You don't need to write a class that implements the interface you define. AM automatically creates this as required.

## Define node properties

Configure the node properties using *methods*. To provide a default value to the tree administrator, mark the method as `default` and define both a method and a value. To omit a default value, define the method's signature but not the implementation.

For example:

```java
public interface Config {

  //This will have no default value for the UI
  @Attribute(order = 10)
  String noDefaultAttribute();

  //This will default to the value LOCK.
  @Attribute(order = 20)
  default LockStatus lockAction() {
    return LockStatus.LOCK;
  }
}
```

For this `Config` example, a custom enum named `LockStatus` is returned.

The defined properties appear as configurable options in the tree designer view when adding a node of the relevant type. The options display to the user automatically.

Attribute names are used when localizing the node's text. For further information, refer to [Internationalization](i18n-nodes.html).

![The output from an example Config interface](_images/config-interface-in-ui.png)

For more information, refer to the [Config](../_attachments/apidocs/org/forgerock/openam/annotations/sm/Config.html) annotation type in the *AM Public API Javadoc*.

## Attribute annotation

The `Attribute` annotation is required. You must specify an integer value for `order`, which determines the position of the attribute in the UI.

Include `requiredValue = true` if the attribute is a required value.

Optionally, specify one or more `validators` to validate the attribute values provided. Create any validator classes that you require by implementing the [ServiceAttributeValidator](../_attachments/apidocs/com/sun/identity/sm/ServiceAttributeValidator.html) interface.

> **Collapse: Example validator**
>
> The following example creates a validator called `GreaterThanZeroValidator`:
>
> ```java
> public class GreaterThanZeroValidator implements ServiceAttributeValidator {
>     @Override
>     public boolean validate(Set<String> values) {
>         boolean isValid = true;
>         for (String value : values) {
>             if (Integer.parseInt(value) <= 0) {
>                 isValid = false;
>                 break;
>             }
>         }
>         return isValid;
>     }
> }
> ```

For example:

```java
public interface Config {
  @Attribute(order = 1)
  String domain();                                                       1

  @Attribute(order = 2, validators = {GreaterThanZeroValidator.class})   2

  int exampleNumber();

  @Attribute(order = 3, requiredValue = true)
  boolean isVerificationRequired();                                      3

  @Attribute(order = 4)
  @Password                                                              4
  char[] clientSecret();

  @Attribute(order = 5)
  default YourCustomEnum action() {
    return YourCustomEnum.LockScreen;                                    5
  };
}
```

1 The `domain` attribute defines a String-typed node property for display in the UI. Access the attribute in the `process` method by using a reference to the `config` interface. For example, `config.domain()`.

2 Specify one or more validator classes as the `validators` parameter.

3 The boolean attribute is defined as a required value.

4 Use the [Password](../_attachments/apidocs/org/forgerock/openam/sm/annotations/adapters/Password.html) annotation to mask the input characters and encrypt the value of the attribute.

5 A custom enum attribute. This provides type safety and negates the misuse of Strings as generic type-unsafe value holders. The UI will correctly handle the enum and only let the tree administrator choose from the defined enum values.

Learn more in the [Attribute](../_attachments/apidocs/org/forgerock/openam/annotations/sm/Attribute.html) annotation type in the *AM Public API Javadoc*.

## Share configuration between nodes

You can share configuration between nodes that have common properties. For example, a number of nodes may call out to an external service that requires a username, password, IP address, and port setting.

Instead of repeating the same configuration in each of these nodes, create a shared, auxiliary *service* to hold the common properties in one node. You can then reference that service from other nodes.

The following sections explain how to create and reference this auxiliary service. The sections also describe how to run more than one instance of an auxiliary service if required, and how to obtain the configuration from services built-in to AM.

### Create a shared auxiliary service

You can create a shared auxiliary service in the configuration interface defined as part of a node. Annotate the service with the `org.forgerock.openam.annotations.sm.Config` annotation to describe how the service functions.

Specify the scope of the service, either `GLOBAL` or `REALM`, as shown below:

```java
@Config(scope = Config.Scope.REALM)
public interface MyAuxService {
  @Attribute(order = 1)
  String serviceUrl();
}
```

You can also specify other features of the service, such as whether the service is a singleton in its scope, or if it can have multiple instances. For information about supporting multiple instances, refer to [Allow multiple instances of an auxiliary service](#allowing-multiple-aux-service-instances).

### Reference a shared auxiliary service instance

To access the shared auxiliary service, add `org.forgerock.openam.sm.AnnotatedServiceRegistry` to the `@Inject`-annotated constructor of the node.

Obtain the instance using the get instance methods on that class, for example:

```java
serviceRegistry.getRealmSingleton(MyAuxService.class, realm)
```

### Reinstall a shared auxiliary service instance

When developing a custom authentication node that references a shared auxiliary service, it can be useful for the node to be able to remove and reinstall the auxiliary service during upgrade, so that any existing configuration is cleared.

In the `upgrade` function of your plugin class, use the following example code to remove and reinstall a service:

```java
public void upgrade(String fromVersion) throws PluginException {
  SSOToken adminToken = AccessController.doPrivileged(AdminTokenAction.getInstance());

  if (fromVersion.equals(PluginTools.DEVELOPMENT_VERSION)) {
    ServiceManager sm = new ServiceManager(adminToken);

    if (sm.getServiceNames().contains("MyAuxService")) {
      sm.removeService("MyAuxService", "1.0");
    }
    pluginTools.install(MyAuxService.class);
  }
}
```

For more information on upgrading custom authentication nodes, refer to [Upgrade nodes and change node configuration](node-upgrade.html).

### Allow multiple instances of an auxiliary service

To enable configuration of multiple instances of the auxiliary service in either the same realm or at a global level, set the `collection` attribute to `true` in the `Config` annotation.

You can present the names of the instances of the service as a drop-down menu to the tree administrator.

To be able to present the names, make sure the service instance exposes its `id`, as follows:

```java
@Config(scope = Config.Scope.REALM, collection = true)
public interface MyAuxService {
  @Id
  String id();
  @Attribute(order = 1)
  String serviceUrl();
}
```

Change the nodes that will be using a service instance to store the `id` it uses, and implement `choiceValuesClass` as shown below:

```java
public class MyCustomNode implements Node {
  public interface Config {
    @Attribute(order = 1, choiceValuesClass = ExternalServiceValues.class)
    String serviceId();
  }

  public static class ExternalServiceValues extends ChoiceValues {

    @Override
    public Map<String, String> getChoiceValues() {
      return getChoiceValues(null);
    }

    @Override
    public Map<String, String> getChoiceValues(Map envParams) {
      String realmName = "/";
      if (envParams != null) {
        realmName = (String) envParams.getOrDefault(Constants.ORGANIZATION_NAME, "/");
      }
      try {
        return InjectorHolder.getInstance(AnnotatedServiceRegistry.class)
            .getRealmInstances(MyAuxService.class, Realms.of(realmName))
            .stream()
            .collect(Collectors.toMap(MyAuxService::id, MyAuxService::id));
      } catch (SSOException | SMSException | RealmLookupException e) {
        LoggerFactory.getLogger("amAuth").error("Couldn't load realm {}", realmName, e);
        throw new IllegalStateException("Couldn't load realm that was passed", e);
      }
    }
  }
  // ...
}
```

### Get configuration of built-in services

You can obtain configuration from services built-in to AM. For example, access the Email Service configuration to obtain the SMTP settings for the realm.

AM services are defined by two methods:

1. An annotated interface (most services)

2. An XML file (legacy services)

The following sections describe how to obtain the configuration from services defined using these two methods.

#### Get configuration from an annotated service

To obtain the configuration from a service that uses an annotated interface, add `org.forgerock.openam.sm.AnnotatedServiceRegistry` to your Guice constructor. If the configuration is realm-based, include the realm in the constructor, as follows:

```java
public class MyCustomNode extends SingleOutcomeNode {
  private final AnnotatedServiceRegistry serviceRegistry;
  private final Realm realm;

  @Inject
  public MyCustomNode(@Assisted Realm realm, AnnotatedServiceRegistry serviceRegistry) {
    this.realm = realm;
    this.serviceRegistry = serviceRegistry;
  }
  // ...
}
```

Obtain an instance of the service using one of the get methods of `AnnotatedServiceRegistry` in the constructor.

If the calls you make depend on input from elsewhere in the tree you can add `AnnotatedServiceRegistry` to the `process` method. Note that the following example assumes that a previous node has stored the ID of the AM service to use in shared state:

```java
public Action process(TreeContext context) throws NodeProcessException {
  String serviceId = context.getState.get("myAuxServiceId");
  MyAuxService instance = serviceRegistry.getRealmInstance(MyAuxService.class, realm, serviceId);
  // ...
}
```

#### Get configuration from a legacy service

To obtain an instance of the configuration from a legacy service, use the APIs in the `com.sun.identity.sm` package.

For example, to obtain the configuration values from a *realm* instance of a service, use `ServiceConfigManager` as follows:

```java
ServiceConfigManager scm = new ServiceConfigManager("legacyServiceName", token);
ServiceConfig sc = scm.getOrganizationConfig(realm.asPath(), null);
final Map<String, Set<String>> configMap = sc.getAttributes();
```

However, to obtain the configuration values from a *global* instance of a service, use `ServiceSchemaManager` as follows:

```java
ServiceSchemaManager ssm = new ServiceSchemaManager("legacyServiceName", getAdminToken());
Map<String, Set<String>> configMap = ssm.getGlobalSchema().getAttributeDefaults();
```
