Add TokenProvider#principal accessor with memoised lookups - #23
Merged
Conversation
Introduce a sealed `Parser.CredentialsFile` AST (with `ServiceAccount` and `AuthorizedUser` cases) returned by a single `Parser.defaultCredentialsFile[F]: F[(Path, Option[CredentialsFile])]` entry point. The dispatch on the file's `type` field is encoded in the `Decoder[CredentialsFile]` instance, so the caller only sees a parsed AST or `None`. `Parser.googleServiceAccount` now returns the `ServiceAccount` AST case directly via `parser.decode[ServiceAccount]`. The PEM-to-RSAPrivateKey transformation is exposed as `Decoder[RSAPrivateKey]` (pure — no `Sync.blocking` wrap, the work is CPU-bound). Split the old `applicationDefaultCredentials` failure modes into a missing-file case (`DefaultCredentialsFileNotFound`) and a parse-failure case (`UnableToGetDefaultCredentials`). PEM-decode failures now fold into `UnableToGetDefaultCredentials` via the decoder's catch-all. Update `TokenProvider.userAccount(httpClient)` and `userIdentity(httpClient)` to pattern-match on the AST, raising `UnsupportedCredentialsType` when handed a service-account JSON (where an authorized-user is required).
Surface the subject identifier each provider is authenticated as (service-account email, workload metadata `/email`, JWT `email`/`sub` claim) through a new abstract `principal: F[Option[String]]` on the trait. Internally route every provider through a single `Impl` class that can be rewired via `withPrincipal`, so factories assemble providers as `TokenProvider.create(fetchToken).withPrincipal(...)`. Factories that need an HTTP call to determine the principal (`identity`, `userIdentity`, `serviceAccount(client)`, and the direct `userAccount(id, secret, refresh, client)`) memoise the lookup at construction time via `Concurrent.memoize`, so each provider instance makes at most one `/email`, `userinfo`, or JWT-decode call regardless of how many times `principal` is read. `cached.build` delegates `principal` to the wrapped provider so caching the token doesn't drop the principal.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
💻 How to review this PR?
This PR was created with the idea of being reviewed commit by commit. Each commit contains an incremental change that makes it easier to review. Also some of the commits contain additional information in their description to help understand why the change was made.
I also recommend checking "Hide whitespace" when reviewing this PR!
🚀 What's included in this PR?
Adds a
principal: F[Option[String]]accessor onTokenProviderthat returns the subject identifier each provider is authenticated as (service-account email, user-account email fromuserinfo, or the JWTemailclaim for identity tokens). The lookup is memoised at construction time, so each provider instance makes at most one underlying HTTP call regardless of how many timesprincipalis read.To make room for
principal, this PR also:Parser's ADC handling into aCredentialsFileAST (ServiceAccount/AuthorizedUser) returned byParser.defaultCredentialsFile[F], replacing the oldonDefaultCredentialsFile+credentialsTypeCPS-style dispatch.googleExpecthelper onClient[F]to encapsulate the trailing-slash +Metadata-Flavor: Googleboilerplate.Implclass forTokenProvider.createsoprincipaland itswithPrincipaloverrides have a place to live.identity,userIdentity,serviceAccount(client),userAccount(triple)).versionPolicyIntentiontoCompatibility.Noneto acknowledge the breaking changes to theTokenProvidertrait.