Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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 {
Expand Down Expand Up @@ -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());
}

Copy link
Copy Markdown
Contributor

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() and checkHasKeyStore(KeyStore) have no production callers left in the repo, but new MtlsHttpTransportFactory(keyStore) still scans keyStore.aliases() and certificate chains on every refresh and 401 retry. Should we remove hasKeyStore() and the duplicate mtlsHttpTransportFactory_hasKeyStore_* tests in IdentityPoolCredentialsTest?

Copy link
Copy Markdown
Contributor Author

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 from IdentityPoolCredentialsTest since they are already covered in MtlsHttpTransportFactoryTest. Note, however, that I kept MtlsHttpTransportFactory.hasKeyStore() itself because PR #14212 actively calls ((MtlsHttpTransportFactory) this.transportFactory).hasKeyStore() in IdentityPoolCredentials.shouldUseMtlsTransportFactory()
and readObject().


@Override
Expand Down Expand Up @@ -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

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The 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?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The 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
Expand Down
Loading
Loading