Repository navigation
feat(oauth2): support non-mTLS token URLs and unbound actor tokens in IdentityPoolCredentials #14430
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat(oauth2): support non-mTLS token URLs and unbound actor tokens in IdentityPoolCredentials #14430
Changes from 3 commits
8e5f005
5b01057
82254db
961361b
78790db
3e40f0a
23fc045
928659b
e5280d8
abfb97c
d12c7ec
8a2ac3f
96423b4
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -40,7 +40,6 @@ | |
| import com.google.errorprone.annotations.CanIgnoreReturnValue; | ||
| import java.io.IOException; | ||
| import java.io.ObjectInputStream; | ||
| import java.net.URI; | ||
| import java.security.KeyStore; | ||
| import java.util.ArrayList; | ||
| import java.util.Collection; | ||
|
|
@@ -53,9 +52,9 @@ | |
| * | ||
| * <p>By default, attempts to exchange the external credential for a GCP access token. | ||
| * | ||
| * <p>Note: Actor token extraction is currently restricted to file-based JSON credential sources | ||
| * over mTLS endpoints. When configuring certificate-bound OAuth 2.0 tokens, ensure your transport | ||
| * layer is configured for mTLS in tandem. | ||
| * <p>Note: Actor token extraction from credential configuration files is currently restricted to | ||
| * file-based JSON credential sources. When configuring certificate-bound OAuth 2.0 tokens, ensure | ||
| * your transport layer and endpoints are configured for mTLS in tandem. | ||
| */ | ||
| @NullMarked | ||
| public class IdentityPoolCredentials extends ExternalAccountCredentials { | ||
|
|
@@ -178,54 +177,6 @@ public class IdentityPoolCredentials extends ExternalAccountCredentials { | |
| throw new IllegalArgumentException( | ||
| "An actorTokenSupplier must be specified when an actorTokenType is configured."); | ||
| } | ||
|
|
||
| if (this.actorTokenSupplier != null && !isMtlsConfigured()) { | ||
| throw new IllegalArgumentException( | ||
| "Actor tokens are only supported for mTLS token exchanges. Please configure a certificate" | ||
| + " source or MtlsHttpTransportFactory."); | ||
| } | ||
|
|
||
| if (this.actorTokenSupplier != null) { | ||
| validateMtlsEndpoint(getTokenUrl(), "tokenUrl"); | ||
| if (getServiceAccountImpersonationUrl() != null) { | ||
| validateMtlsEndpoint(getServiceAccountImpersonationUrl(), "serviceAccountImpersonationUrl"); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| private static void validateMtlsEndpoint(@Nullable String url, String fieldName) { | ||
| if (url == null) { | ||
| return; | ||
| } | ||
| String host = URI.create(url).getHost(); | ||
| // For Google Default Universe (googleapis.com), actor tokens require mTLS binding so plain | ||
| // public endpoints (lacking '.mtls.' or Private Service Connect '.p.') are rejected early. | ||
| // Non-GDU domains (e.g. custom universes or TPC) may use different hostname conventions. | ||
| if (host != null | ||
| && host.endsWith("googleapis.com") | ||
| && !host.contains(".mtls.") | ||
| && !host.contains(".p.")) { | ||
| throw new IllegalArgumentException( | ||
| "The " | ||
| + fieldName | ||
| + " endpoint (" | ||
| + url | ||
| + ") cannot be used with actor tokens because it is a plain public Google API" | ||
| + " endpoint. Please use an mTLS endpoint (e.g. containing '.mtls.') or Private" | ||
| + " Service Connect (containing '.p.')."); | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * Checks whether mTLS is properly configured by verifying that an X509Provider is set or the | ||
| * transport factory is an MtlsHttpTransportFactory with a non-null KeyStore. This avoids false | ||
| * positives from a no-arg-constructed MtlsHttpTransportFactory (e.g. after deserialization) that | ||
| * has no actual certificates. | ||
| */ | ||
| private boolean isMtlsConfigured() { | ||
| return this.x509Provider != null | ||
| || (this.transportFactory instanceof MtlsHttpTransportFactory | ||
| && ((MtlsHttpTransportFactory) this.transportFactory).hasKeyStore()); | ||
| } | ||
|
|
||
| @Override | ||
|
|
@@ -479,12 +430,11 @@ public Builder setSubjectTokenSupplier(IdentityPoolSubjectTokenSupplier subjectT | |
| } | ||
|
|
||
| /** | ||
| * Sets the actor token supplier used for certificate-bound OAuth 2.0 token exchanges. The | ||
| * supplier provides an actor token representing the entity on whose behalf the subject is | ||
| * acting. | ||
| * Sets the actor token supplier used for OAuth 2.0 token exchanges. The supplier provides an | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In RFC 8693 Section 2.1, the actor token represents the acting party that acts on behalf of the subject rather than the entity on whose behalf the subject is acting. Should we update this Javadoc wording to match?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. updated the Javadoc to match RFC 8693 Section 2.1 terminology |
||
| * actor token representing the entity on whose behalf the subject is acting. | ||
| * | ||
| * <p>An actor token supplier must be paired with an {@link #setActorTokenType actor token type} | ||
| * and requires an mTLS-configured transport. | ||
| * <p>An actor token supplier must be paired with an {@link #setActorTokenType actor token | ||
| * type}. | ||
| * | ||
| * @param actorTokenSupplier the supplier to use for retrieving actor tokens | ||
| * @return this {@code Builder} object | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Now that
isMtlsConfigured()is removed here,MtlsHttpTransportFactory.hasKeyStore()andcheckHasKeyStore(KeyStore)have no production callers left in the repo, butnew MtlsHttpTransportFactory(keyStore)still scanskeyStore.aliases()and certificate chains on every refresh and 401 retry. Should we removehasKeyStore()and the duplicatemtlsHttpTransportFactory_hasKeyStore_*tests inIdentityPoolCredentialsTest?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Removed the 3 duplicate
mtlsHttpTransportFactory_hasKeyStore_*tests fromIdentityPoolCredentialsTestsince they are already covered inMtlsHttpTransportFactoryTest. Note, however, that I keptMtlsHttpTransportFactory.hasKeyStore()itself because PR #14212 actively calls((MtlsHttpTransportFactory) this.transportFactory).hasKeyStore()inIdentityPoolCredentials.shouldUseMtlsTransportFactory()and
readObject().