Collector Token Authentication

Collector token authentication is used to obtain an authentication token from an HTTP-based API. It works in combination with the HTTP collector and the XML/JSON collector: those collectors reference the resulting token through the ${token:<name>} placeholder when building their data-collection requests.

Examples may reference vendor APIs that have since changed. Check current vendor documentation before adapting any specific block.

The feature is opt-in. If token-auth-configuration.xml defines no <token-auth> blocks, no runtime behavior changes. Collector configurations that do not reference ${token:…​} are unaffected.

File location

The configuration lives at ${OPENNMS_HOME}/etc/token-auth-configuration.xml. A default install includes a minimal stub with commented examples.

The schema is at ${OPENNMS_HOME}/share/xsds/token-auth-configuration.xsd. Validation runs through the standard bin/config-tester flow.

Define a token-auth block

Each authentication endpoint is one <token-auth> block. The name attribute is the key used in the ${token:<name>} placeholder.

<token-auth-configuration xmlns="http://xmlns.opennms.org/xsd/config/token-auth">
  <token-auth name="catalyst-prod">
    <url>https://catalyst.example.com/dna/system/api/v1/auth/token</url>
    <method>POST</method>
    <basic-auth username="${scv:catalyst:username}" password="${scv:catalyst:password}"/>
    <token-from jsonpath="Token"/>
    <ttl-seconds>3300</ttl-seconds>
  </token-auth>
</token-auth-configuration>

A collector configuration that supports request headers can then reference the resulting token:

<request>
  <header name="X-Auth-Token" value="${token:catalyst-prod}"/>
</request>

At request build time, Horizon resolves the placeholder by performing the auth call described in the <token-auth> block. The resulting token is cached and substituted into the header before the data request is sent.

Element reference

<token-auth> element
Element / Attribute Required Description

name

yes

Identifier used in ${token:<name>}. Names must be unique across the file.

<url>

yes

HTTP or HTTPS URL of the token-auth endpoint. Per-node placeholders are permitted (see Metadata DSL inside token-auth blocks).

<method>

no

One of GET, POST, or PUT. Defaults to POST.

<basic-auth>

no

Sends an HTTP Basic Authorization header. Both username and password are required when this element is present.

<header>

no, repeatable

Adds a request header to the auth call. Common uses include Content-Type and vendor-specific authentication headers.

<content>

no

Body of the auth request. The type attribute sets the Content-Type.

<token-from>

yes

Locates the token in the auth response. See Token extraction modes.

<ttl-seconds>

no

Optional proactive refresh interval. The cache fetches a new token before this elapses. When unset, the token is held until something invalidates it.

<disable-ssl-verification>

no

Set to true to skip TLS certificate validation on the auth call. Defaults to false.

<use-system-proxy>

no

When true, the auth call uses the JVM-configured proxy. Defaults to false.

Token extraction modes

The <token-from> element supports three mutually exclusive modes. Match the mode to the response shape of the auth API in question.

<token-from> modes
Attribute Description

jsonpath

The token is in the JSON response body at the given slash-separated path. jsonpath="Token" reads {"Token":"…​"}. jsonpath="auth/client_token" reads {"auth":{"client_token":"…​"}}.

header

The token is returned in an HTTP response header. The attribute value is the header name, for example header="X-Auth-Token". Some endpoints respond with 204 No Content and the token in a header.

body-as-token

Set to true if the entire response body is the token. Surrounding double quotes are stripped, so a response body of "abc123" yields abc123.

Metadata DSL inside token-auth blocks

Field values inside an <token-auth> block are resolved through the metadata DSL before the auth call goes out. Three scopes are available:

  • ${scv:<alias>:<key>}: This scope reads the value from the secure credentials vault. See Secure Credentials Vault.

  • ${env:<NAME>}: This scope reads a process environment variable.

  • ${node:<key>}, ${requisition:<key>}, etc.: These scopes carry the metadata of the node that triggered the auth lookup. The same logical auth can therefore fan out to per-region or per-tenant endpoints, for example https://${node:label}.api.example.com/auth.

The cache key includes a hash of the resolved fields. Requests that resolve to the same URL, credentials, and body share one cached token.

${token:…​} placeholders inside a token-auth block aren’t resolved here. The block’s own URL, credentials, body, and header values pass through the standard metadata DSL (so ${scv:…​}, ${node:…​}, etc. work), but a ${token:other} reference is unknown to the DSL and resolves to an empty string. Chain definitions through other mechanisms (e.g., shared SCV entries) rather than through token references.

Caching and refresh

Tokens are held in memory only. A restart drops the cache. A configuration reload flushes it explicitly.

The cache refreshes a token when one of the following holds:

  • No entry exists yet for the resolved auth shape.

  • The configured <ttl-seconds> has elapsed, or the cache’s internal early-refresh window has been reached. The early-refresh window is max(600 seconds, 5% of TTL) before the configured TTL expires, so the cache never hands back a token that’s within ten minutes of upstream expiry.

  • A downstream collector reports a FAILED collection status and the underlying cause is an HTTP 401 or 403 from the data endpoint. The adaptor invalidates the affected entry; the next collection cycle re-acquires.

Invalidation is passive: the failing request itself isn’t retried. The next scheduled cycle picks up a fresh token. For a Minion-served collection, the FAILED status flows back to the core, where the same adaptor invalidates locally, so the next cycle through the same Minion goes out with a fresh token.

A FAILED status that surfaces for non-auth reasons — RPC timeout, broker outage, generic 5xx, transport error — does not invalidate the cached token. The adaptor only acts when the exception cause chain carries an auth failure: marker emitted by the collector on a 401/403 from the data endpoint.
A short <ttl-seconds> is still useful as defense-in-depth for environments where tokens can be revoked out of band, since it bounds the window during which the cache may hold a token the auth server has already invalidated.

Reload without restart

A standard daemon-reload event re-reads token-auth-configuration.xml and flushes the cache:

opennms:send-event -p daemonName=TokenAuth uei.opennms.org/internal/reloadDaemonConfig

A successful reload publishes uei.opennms.org/internal/reloadDaemonConfigSuccessful with daemonName=TokenAuth. Validation errors (duplicate names, malformed URLs, missing <token-from>) cause the reload to fail and publish reloadDaemonConfigFailed with a truncated reason. The previously-loaded configuration remains in effect until the next successful reload.

Reference from collector configurations

Both the XML and HTTP collectors accept ${token:<name>} in any header value.

  • In xml-datacollection-config.xml: This collector supports the placeholder inside a <request>/<header> element of an <xml-source>.

  • In http-datacollection-config.xml: The <url> element supports a <headers> block for the same purpose.

The header value is resolved at request build time, alongside any other metadata DSL placeholders. A header such as X-Tenant: ${node:foreign-source} and Authorization: Bearer ${token:my-auth} can appear on the same request.

HTTPS

Auth URLs may use either http:// or https://. HTTPS uses the JVM truststore for certificate validation. The Certificate Authority needs to be added to ${JAVA_HOME}/lib/security/cacerts, or pointed to via javax.net.ssl.trustStore.

Client certificate (mTLS) authentication on the auth call is not currently supported. Auth definitions can only carry credentials through HTTP Basic, the request body, or request headers.

<disable-ssl-verification>true</disable-ssl-verification> is intended for lab use only. This flag bypasses the certificate trust check entirely.

Worked example: Catalyst Center

<token-auth-configuration xmlns="http://xmlns.opennms.org/xsd/config/token-auth">
  <token-auth name="catalyst">
    <url>https://${node:label}.example.com/dna/system/api/v1/auth/token</url>
    <method>POST</method>
    <basic-auth username="${scv:catalyst:username}" password="${scv:catalyst:password}"/>
    <token-from jsonpath="Token"/>
    <ttl-seconds>3300</ttl-seconds>
  </token-auth>
</token-auth-configuration>

The XML collector then references the resolved token:

<xml-source url="https://{ipaddr}/dna/intent/api/v1/network-device">
  <request>
    <header name="X-Auth-Token" value="${token:catalyst}"/>
  </request>
  ...
</xml-source>

Worked example: response header

When the auth API returns the token in a response header rather than the body:

<token-auth name="header-style">
  <url>https://api.example.com/login</url>
  <method>POST</method>
  <content type="application/json">{"identity":"${scv:api:user}","password":"${scv:api:password}"}</content>
  <token-from header="X-Auth-Token"/>
  <ttl-seconds>1800</ttl-seconds>
</token-auth>

Worked example: body-as-token

When the entire response body is the token (some APIs return a JSON-encoded bare string):

<token-auth name="vsphere-session">
  <url>https://vcenter.example.com/api/session</url>
  <method>POST</method>
  <basic-auth username="${scv:vsphere:username}" password="${scv:vsphere:password}"/>
  <token-from body-as-token="true"/>
  <ttl-seconds>1800</ttl-seconds>
</token-auth>