Add AthenzTokenClient API for standalone Athenz token fetching - #6691
Conversation
Motivation: Currently, AsyncLoader loads values lazily on the first load() call. For token clients (Athenz, OAuth2), this means the first request incurs the latency of fetching a token. A preload option allows tokens to be fetched eagerly at construction time, eliminating cold-start latency. Modifications: - Add `preload(boolean)` to `AsyncLoaderBuilder` that calls `load()` in the `DefaultAsyncLoader` constructor when enabled. - Wire the `preload` option through `OAuth2AuthorizationGrantBuilder` and `DefaultOAuth2AuthorizationGrant`. - Propagate `preload` through `AthenzClientBuilder`, `AthenzClient`, `AccessTokenClient`, and `RoleTokenClient`. - Add tests for preload in `DefaultAsyncLoaderTest`, `OAuth2ClientCredentialsGrantTest`, `AccessTokenClientTest`, and `RoleTokenClientTest`. Result: - Users can set `preload(true)` on `AsyncLoader`, `OAuth2AuthorizationGrantBuilder`, or `AthenzClientBuilder` to eagerly fetch tokens at build time, reducing first-request latency.
Motivation: Previously, Athenz token fetching was tightly coupled to the AthenzClient HTTP decorator. Users who wanted to use Athenz tokens with non-Armeria clients (e.g., Spring WebClient or RestTemplate) had no public API to fetch tokens independently. Modifications: - Add `AthenzTokenClient` public interface with `domainName()`, `roleNames()`, and `getToken()` methods. - Add `AthenzTokenClientBuilder` for constructing token clients with domain, roles, refresh, and preload settings. - Add `TokenClientSetters` interface to share setter methods between `AthenzClientBuilder` and `AthenzTokenClientBuilder`. - Refactor `AthenzClientBuilder` to delegate to `AthenzTokenClientBuilder`. - Refactor `AthenzClient` constructor to accept a pre-built `AthenzTokenClient` instead of building one internally. - Delete the old internal `TokenClient` interface. - Add `domainName()`, `roleNames()`, and `toString()` to `AccessTokenClient` and `RoleTokenClient`. Result: - Closes line#6431. - Users can now create an `AthenzTokenClient` independently: ```java AthenzTokenClient tokenClient = AthenzTokenClient.builder(ztsBaseClient) .domainName("my-domain") .roleNames("my-role") .build(); tokenClient.getToken().thenAccept(token -> { // Use the token for your own client. }); ```
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (2)
✅ Files skipped from review due to trivial changes (1)
📝 WalkthroughWalkthroughThe Athenz token client API was refactored: a new public Changes
Sequence Diagram(s)sequenceDiagram
participant Builder as AthenzClientBuilder
participant TokenBuilder as AthenzTokenClientBuilder
participant TokenClient as AthenzTokenClient
participant ZTS as ZtsBaseClient
participant Loader as AsyncLoader / OAuth2Grant
Builder->>TokenBuilder: delegate domain/roles/refreshBefore/preload
TokenBuilder->>TokenClient: build() -> RoleTokenClient / AccessTokenClient
TokenClient->>ZTS: request token (via HTTP)
TokenClient->>Loader: AsyncLoader / OAuth2Grant (may preload)
Loader-->>TokenClient: token (CompletableFuture)
TokenClient-->>Builder: token client instance used by AthenzClient
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 3
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
athenz/src/main/java/com/linecorp/armeria/client/athenz/RoleTokenClient.java (1)
1-15:⚠️ Potential issue | 🟡 MinorCopyright header uses "LINE Corporation" instead of "LY Corporation".
Same inconsistency as noted in other files. Consider using "LY Corporation" for consistency.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@athenz/src/main/java/com/linecorp/armeria/client/athenz/RoleTokenClient.java` around lines 1 - 15, Update the file header comment in RoleTokenClient (top of athenz/src/main/java/com/linecorp/armeria/client/athenz/RoleTokenClient.java) to use "LY Corporation" instead of "LINE Corporation" so the copyright header matches other files; replace all occurrences in the header block (the file-level comment above the RoleTokenClient class) accordingly.athenz/src/main/java/com/linecorp/armeria/client/athenz/AccessTokenClient.java (1)
1-15:⚠️ Potential issue | 🟡 MinorCopyright header uses "LINE Corporation" instead of "LY Corporation".
Same inconsistency as noted in
AthenzTokenClient.java. Consider using "LY Corporation" for consistency with other files in this PR.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@athenz/src/main/java/com/linecorp/armeria/client/athenz/AccessTokenClient.java` around lines 1 - 15, Update the file-level copyright header in AccessTokenClient.java to match the project's standard by replacing "LINE Corporation" with "LY Corporation"; locate the top-of-file header in AccessTokenClient.java (the same header pattern used in AthenzTokenClient.java) and adjust the copyright holder string so it is consistent across both files.
🧹 Nitpick comments (1)
core/src/main/java/com/linecorp/armeria/common/util/AsyncLoaderBuilder.java (1)
141-145: Polish preload Javadoc wording and semantics.Tiny doc cleanup: “This options” → “This option”, and consider explicitly saying preload is async fire-and-forget (build does not wait for completion).
Suggested Javadoc tweak
- * Preloads the value by calling the loader function immediately when {`@link` `#build`()} is called. - * This option is disabled by default, and the value is loaded lazily when {`@link` AsyncLoader#load()} is - * called for the first time. + * Preloads the value by calling the loader function immediately when {`@link` `#build`()} is called. + * This option is disabled by default, and the value is loaded lazily when {`@link` AsyncLoader#load()} is + * called for the first time. + * The preload is asynchronous and does not block {`@link` `#build`()}.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@core/src/main/java/com/linecorp/armeria/common/util/AsyncLoaderBuilder.java` around lines 141 - 145, Fix the Javadoc on AsyncLoaderBuilder: correct "This options" to "This option" and explicitly state preload is async fire-and-forget by saying that when preload is enabled the loader is invoked asynchronously during build() and build() does not wait for completion; retain that preload is disabled by default and that, otherwise, the value is loaded lazily when AsyncLoader#load() is called for the first time.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In
`@athenz/src/main/java/com/linecorp/armeria/client/athenz/AthenzTokenClient.java`:
- Around line 1-15: The file AthenzTokenClient.java has an outdated copyright
header reading "LINE Corporation"; update the file header to match the project
standard by replacing "LINE Corporation" with "LY Corporation" (follow the same
header format used in AthenzTokenClientBuilder.java and AthenzClient.java) so
the AthenzTokenClient class file uses the correct LY copyright block.
In
`@athenz/src/main/java/com/linecorp/armeria/client/athenz/TokenClientSetters.java`:
- Around line 28-64: Add the `@UnstableApi` annotation to the new public interface
TokenClientSetters to mark this API as unstable: import
com.linecorp.armeria.common.annotation.UnstableApi and place `@UnstableApi`
immediately above the TokenClientSetters declaration; no other behavioral
changes are required (the annotation only marks the interface and its public
methods as unstable).
In
`@oauth2/src/main/java/com/linecorp/armeria/client/auth/oauth2/OAuth2AuthorizationGrantBuilder.java`:
- Around line 166-167: Fix the Javadoc typo in OAuth2AuthorizationGrantBuilder's
preload API comment: change "This options is disabled by default..." to "This
option is disabled by default..." in the Javadoc for the preload behavior (the
comment associated with the OAuth2AuthorizationGrantBuilder/preload option).
---
Outside diff comments:
In
`@athenz/src/main/java/com/linecorp/armeria/client/athenz/AccessTokenClient.java`:
- Around line 1-15: Update the file-level copyright header in
AccessTokenClient.java to match the project's standard by replacing "LINE
Corporation" with "LY Corporation"; locate the top-of-file header in
AccessTokenClient.java (the same header pattern used in AthenzTokenClient.java)
and adjust the copyright holder string so it is consistent across both files.
In
`@athenz/src/main/java/com/linecorp/armeria/client/athenz/RoleTokenClient.java`:
- Around line 1-15: Update the file header comment in RoleTokenClient (top of
athenz/src/main/java/com/linecorp/armeria/client/athenz/RoleTokenClient.java) to
use "LY Corporation" instead of "LINE Corporation" so the copyright header
matches other files; replace all occurrences in the header block (the file-level
comment above the RoleTokenClient class) accordingly.
---
Nitpick comments:
In `@core/src/main/java/com/linecorp/armeria/common/util/AsyncLoaderBuilder.java`:
- Around line 141-145: Fix the Javadoc on AsyncLoaderBuilder: correct "This
options" to "This option" and explicitly state preload is async fire-and-forget
by saying that when preload is enabled the loader is invoked asynchronously
during build() and build() does not wait for completion; retain that preload is
disabled by default and that, otherwise, the value is loaded lazily when
AsyncLoader#load() is called for the first time.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: a3df7f51-494e-435f-98f4-aeeb2e95b72b
📒 Files selected for processing (16)
athenz/src/main/java/com/linecorp/armeria/client/athenz/AccessTokenClient.javaathenz/src/main/java/com/linecorp/armeria/client/athenz/AthenzClient.javaathenz/src/main/java/com/linecorp/armeria/client/athenz/AthenzClientBuilder.javaathenz/src/main/java/com/linecorp/armeria/client/athenz/AthenzTokenClient.javaathenz/src/main/java/com/linecorp/armeria/client/athenz/AthenzTokenClientBuilder.javaathenz/src/main/java/com/linecorp/armeria/client/athenz/RoleTokenClient.javaathenz/src/main/java/com/linecorp/armeria/client/athenz/TokenClient.javaathenz/src/main/java/com/linecorp/armeria/client/athenz/TokenClientSetters.javaathenz/src/test/java/com/linecorp/armeria/client/athenz/AccessTokenClientTest.javaathenz/src/test/java/com/linecorp/armeria/client/athenz/RoleTokenClientTest.javacore/src/main/java/com/linecorp/armeria/common/util/AsyncLoaderBuilder.javacore/src/main/java/com/linecorp/armeria/common/util/DefaultAsyncLoader.javacore/src/test/java/com/linecorp/armeria/common/util/DefaultAsyncLoaderTest.javaoauth2/src/main/java/com/linecorp/armeria/client/auth/oauth2/DefaultOAuth2AuthorizationGrant.javaoauth2/src/main/java/com/linecorp/armeria/client/auth/oauth2/OAuth2AuthorizationGrantBuilder.javaoauth2/src/test/java/com/linecorp/armeria/client/auth/oauth2/OAuth2ClientCredentialsGrantTest.java
💤 Files with no reviewable changes (1)
- athenz/src/main/java/com/linecorp/armeria/client/athenz/TokenClient.java
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## main #6691 +/- ##
============================================
- Coverage 74.46% 73.94% -0.53%
- Complexity 22234 24049 +1815
============================================
Files 1963 2174 +211
Lines 82437 90238 +7801
Branches 10764 11826 +1062
============================================
+ Hits 61385 66724 +5339
- Misses 15918 17914 +1996
- Partials 5134 5600 +466 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In
`@athenz/src/main/java/com/linecorp/armeria/server/athenz/AthenzPublicKeyProvider.java`:
- Around line 75-78: The init() method in AthenzPublicKeyProvider currently
calls ztsKeyLoader.load().join() and zmsKeyLoader.load().join() which can block
indefinitely; replace these unbounded joins with bounded waits (e.g., use
CompletableFuture.get(timeout, TimeUnit.SECONDS) or
CompletableFuture.orTimeout(...)) for both ztsKeyLoader.load() and
zmsKeyLoader.load(), choose a sensible timeout (e.g., 5–15s), and handle
InterruptedException, ExecutionException and TimeoutException by logging (via
the class logger) and either proceeding safely or failing fast with a
descriptive exception so authorizer initialization cannot hang forever. Ensure
you update the init() method and use the symbols ztsKeyLoader, zmsKeyLoader, and
their load() calls when making the change.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 8478da04-dc77-44d7-a6cb-c4617cadd568
📒 Files selected for processing (2)
athenz/src/main/java/com/linecorp/armeria/server/athenz/AbstractAthenzAuthorizerBuilder.javaathenz/src/main/java/com/linecorp/armeria/server/athenz/AthenzPublicKeyProvider.java
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
athenz/src/main/java/com/linecorp/armeria/client/athenz/RoleTokenClient.java (1)
95-108:⚠️ Potential issue | 🔴 CriticalNon-forbidden failures can be masked by a
NullPointerExceptionAt Line 96-107, when
cause != nullbut status is notFORBIDDEN, the code falls through toresponse.content(). In exceptional completion,responsecan benull, which replaces the original failure with an NPE.Suggested fix
+import java.util.concurrent.CompletionException; ... return preparation .asJson(RoleToken.class) .execute() .handle((response, cause) -> { if (cause != null) { cause = Exceptions.peel(cause); if (cause instanceof InvalidHttpResponseException) { final InvalidHttpResponseException exception = (InvalidHttpResponseException) cause; if (exception.response().status() == HttpStatus.FORBIDDEN) { throw new AccessDeniedException( "Failed to obtain an Athenz role token. (domain: " + domainName + ", roles: " + roleNamesString + ')', exception); } } + throw new CompletionException(cause); } return response.content(); });🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@athenz/src/main/java/com/linecorp/armeria/client/athenz/RoleTokenClient.java` around lines 95 - 108, The handler in RoleTokenClient's .handle((response, cause) -> ...) accesses response.content() even when cause != null, which can be null and cause an NPE; change the control flow so that after peeling the cause and handling the FORBIDDEN case (InvalidHttpResponseException -> throw AccessDeniedException), you rethrow or propagate the original cause (e.g., rethrow the peeled throwable or wrap it in a CompletionException) instead of falling through to response.content(); only call response.content() when cause == null to avoid masking the original failure.
🧹 Nitpick comments (1)
athenz/src/main/java/com/linecorp/armeria/client/athenz/RoleTokenClient.java (1)
52-53: Defensively snapshotroleNamesto keep internal state consistentAt Line 52-53 and Line 70-71, this class stores and returns the incoming list reference directly. If a mutable list is ever passed by an in-package caller,
roleNames()can diverge fromroleNamesString(used for requests/errors). Snapshotting once avoids this class invariant risk.Suggested refactor
+import com.google.common.collect.ImmutableList; ... - this.roleNames = roleNames; - roleNamesString = ROLE_JOINER.join(roleNames); + this.roleNames = ImmutableList.copyOf(roleNames); + roleNamesString = ROLE_JOINER.join(this.roleNames);Also applies to: 70-71
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@athenz/src/main/java/com/linecorp/armeria/client/athenz/RoleTokenClient.java` around lines 52 - 53, The class stores and returns the incoming List reference directly causing a potential mismatch between the mutable roleNames and roleNamesString; defensively snapshot the incoming list in the constructor (e.g., replace assigning roleNames = roleNames with an immutable or new ArrayList copy) and ensure the roleNames() accessor returns an unmodifiable/immutable view or the same snapshot (not the original mutable reference) so roleNamesString and roleNames remain consistent; update the constructor and the roleNames() method (referencing fields roleNames and roleNamesString) to use the defensive copy.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Outside diff comments:
In
`@athenz/src/main/java/com/linecorp/armeria/client/athenz/RoleTokenClient.java`:
- Around line 95-108: The handler in RoleTokenClient's .handle((response, cause)
-> ...) accesses response.content() even when cause != null, which can be null
and cause an NPE; change the control flow so that after peeling the cause and
handling the FORBIDDEN case (InvalidHttpResponseException -> throw
AccessDeniedException), you rethrow or propagate the original cause (e.g.,
rethrow the peeled throwable or wrap it in a CompletionException) instead of
falling through to response.content(); only call response.content() when cause
== null to avoid masking the original failure.
---
Nitpick comments:
In
`@athenz/src/main/java/com/linecorp/armeria/client/athenz/RoleTokenClient.java`:
- Around line 52-53: The class stores and returns the incoming List reference
directly causing a potential mismatch between the mutable roleNames and
roleNamesString; defensively snapshot the incoming list in the constructor
(e.g., replace assigning roleNames = roleNames with an immutable or new
ArrayList copy) and ensure the roleNames() accessor returns an
unmodifiable/immutable view or the same snapshot (not the original mutable
reference) so roleNamesString and roleNames remain consistent; update the
constructor and the roleNames() method (referencing fields roleNames and
roleNamesString) to use the defensive copy.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: f7f6eeee-17b4-442d-ac89-abbd507ae1e3
📒 Files selected for processing (4)
athenz/src/main/java/com/linecorp/armeria/client/athenz/AthenzTokenClient.javaathenz/src/main/java/com/linecorp/armeria/client/athenz/RoleTokenClient.javaathenz/src/main/java/com/linecorp/armeria/client/athenz/TokenClientSetters.javaoauth2/src/main/java/com/linecorp/armeria/client/auth/oauth2/OAuth2AuthorizationGrantBuilder.java
✅ Files skipped from review due to trivial changes (1)
- athenz/src/main/java/com/linecorp/armeria/client/athenz/AthenzTokenClient.java
🚧 Files skipped from review as they are similar to previous changes (1)
- athenz/src/main/java/com/linecorp/armeria/client/athenz/TokenClientSetters.java
There was a problem hiding this comment.
♻️ Duplicate comments (1)
athenz/src/main/java/com/linecorp/armeria/server/athenz/AthenzPublicKeyProvider.java (1)
81-83:⚠️ Potential issue | 🟠 MajorRestore thread interrupt status when handling
InterruptedException.Lines 81 and 86 catch
InterruptedExceptionand swallow it intoIllegalStateExceptionwithout restoring the interrupt flag viaThread.currentThread().interrupt(). This breaks cooperative cancellation on caller threads.Proposed fix
void init() { try { ztsKeyLoader.load().get(30, TimeUnit.SECONDS); - } catch (InterruptedException | ExecutionException | TimeoutException e) { + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + throw new IllegalStateException("Interrupted while loading ZTS keys", e); + } catch (ExecutionException | TimeoutException e) { throw new IllegalStateException("Failed to load ZTS keys in 30 seconds", e); } try { zmsKeyLoader.load().get(30, TimeUnit.SECONDS); - } catch (InterruptedException | ExecutionException | TimeoutException e) { + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + throw new IllegalStateException("Interrupted while loading ZMS keys", e); + } catch (ExecutionException | TimeoutException e) { throw new IllegalStateException("Failed to load ZMS keys in 30 seconds", e); } }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@athenz/src/main/java/com/linecorp/armeria/server/athenz/AthenzPublicKeyProvider.java` around lines 81 - 83, The catch block in AthenzPublicKeyProvider that currently catches InterruptedException | ExecutionException | TimeoutException and throws new IllegalStateException("Failed to load ZTS keys in 30 seconds", e) swallows the interrupt; restore the thread interrupt status before rethrowing. Modify the error handling in the method that loads ZTS keys (the catch handling InterruptedException | ExecutionException | TimeoutException) so that either you split out a dedicated catch(InterruptedException ie) { Thread.currentThread().interrupt(); throw new IllegalStateException("Failed to load ZTS keys in 30 seconds", ie); } before the other catches, or keep the multi-catch but check (e instanceof InterruptedException) and call Thread.currentThread().interrupt() prior to throwing the IllegalStateException.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Duplicate comments:
In
`@athenz/src/main/java/com/linecorp/armeria/server/athenz/AthenzPublicKeyProvider.java`:
- Around line 81-83: The catch block in AthenzPublicKeyProvider that currently
catches InterruptedException | ExecutionException | TimeoutException and throws
new IllegalStateException("Failed to load ZTS keys in 30 seconds", e) swallows
the interrupt; restore the thread interrupt status before rethrowing. Modify the
error handling in the method that loads ZTS keys (the catch handling
InterruptedException | ExecutionException | TimeoutException) so that either you
split out a dedicated catch(InterruptedException ie) {
Thread.currentThread().interrupt(); throw new IllegalStateException("Failed to
load ZTS keys in 30 seconds", ie); } before the other catches, or keep the
multi-catch but check (e instanceof InterruptedException) and call
Thread.currentThread().interrupt() prior to throwing the IllegalStateException.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 37a64615-e855-493c-8611-65725e2648bd
📒 Files selected for processing (1)
athenz/src/main/java/com/linecorp/armeria/server/athenz/AthenzPublicKeyProvider.java
| throw new IllegalStateException("Failed to load ZTS keys in 30 seconds", e); | ||
| } | ||
| try { | ||
| zmsKeyLoader.load().get(30, TimeUnit.SECONDS); |
There was a problem hiding this comment.
Better to call load() together before calling get()?
There was a problem hiding this comment.
They are already called at the end of the constructor.
There was a problem hiding this comment.
I was imagining something like:
var future1 = ztsKeyLoader.load();
var future2 = zmsKeyLoader.load();
future1.get();
future2.get();
There was a problem hiding this comment.
The loaders will work as you imagined because ztsKeyLoader.load() and zmsKeyLoader.load() return cached futures that were already triggered in the constructor.
Motivation:
Previously, Athenz token fetching was tightly coupled to the
AthenzClientHTTP decorator. Users who wanted to use Athenz tokens with non-Armeria
clients (e.g., Spring WebClient or RestTemplate) had no public API to
fetch tokens independently.
Modifications:
AthenzTokenClientpublic interface withdomainName(),roleNames(), andgetToken()methods.AthenzTokenClientBuilderfor constructing token clients withdomain, roles, refresh, and preload settings.
TokenClientSettersinterface to share setter methods betweenAthenzClientBuilderandAthenzTokenClientBuilder.AthenzClientBuilderto delegate toAthenzTokenClientBuilder.AthenzClientconstructor to accept a pre-builtAthenzTokenClientinstead of building one internally.TokenClientinterface.domainName(),roleNames(), andtoString()toAccessTokenClientandRoleTokenClient.Result:
Closes Expose Athenz
TokenClientas public API #6431.Users can now create an
AthenzTokenClientindependently: