Class TokenSourceChain

java.lang.Object
co.ankatech.ankasecure.sdk.auth.TokenSourceChain

public final class TokenSourceChain extends Object
Decides which credential the workload will present, from configuration alone.

The consumer never chooses, and the CLI never branches

That constraint is the reason this class exists. AWS's SDKs resolve credentials through an ordered provider chain in which the first source that yields anything wins and, in their own words, no additional code is required. Google's Workload Identity Federation - which is precisely the scenario here - puts the decision in a local configuration file naming where the external token comes from, with three source kinds, and states that application code remains largely identical across all three. Both settled on the same answer: the environment decides, the code does not. A public API that made the caller name a mechanism would push a deployment decision into a compile-time one, and every consumer would then need a branch per provider - which is the exact outcome this feature exists to prevent.

The order, and why it is this order

  1. TokenSourceChain.Link.EXTERNAL_TOKEN - a token supplied verbatim
  2. TokenSourceChain.Link.EXTERNAL_TOKEN_FILE - a file named by configuration
  3. TokenSourceChain.Link.EXTERNAL_TOKEN_COMMAND - a command named by configuration
  4. TokenSourceChain.Link.EXTERNAL_CLIENT_CREDENTIALS - the workload's own client-credentials grant, performed by this SDK against the workload's own authorization server
  5. TokenSourceChain.Link.ACTOR_KEY_PAIR - a registered key pair this process signs with
  6. TokenSourceChain.Link.NATIVE_CLIENT_SECRET - an ANKASecure client secret

Specific before ambient: the first four are statements about an identity issued elsewhere, which somebody configured on purpose for this workload. Native is last, and that placement carries the whole safety argument. A client secret is the value most likely to be left behind in a configuration file from an earlier setup, so had it been preferred, a deployment that deliberately moved to federated identity would keep authenticating with the old secret, succeed every time, and never report that the new configuration was being ignored. Silent success is the failure mode with no symptom.

Two external sources at once is refused, not resolved

The first four links are four spellings of ONE mechanism - Google models this as a single credential_source per configuration, never a list. Picking one silently is how an operator who adds a command discovers, months later, that a stale file has been answering all along. So an ambiguity among those four throws, naming both, at resolution time.

Reading a token somebody else obtained and obtaining one here are the same mechanism for this purpose: both end with ONE externally minted token being carried across ONE hop. Where it came from is the provider's business, and two of them at once is still an operator who cannot be told which one answered.

External-versus-native is a different case and is NOT an error: those are different credential kinds, and preferring one is a stated policy rather than a guess. Which link answered is reported by TokenSource.mechanism() and by selected(AuthSettings), so the preference is observable rather than merely silent.

There is still no URL source, and the reason has narrowed to the one that survives

Google offers a kind that fetches the external token by HTTP from a URL named in configuration. It is not reproduced here.

One of the reasons this class used to give has been superseded and is recorded as superseded rather than quietly deleted: contacting a host the ANKASecure configuration does not name is no longer something this SDK never does. TokenSourceChain.Link.EXTERNAL_CLIENT_CREDENTIALS does exactly that. What made that admissible is not that the objection was wrong - it is that the case answers it: the host is an issuer, screened by ExternalIssuerAdmission before anything dials it, resolved once and pinned to the addresses it resolved to, reached over a client that trusts the platform anchors and follows no redirect, and asked for a document with a protocol - OIDC discovery - whose issuer must equal the configured one exactly.

A bare URL returning a bearer token has none of that. There is no protocol to conform to, no discovery document to compare against, no issuer to validate and no client authentication: the SDK would GET whatever address configuration named and believe the bytes. That is the reason the rejection rests on, and it is unchanged. The remaining objection is unchanged too - it buys nothing a consumer cannot already do, since a workload able to reach a metadata endpoint can perform that one GET itself and hand the result to ExplicitExternalTokenProvider, or point FileExternalTokenProvider at whatever wrote it down.

The command source covers the same ground with a better trust story - an operator names a program, not a URL, and it will not run at all without an environment opt-in that no configuration file can set.

  • Field Details

    • ORDER

      public static final List<TokenSourceChain.Link> ORDER
      The chain, in the order it is evaluated. Exposed so the order is asserted by a test against a declaration rather than inferred from the body of a resolution method.
  • Method Details

    • configuredLinks

      public static List<TokenSourceChain.Link> configuredLinks(AuthSettings settings)
      Reports which links the given settings configure, in chain order, without building anything.

      Pure and side-effect free: it reads no file, starts no process and opens no connection. A caller can therefore ask what WOULD be used - to display it, to log it, to check a deployment - without that question having consequences.

      Parameters:
      settings - the configuration; must not be null
      Returns:
      the configured links in chain order; empty when nothing is configured
    • selected

      public static Optional<TokenSourceChain.Link> selected(AuthSettings settings) throws AnkaSecureSdkException
      The link that would answer, applying the order and the ambiguity rule.
      Parameters:
      settings - the configuration; must not be null
      Returns:
      the winning link, or empty when nothing is configured
      Throws:
      AnkaSecureSdkException - if two or more external-token sources are configured at once
    • resolve

      public static TokenSource resolve(co.ankatech.ankasecure.openapi.client.AnkaSecureOpenApiClient client, AuthSettings settings) throws AnkaSecureSdkException
      Builds the token source the configuration calls for.
      Parameters:
      client - the transport the resulting source mints against; must not be null
      settings - the configuration; must not be null
      Returns:
      a token source; never null
      Throws:
      AnkaSecureSdkException - if nothing is configured, if two external-token sources are configured at once, or if the winning link is configured incompletely