@@ -141,6 +141,7 @@ public final class InstantiatingGrpcChannelProvider implements TransportChannelP
141141 @ Nullable private final Boolean keepAliveWithoutCalls ;
142142 private final ChannelPoolSettings channelPoolSettings ;
143143 @ Nullable private final Credentials credentials ;
144+ @ Nullable private final CallCredentials altsCallCredentials ;
144145 @ Nullable private final CallCredentials mtlsS2ACallCredentials ;
145146 @ Nullable private final ChannelPrimer channelPrimer ;
146147 @ Nullable private final Boolean attemptDirectPath ;
@@ -191,6 +192,7 @@ private InstantiatingGrpcChannelProvider(Builder builder) {
191192 this .channelPoolSettings = builder .channelPoolSettings ;
192193 this .channelConfigurator = builder .channelConfigurator ;
193194 this .credentials = builder .credentials ;
195+ this .altsCallCredentials = builder .altsCallCredentials ;
194196 this .mtlsS2ACallCredentials = builder .mtlsS2ACallCredentials ;
195197 this .channelPrimer = builder .channelPrimer ;
196198 this .attemptDirectPath = builder .attemptDirectPath ;
@@ -616,8 +618,14 @@ private ManagedChannel createSingleChannel() throws IOException {
616618 boolean useDirectPathXds = false ;
617619 if (canUseDirectPath ()) {
618620 CallCredentials callCreds = MoreCallCredentials .from (credentials );
621+ // altsCallCredentials may be null and GoogleDefaultChannelCredentials
622+ // will solely use callCreds. Otherwise it uses altsCallCredentials
623+ // for DirectPath connections and callCreds for CloudPath fallbacks.
619624 ChannelCredentials channelCreds =
620- GoogleDefaultChannelCredentials .newBuilder ().callCredentials (callCreds ).build ();
625+ GoogleDefaultChannelCredentials .newBuilder ()
626+ .callCredentials (callCreds )
627+ .altsCallCredentials (altsCallCredentials )
628+ .build ();
621629 useDirectPathXds = isDirectPathXdsEnabled ();
622630 if (useDirectPathXds ) {
623631 // google-c2p: CloudToProd(C2P) Directpath. This scheme is defined in
@@ -822,6 +830,7 @@ public static final class Builder {
822830 @ Nullable private Boolean keepAliveWithoutCalls ;
823831 @ Nullable private ApiFunction <ManagedChannelBuilder , ManagedChannelBuilder > channelConfigurator ;
824832 @ Nullable private Credentials credentials ;
833+ @ Nullable private CallCredentials altsCallCredentials ;
825834 @ Nullable private CallCredentials mtlsS2ACallCredentials ;
826835 @ Nullable private ChannelPrimer channelPrimer ;
827836 private ChannelPoolSettings channelPoolSettings ;
@@ -853,6 +862,7 @@ private Builder(InstantiatingGrpcChannelProvider provider) {
853862 this .keepAliveWithoutCalls = provider .keepAliveWithoutCalls ;
854863 this .channelConfigurator = provider .channelConfigurator ;
855864 this .credentials = provider .credentials ;
865+ this .altsCallCredentials = provider .altsCallCredentials ;
856866 this .mtlsS2ACallCredentials = provider .mtlsS2ACallCredentials ;
857867 this .channelPrimer = provider .channelPrimer ;
858868 this .channelPoolSettings = provider .channelPoolSettings ;
@@ -919,6 +929,7 @@ Builder setUseS2A(boolean useS2A) {
919929 this .useS2A = useS2A ;
920930 return this ;
921931 }
932+
922933 /*
923934 * Sets the allowed hard bound token types for this TransportChannelProvider.
924935 *
@@ -996,6 +1007,7 @@ public Integer getMaxInboundMetadataSize() {
9961007 public Builder setKeepAliveTime (org .threeten .bp .Duration duration ) {
9971008 return setKeepAliveTimeDuration (toJavaTimeDuration (duration ));
9981009 }
1010+
9991011 /** The time without read activity before sending a keepalive ping. */
10001012 public Builder setKeepAliveTimeDuration (java .time .Duration duration ) {
10011013 this .keepAliveTime = duration ;
@@ -1172,6 +1184,18 @@ boolean isMtlsS2AHardBoundTokensEnabled() {
11721184 .anyMatch (val -> val .equals (HardBoundTokenTypes .MTLS_S2A ));
11731185 }
11741186
1187+ boolean isDirectPathBoundTokenEnabled () {
1188+ // If the list of allowed hard bound token types is empty or doesn't contain
1189+ // {@code HardBoundTokenTypes.ALTS}, the {@code credentials} are null or not of type
1190+ // {@code ComputeEngineCredentials} then DirectPath hard bound tokens should not be used.
1191+ // DirectPath hard bound tokens should only be used on ALTS channels.
1192+ if (allowedHardBoundTokenTypes .isEmpty ()
1193+ || this .credentials == null
1194+ || !(credentials instanceof ComputeEngineCredentials )) return false ;
1195+ return allowedHardBoundTokenTypes .stream ()
1196+ .anyMatch (val -> val .equals (HardBoundTokenTypes .ALTS ));
1197+ }
1198+
11751199 CallCredentials createHardBoundTokensCallCredentials (
11761200 ComputeEngineCredentials .GoogleAuthTransport googleAuthTransport ,
11771201 ComputeEngineCredentials .BindingEnforcement bindingEnforcement ) {
@@ -1194,6 +1218,11 @@ public InstantiatingGrpcChannelProvider build() {
11941218 ComputeEngineCredentials .GoogleAuthTransport .MTLS ,
11951219 ComputeEngineCredentials .BindingEnforcement .ON );
11961220 }
1221+ if (isDirectPathBoundTokenEnabled ()) {
1222+ this .altsCallCredentials =
1223+ createHardBoundTokensCallCredentials (
1224+ ComputeEngineCredentials .GoogleAuthTransport .ALTS , null );
1225+ }
11971226 InstantiatingGrpcChannelProvider instantiatingGrpcChannelProvider =
11981227 new InstantiatingGrpcChannelProvider (this );
11991228 instantiatingGrpcChannelProvider .removeApiKeyCredentialDuplicateHeaders ();
0 commit comments