NMS-19751: Support for Token Based Authentication with XML/JSON Collector (and HTTP collector)#8495
Conversation
|
So this is passing except for the WmiPeerFactoryTest in CircleCI as far as I can tell. There were indeed some integration test failures that I needed to fix. They should be now. |
The |
|
I'll give it a look again. |
|
Integration tests all seem to be passing now. There were definitely a couple things in there. I've made a note to myself to run those locally first now. |
|
Smooth-brain thought: Couldn't you accomplish this same thing using a shell script in cron that does a |
I think that can/will work... but there are some misses to me... It could definitely cover a single long lived token use case... I can't argue that really but I don't think it covers the shorter lived ones as nicely. That being said, I think the biggest issue is that any 401/403 recoveries have to happen on the cron schedule and can't happen in-line with a response (granted, I guess you could use the failed collection in OpenNMS and have it do that with scripting but then you've got 3 things to mangle). There is also something to be said that this works with metadata (and SCV) whereas cron wouldn't so you could do this across a fleet of systems instead of having to probably create a separate script for each one and having to manage other secure vault stuff. I also think this keeps everything in OpenNMS and that wayt tokens live in memory (RAM) as a result so I think that fits with the idea of tokens being short lived and not meant to be permanent (semi-permanent?) and stored in a vault. |
There was a problem hiding this comment.
Pull request overview
Adds a dynamic-auth runtime to OpenNMS that can acquire/cache/refresh tokens from external HTTP login flows defined in etc/auth-configuration.xml, expose them via the metadata DSL ${auth:<name>}, and enable collector retry-on-401/403 behavior (XML/JSON collectors and HTTP collector headers).
Changes:
- Introduces
auth-configuration.xmlschema/model, parsing/validation, token acquisition and caching runtime, and a reload listener to re-read config + flush cache. - Wires
${auth:<name>}into the metadata DSL via a newAuthScope,TokenProviderAPI, andEntityScopeProviderintegration. - Adds collector-side support: HTTP collector
<headers>block, and 401/403 retry logic in XML/JSON collectors (with token invalidation + refresh).
Reviewed changes
Copilot reviewed 51 out of 52 changed files in this pull request and generated 7 comments.
Show a summary per file
| File | Description |
|---|---|
| protocols/xml/src/test/java/org/opennms/protocols/xml/collector/RefreshAuthTokensTest.java | Adds unit tests for request-header token refresh behavior on retry. |
| protocols/xml/src/main/java/org/opennms/protocols/xml/collector/AbstractXmlCollectionHandler.java | Adds 401/403 retry-once path and token refresh/invalidation logic for XML collector requests. |
| protocols/xml/src/main/java/org/opennms/protocols/json/collector/AbstractJsonCollectionHandler.java | Adds 401/403 retry-once path using shared token refresh logic for JSON collector requests. |
| protocols/xml/src/main/java/org/opennms/protocols/http/HttpUrlConnection.java | Throws typed AuthFailureException on 401/403 to enable auth-aware retry upstream. |
| protocols/xml/src/main/java/org/opennms/protocols/http/AuthFailureException.java | Adds typed exception carrying HTTP status for auth failures. |
| opennms-config/src/test/resources/JUnitHttpServer.keystore | Adds test keystore for HTTPS acquisition tests. |
| opennms-config/src/test/java/org/opennms/netmgt/config/auth/TokenProviderImplTest.java | Unit tests for TokenProviderImpl behaviors. |
| opennms-config/src/test/java/org/opennms/netmgt/config/auth/TokenCacheTest.java | Unit tests for token caching, expiry, invalidation, and scope-fingerprinting behavior. |
| opennms-config/src/test/java/org/opennms/netmgt/config/auth/TokenAcquirerTest.java | Tests token acquisition and extraction modes (jsonpath/header/body-as-token) plus placeholder interpolation. |
| opennms-config/src/test/java/org/opennms/netmgt/config/auth/TokenAcquirerHttpsTest.java | Verifies disable-SSL-verification behavior against self-signed certs. |
| opennms-config/src/test/java/org/opennms/netmgt/config/auth/AuthRuntimeIntegrationTest.java | Integration test covering the full dynamic-auth stack composition. |
| opennms-config/src/test/java/org/opennms/netmgt/config/auth/AuthConfigReloadListenerTest.java | Tests daemon reload event handling and cache flush behavior. |
| opennms-config/src/test/java/org/opennms/netmgt/config/AuthConfigFactoryTest.java | Tests parsing/validation rules for auth-configuration.xml. |
| opennms-config/src/main/resources/META-INF/opennms/component-dao.xml | Imports auth runtime Spring context and registers auth-configuration.xml as a JAXB config resource. |
| opennms-config/src/main/resources/META-INF/opennms/applicationContext-auth.xml | Wires TokenAcquirer/TokenCache/TokenProvider + OSGi service + reload listener. |
| opennms-config/src/main/java/org/opennms/netmgt/config/auth/TokenProviderImpl.java | Implements TokenProvider backed by AuthConfigFactory + TokenCache. |
| opennms-config/src/main/java/org/opennms/netmgt/config/auth/TokenCache.java | Adds in-memory token cache keyed by (authName, resolved fingerprint), plus reverse-lookup invalidation. |
| opennms-config/src/main/java/org/opennms/netmgt/config/auth/TokenAcquirerScopeBinder.java | Late-binds EntityScopeProvider onto TokenAcquirer when available to avoid wiring cycles. |
| opennms-config/src/main/java/org/opennms/netmgt/config/auth/TokenAcquirer.java | Implements HTTP login flow execution, placeholder interpolation, token extraction, and fingerprinting. |
| opennms-config/src/main/java/org/opennms/netmgt/config/auth/CachedToken.java | Introduces token value + optional expiry container for caching. |
| opennms-config/src/main/java/org/opennms/netmgt/config/auth/AuthConfigReloadListener.java | Implements daemon-reload listener that reloads auth config and flushes token cache. |
| opennms-config/src/main/java/org/opennms/netmgt/config/AuthConfigFactory.java | Adds singleton config loader/validator for auth-configuration.xml. |
| opennms-config/pom.xml | Adds runtime deps needed for auth acquisition (httpclient, core.web wrapper, jackson-databind). |
| opennms-config-tester/src/test/java/org/opennms/netmgt/config/tester/ConfigTesterTest.java | Ensures config-tester validates auth-configuration.xml. |
| opennms-config-tester/src/main/resources/META-INF/opennms/applicationContext-configTester.xml | Wires AuthConfigFactory for config-tester context. |
| opennms-config-model/src/test/java/org/opennms/netmgt/config/httpdatacollection/HttpDatacollectionConfigTest.java | Updates HTTP DC model test to include new <headers> element. |
| opennms-config-model/src/test/java/org/opennms/netmgt/config/auth/AuthConfigurationTest.java | Adds JAXB/XSD round-trip tests for new auth config model. |
| opennms-config-model/src/main/resources/xsds/http-datacollection-config.xsd | Adds <headers> block support under <url> for HTTP collector. |
| opennms-config-model/src/main/resources/xsds/auth-configuration.xsd | Adds schema for auth-configuration.xml. |
| opennms-config-model/src/main/java/org/opennms/netmgt/config/httpdatacollection/Url.java | Adds headers list to HTTP DC Url model. |
| opennms-config-model/src/main/java/org/opennms/netmgt/config/httpdatacollection/Header.java | Adds new HTTP DC header model type. |
| opennms-config-model/src/main/java/org/opennms/netmgt/config/auth/package-info.java | Defines JAXB namespace for new auth config model package. |
| opennms-config-model/src/main/java/org/opennms/netmgt/config/auth/TokenFrom.java | Adds token extraction configuration model. |
| opennms-config-model/src/main/java/org/opennms/netmgt/config/auth/Header.java | Adds auth-request header model type. |
| opennms-config-model/src/main/java/org/opennms/netmgt/config/auth/Content.java | Adds auth-request content/body model type. |
| opennms-config-model/src/main/java/org/opennms/netmgt/config/auth/BasicAuth.java | Adds basic-auth model for auth request. |
| opennms-config-model/src/main/java/org/opennms/netmgt/config/auth/AuthConfiguration.java | Adds top-level auth configuration model. |
| opennms-config-model/src/main/java/org/opennms/netmgt/config/auth/Auth.java | Adds per-auth definition model (URL/method/headers/content/token-from/TTL/flags). |
| opennms-base-assembly/src/main/filtered/etc/auth-configuration.xml | Adds default stub config file shipped in base assembly. |
| integration-tests/config/src/test/java/org/opennms/netmgt/config/WillItUnmarshalIT.java | Adds unmarshal IT coverage for auth-configuration.xml. |
| features/collection/collectors/src/test/java/org/opennms/netmgt/collectd/HttpCollectorHeaderTest.java | Adds focused unit tests for applying configured headers to outgoing HTTP requests. |
| features/collection/collectors/src/main/java/org/opennms/netmgt/collectd/HttpCollector.java | Applies configured URL headers to outgoing requests via new helper. |
| core/mate/model/src/main/java/org/opennms/core/mate/model/EntityScopeProviderImpl.java | Wires optional TokenProvider to provide AuthScope in entity scope chains. |
| core/mate/api/src/test/java/org/opennms/core/mate/api/AuthScopeTest.java | Adds unit tests for AuthScope behavior. |
| core/mate/api/src/test/java/org/opennms/core/mate/api/AuthScopeInterpolatorTest.java | Adds tests confirming ${auth:...} flows through Interpolator with AuthScope. |
| core/mate/api/src/main/java/org/opennms/core/mate/api/TokenProvider.java | Introduces TokenProvider API used by metadata DSL auth namespace. |
| core/mate/api/src/main/java/org/opennms/core/mate/api/Interpolator.java | Tracks calling scope stack for auth placeholder resolution to inherit per-node context. |
| core/mate/api/src/main/java/org/opennms/core/mate/api/EntityScopeProvider.java | Adds default getScopeForAuth() hook for ${auth:...} support. |
| core/mate/api/src/main/java/org/opennms/core/mate/api/AuthScope.java | Adds new metadata DSL scope implementation for ${auth:<name>}. |
| core/lib/src/main/java/org/opennms/core/utils/ConfigFileConstants.java | Registers auth-configuration.xml in config file constants mapping. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Seven follow-ups from the PR OpenNMS#8495 review pass: - Interpolator: when scope is null, substitute EmptyScope.EMPTY before recursing so inputs that contain placeholders no longer NPE inside interpolateSingle when scope.get(...) is invoked. Plain non-templated inputs still fall through unchanged. - AuthConfigFactory.reload: parse and validate into a fresh factory first, then swap m_singleton on success. A failed reload now leaves the previously-loaded configuration in effect, matching the behavior documented in auth-configuration.adoc. - AbstractXmlCollectionHandler.refreshAuthTokensInRequest: two-pass refresh. First pass collects all distinct (oldToken -> freshToken) pairs by probing one header at a time and skipping headers already covered by a queued swap; second pass applies the swaps across every matching header. A request that uses the same ${auth:...} in multiple headers (Authorization plus X-Auth-Token, for example) now refreshes all of them before the retry instead of only the first. - TokenAcquirer: take an injectable Clock so the cached-token expiry timestamp shares a time source with TokenCache. Production wiring stays on Clock.systemUTC(); test fixtures can pass a fake clock to make TTL behavior fast-forwardable. - TokenProviderImpl: fix log message typo ${{auth:...}} -> ${auth:...}. - TokenAcquirerHttpsTest: capture the original default HostnameVerifier in @BeforeClass and install Apache HttpClient's strict DefaultHostnameVerifier for the duration of the class, then restore in @afterclass. The negative test now runs against a known-strict verifier regardless of what other tests in the same surefire fork did to the global default. - HttpCollector.applyConfiguredHeaders: guard against null Header entries and null header values, mirroring the existing name guard so a partially populated Header from programmatic construction can no longer reach method.setHeader(name, null).
|
@cgorantla I added the minion 401/403 feature. Wasn't too bad to add but it does alter the HTTPCollector a bit given that those need to be retries but I could not come up with a reason someone would be using that collector for testing that and there is an alternative through PSM. Tests all seem to be passing. Let me know about feedback. I can adjust accordingly. |
cgorantla
left a comment
There was a problem hiding this comment.
Reviewed few modules. Need to actually validate this by running this code.
|
Converted to draft for now. Working on a revised plan based on the feedback. |
cgorantla
left a comment
There was a problem hiding this comment.
I have added few more comments on what I think should be considered.
af53eb0 to
d408642
Compare
|
This PR has been rewritten to address issues raised:
Other changes:
I'm going to kick off a copilot review @cgorantla but will wait to address anything until you've looked this over. I also still need to run integration tests locally. |
Define the user-facing config for token-based authentication against external HTTP APIs used by the XML/JSON and HTTP collectors. The schema covers: - Token-auth definitions with login URL, method, basic-auth, content, headers - Three token-extraction modes: jsonpath, response header, body-as-token - Optional TTL for cache expiry; cache identity is widened at runtime by a fingerprint of the resolved request fields so one definition can serve many endpoints This commit adds only the JAXB types, XSD, sample etc/collector-token-auth-configuration.xml, and the ConfigFileConstants entry. Runtime (factory, acquirer, cache, provider) and collector integration land in follow-up commits.
The previous adaptor scanned only the flat collection-parameter map,
which made ${token:NAME} work for collectd-configuration.xml
<parameter> values but not for the placeholders customers actually
write -- headers inside xml-datacollection-config.xml /
http-datacollection-config.xml <xml-source>/<request>/<header> blocks.
Those headers travel as JAXB-deserialized runtime attributes, get
processed only by Mate's Interpolator, and unknown prefixes are
stripped to empty -- so the documented use case shipped broken.
CollectorAdaptor gains a beforeRuntimeInterpolation hook that runs
before Interpolator.interpolateAttributes. Adaptors that own a custom
${prefix:...} placeholder now have a chance to substitute on the
runtime-attribute tree first; Mate's standard pass then resolves the
remaining prefixes it knows. No changes to core/mate; the new
substitution is contained in the adaptor.
TokenAuthCollectorAdaptor's implementation walks each value:
- String values: regex-substitute directly.
- @XmlRootElement values: round-trip via JaxbUtils.marshal +
regex-substitute + JaxbUtils.unmarshal, mirroring how Mate's own
Interpolator.interpolate(Object, Scope) handles JAXB types.
- Mate ToBeInterpolated wrappers (Interpolator.pleaseInterpolate)
are unwrapped reflectively, the inner value is processed, and the
result is re-wrapped with pleaseInterpolate so Mate's standard
pass continues to recurse into it.
Names resolved during the runtime-attribute walk are merged into
AUTH_NAMES_USED_PARAM alongside anything beforeCollect already
stashed, so handleCollectionResult invalidates the right cache
entries on FAILED collections via either path.
Five new TokenAuthCollectorAdaptorTest cases cover the three value
shapes and the empty / no-placeholder / name-merge paths.
- TokenAuthConfigFactoryTest, TokenAuthConfigReloadListenerTest:
test XML used the stale /xsd/config/auth namespace; under that
namespace JAXB unmarshalled an empty config and the assertions
were exercising malformed input. Swap to the real
/xsd/config/token-auth namespace.
- Delete the unused TokenAuthFailureException class.
HttpUrlConnection never threw it and nothing else referenced it;
401/403 handling lives on the HttpCollector path via
HttpCollectorException, and the XML/JSON path surfaces failures
through a FAILED CollectionSet so the adaptor invalidates on
the next cycle.
- TokenAcquirer.fingerprint(): TokenAuth.getMethod() returns
DEFAULT_METHOD ("POST") when <method> is omitted, so the
null-coalesce in the fingerprint was dead and the comment
copilot raised about implicit-vs-explicit POST hashing
differently never actually happened. Drop the redundant guard
and call out the model default in a comment.
- TokenAcquirerTest.acquireSetsExpiresAt: the original assertion
message implied expiresAt should be roughly ttl seconds ahead.
TokenAcquirer intentionally subtracts the early-refresh buffer
(max(600s, 5% of ttl)) -- for ttl=900 the effective remaining
time is ~300s. Update the comment and message to describe the
actual contract; bounds stay as-is since they already pass.
- Url.equals/hashCode: include m_headers. Two Url values that
differ only by their <headers> block were comparing equal,
which would silently merge them in any value-equality cache or
collection.
cgorantla
left a comment
There was a problem hiding this comment.
Suggested few comments, I still need to go through whole code one more time
It is difficult to go through the code as there is too much commenting for every method.
Only add comments that are strictly necessary.
Also lot of places, package naming is expanded like java.util.List<org.opennms.netmgt.collection.api.CollectorAdaptor> which should be avoided.
- Add Interpolator.isPleaseInterpolate / unwrap helpers; drop the reflection on Interpolator$ToBeInterpolated in TokenAuthCollectorAdaptor. - Wire TokenAuthConfigFactory directly via Spring (mirroring applicationContext-configTester.xml); remove the Supplier indirection and lenientFactoryLookup from TokenProviderImpl. - Move test helpers in TokenAuthConfigFactory to the bottom of the class with @VisibleForTesting; collapse clearForTest into resetForTesting. - Import StandardCharsets / Clock / Security; replace fully-qualified type names with imports across CollectionSpecification, Collectd, TokenAcquirer, HttpCollector, and TokenAuthConfigReloadListener. - Strip repetitive method-level Javadoc and inline prose; keep the SHA-256 fingerprint rationale, the substring-match guard, the ConcurrentHashMap.compute caveat, and the late-binding listener note. - Remove all comment blocks from applicationContext-token-auth.xml.
Missed in the previous sweep — the seven java.util.List.of(...) call sites in the test file now use the imported short name.
When component-dao.xml imports applicationContext-token-auth.xml, the
MethodInvokingFactoryBean fires TokenAuthConfigFactory.init() at Spring
context load. In test contexts without an opennms.home (or without a
staged etc/token-auth-configuration.xml), getFile() throws
FileNotFoundException and the whole context fails to load -- breaking
ITs like LiquibaseUpgraderIT that legitimately need component-dao.xml
but have no use for token-auth.
Catch FileNotFoundException in init()/reload(), log INFO, and start
the singleton with an empty TokenAuthConfiguration. Production
behaviour is unchanged because opennms-base-assembly always ships the
file; a missing definition is still reported explicitly at first
${token:NAME} use.
…orAdaptor
Three gaps closed so token-auth invalidation actually fires end-to-end:
- features/collection/shell-commands/CollectCommand: bind every OSGi
CollectorAdaptor on the request builder before .execute(), so
`opennms:collect` resolves ${token:NAME} the same way the scheduled
path does.
- features/collection/client-rpc/CollectorRequestBuilderImpl: replace
thenApply with handle so adaptors run on both success and exceptional
completion. On a failure, synthesize a FAILED CollectionSet, run the
adaptors against it, then rethrow the original exception. Extracted
the merge logic into a public static applyAdaptorsAndPropagate for
direct testing.
- protocols/xml/HttpUrlConnection.getInputStream(): check the HTTP
response status and throw IOException on >=400. Apache HttpClient
otherwise returns the error body as a normal InputStream, which made
401s look like valid (empty) collections and silently kept stale
tokens cached.
Tests:
- CollectCommandAdaptorTest (shell command picks up OSGi adaptors)
- CollectorRequestBuilderImplTest (handle() runs adaptors on success
and exception, FAILED synthesized, original exception rethrown,
CompletionException unwrapped)
- HttpUrlConnectionAuthFailureTest (200 returns body, 401/500 throw)
- TokenAuthInvalidationIntegrationTest (cross-module: real
TokenAuthCollectorAdaptor + applyAdaptorsAndPropagate together
invalidate the right names on a thrown collection)
Doc: token-authentication.adoc now flags that a FAILED status from a
non-auth cause (RPC timeout, broker outage, transport error) also
invalidates the cached token. Benign at most one extra acquire per
transient cycle.
The TokenAuthInvalidationIntegrationTest added in the previous commit
pulled opennms-config (test scope) -> features.collection.client-rpc.
Combined with the existing
client-rpc -> core.ipc.rpc.utils -> core.mate.model -> dao-mock -> opennms-config
chain, that produced a reactor cycle that broke `mvn install` on CI.
Drop the test-scope dep and remove the integration test. The unit
coverage was already complete piecewise:
- CollectorRequestBuilderImpl.applyAdaptorsAndPropagate is exercised
by CollectorRequestBuilderImplTest (FAILED synthesis on exception,
rethrow, CompletionException unwrap, adaptor invocation order).
- TokenAuthCollectorAdaptor.handleCollectionResult is exercised by
TokenAuthCollectorAdaptorTest (invalidate-on-FAILED across name
sets, no-op on SUCCEEDED, etc.).
Both halves bind on the same `CollectionStatus.FAILED` enum, so the
composition can't drift silently.
cgorantla
left a comment
There was a problem hiding this comment.
Looks like we are adding tests for every source file.
Not sure if all of them are worth it. May be worth looking at reducing them.
…ilures * Restrict TokenAuthCollectorAdaptor to invalidate only on FAILED collections whose exception cause chain carries the "auth failure:" marker (set by HttpCollector / HttpUrlConnection on 401 and 403 from a data endpoint). Transport errors, broker outages, and 5xx surface as FAILED but leave the cached token alone. * HttpUrlConnection: prefix the IOException message with "auth failure:" on 401/403 so the controller-side wrapper can mark the synthetic FAILED CollectionSet. * CollectorAdaptor: add AUTH_FAILURE_PARAM constant. * CollectorRequestBuilderImpl: walk the cause chain in applyAdaptorsAndPropagate and set the marker before invoking adaptors. * Drop the unused beforeCollect adaptor hook (no remaining callers carried token placeholders in the flat <parameter> map). * Drop dead invalidateByTokenValue + InvalidationResult code from TokenProvider, TokenProviderImpl, TokenCache, and their tests; the remaining invalidate(name) path covers every actual call site. * Drop CollectCommandAdaptorTest per reviewer feedback. * Rewrite the token-authentication.adoc "benign false-positive" NOTE to state the new auth-only invalidation contract.
…rAdaptor services * HttpUrlConnection.getInputStream: wrap the >=400 branch in try-with-resources so the CloseableHttpResponse is closed before throwing. Without this, the new auth-failure throw leaked the response object even though consumeQuietly had drained the entity. * CollectCommand.registerAdaptors: pair each bundleContext.getService(ref) with bundleContext.ungetService(ref) in a finally block so repeated opennms:collect invocations don't accumulate OSGi service use-counts on each CollectorAdaptor.
…leContext Replace the manual BundleContext.getServiceReferences/getService loop in CollectCommand with @reference List<CollectorAdaptor> adaptors, which Karaf's shell wires via MultiServiceTracker. This is the pattern cgorantla pointed out and also retires the prior getService/ungetService balance: there is no use-count to manage when the shell framework owns the tracker lifecycle.
cgorantla
left a comment
There was a problem hiding this comment.
One improvement we can do otherwise LGTM!
…igured Per cgorantla, exit beforeRuntimeInterpolation early when the factory holds zero auth definitions. This skips the containsTokenPlaceholder walk, whose JAXB-marshal branch otherwise runs on every collection. The provider exposes hasAnyAuths() with a safe default of true so unknown implementations don't silently bypass substitution.
cgorantla
left a comment
There was a problem hiding this comment.
There is one more bug that might still be there.
Per cgorantla's review (discussion_r3284280039), HttpCollectorAgent.collect() caught its own HttpCollectorException and only set status=FAILED on the CollectionSet, which meant the controller-side TokenAuthCollectorAdaptor never saw the "auth failure:" marker on the cause chain and the auth-only invalidation gate did not fire for the HttpCollector path. Track auth-failure across the URI loop in HttpCollectorAgent.collect() and rethrow at the end so the exception propagates through the RPC future chain. Add a specific catch (HttpCollectorException) -> throw in doCollection() so the auth-failure exception is not re-wrapped by the generic catch (Throwable) (which obscured the marker message before). Also add HttpCollectorAuthFailureTest covering 401 and 403 responses against an embedded HttpServer, asserting the rethrown HttpCollectorException carries the expected prefix.
This adds support for token-based authentication against external HTTP APIs during data collection. Operators describe an API login flow once in etc/token-auth-configuration.xml and reference the resulting token from any XML/JSON or HTTP collector header via the ${token:} metadata placeholder. The runtime performs the login call, caches the token, refreshes on TTL, invalidates on 401/403, and supports reload via the standard daemon-reload event.
The motivating use case is vendor APIs like Cisco Catalyst Center, F5 BIG-IP, and VMware vSphere, where the collectors themselves can't perform a login flow.
What's in here:
Validated end-to-end against Hashicorp Vault (auth/client_token JSON), PocketBase (superuser token JSON), and WireMock — including a Cisco Catalyst Center-style stub that exchanges HTTP Basic for a
Token JSON and gates data endpoints behind X-Auth-Token. Each test exercises SCV-resolved credentials plus per-node metadata in the auth definition (URL path, basic-auth, request body, and request
headers).
I'd love feedback on this. We were asked by a potential customer the other day about our capability to scrape some specific endpoints that use this style of authentication and I think this fills a
pretty big gap for us in the XML/JSON Collector today.
Assisted-by: Claude Code:Opus 4.7