Skip to content

Commit 161f288

Browse files
committed
decouple mTLS endpoint selection and certificate availability checks per AIP-4114, removing fail-fast exceptions when client certificates are unavailable under always policy.
1 parent 99d19a0 commit 161f288

3 files changed

Lines changed: 76 additions & 23 deletions

File tree

google-auth-library-java/oauth2_http/java/com/google/auth/mtls/MtlsUtils.java

Lines changed: 32 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -173,28 +173,47 @@ public static boolean canBeEnabled(
173173

174174
// Check if client certificate usage is allowed
175175
String useClientCertificate = envProvider.getEnv("GOOGLE_API_USE_CLIENT_CERTIFICATE");
176-
MtlsEndpointUsagePolicy policy = getMtlsEndpointUsagePolicy(envProvider);
177176
if ("false".equalsIgnoreCase(useClientCertificate)) {
178-
if (policy == MtlsEndpointUsagePolicy.ALWAYS) {
179-
throw new CertificateSourceUnavailableException(
180-
"mTLS is configured to ALWAYS, but client certificate usage was explicitly disabled via GOOGLE_API_USE_CLIENT_CERTIFICATE=false.");
181-
}
182177
return false;
183178
}
184179

180+
MtlsEndpointUsagePolicy policy = getMtlsEndpointUsagePolicy(envProvider);
185181
if (policy == MtlsEndpointUsagePolicy.NEVER) {
186182
return false;
187183
}
188184

189-
if (policy == MtlsEndpointUsagePolicy.ALWAYS) {
190-
return true;
191-
}
192-
193185
File certConfigFile =
194186
resolveCertificateConfigFile(envProvider, propProvider, certConfigPathOverride);
195187
return certConfigFile != null;
196188
}
197189

190+
/**
191+
* Returns whether the mutual TLS (mTLS) endpoint should be used.
192+
*
193+
* @param envProvider the environment provider to use for resolving environment variables
194+
* @param propProvider the property provider to use for resolving system properties
195+
* @param certConfigPathOverride optional override path for the configuration file
196+
* @return true if the mTLS endpoint should be used, false otherwise
197+
*/
198+
public static boolean shouldMtlsEndpointBeUsed(
199+
EnvironmentProvider envProvider,
200+
PropertyProvider propProvider,
201+
String certConfigPathOverride) {
202+
MtlsEndpointUsagePolicy policy = getMtlsEndpointUsagePolicy(envProvider);
203+
if (policy == MtlsEndpointUsagePolicy.ALWAYS) {
204+
return true;
205+
}
206+
if (policy == MtlsEndpointUsagePolicy.NEVER) {
207+
return false;
208+
}
209+
// policy is AUTO: use mTLS endpoint if client certificate can be enabled
210+
try {
211+
return canBeEnabled(envProvider, propProvider, certConfigPathOverride);
212+
} catch (IOException e) {
213+
return false;
214+
}
215+
}
216+
198217
/**
199218
* Resolves the mutual TLS (mTLS) certificate configuration file.
200219
*
@@ -313,7 +332,6 @@ public static HttpTransportFactory prepareTransportFactoryIfMtlsEnabled(
313332
return baseTransportFactory;
314333
}
315334

316-
MtlsEndpointUsagePolicy mtlsPolicy = getMtlsEndpointUsagePolicy(envProvider);
317335
try {
318336
// This is the default HttpTransportFactory assigned by credentials.
319337
// Automatically discover and load client certificates to construct an mTLS factory.
@@ -322,11 +340,10 @@ public static HttpTransportFactory prepareTransportFactoryIfMtlsEnabled(
322340
KeyStore mtlsKeyStore = x509Provider.getKeyStore();
323341
return new MtlsHttpTransportFactory(mtlsKeyStore);
324342
} catch (Exception e) {
325-
if (mtlsPolicy == MtlsEndpointUsagePolicy.ALWAYS) {
326-
throw new IOException(
327-
"mTLS is configured to ALWAYS, but initialization failed: " + e.getMessage(), e);
328-
}
329-
// Graceful fallback to standard transport if mTLS initialization fails under AUTO policy
343+
LOGGER.warning(
344+
"mTLS transport factory initialization failed, falling back to non-mTLS transport: "
345+
+ e.getMessage());
346+
// Graceful fallback to standard transport if mTLS initialization fails
330347
return baseTransportFactory;
331348
}
332349
}

google-auth-library-java/oauth2_http/java/com/google/auth/oauth2/RegionalAccessBoundaryManager.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -214,8 +214,7 @@ void triggerAsyncRefresh(
214214
HttpTransportFactory upgradedTransportFactory =
215215
MtlsUtils.prepareTransportFactoryIfMtlsEnabled(
216216
transportFactory, envProvider, propProvider, null);
217-
if (MtlsUtils.canBeEnabled(envProvider, propProvider, null)
218-
&& upgradedTransportFactory != OAuth2Utils.HTTP_TRANSPORT_FACTORY) {
217+
if (MtlsUtils.shouldMtlsEndpointBeUsed(envProvider, propProvider, null)) {
219218
url = url.replace(IAM_ENDPOINT, MTLS_IAM_ENDPOINT);
220219
}
221220
RegionalAccessBoundary newRAB =

google-auth-library-java/oauth2_http/javatests/com/google/auth/mtls/MtlsUtilsTest.java

Lines changed: 43 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -353,7 +353,7 @@ public String getProperty(String name, String def) {
353353
}
354354

355355
@Test
356-
void canBeEnabled_alwaysPolicy_clientCertDisabled_throwsException() {
356+
void canBeEnabled_alwaysPolicy_clientCertDisabled_returnsFalse() throws IOException {
357357
EnvironmentProvider envProvider =
358358
name -> {
359359
if ("GOOGLE_API_USE_CLIENT_CERTIFICATE".equals(name)) {
@@ -366,9 +366,8 @@ void canBeEnabled_alwaysPolicy_clientCertDisabled_throwsException() {
366366
};
367367
PropertyProvider propProvider = (name, def) -> def;
368368

369-
assertThrows(
370-
CertificateSourceUnavailableException.class,
371-
() -> MtlsUtils.canBeEnabled(envProvider, propProvider, null));
369+
assertFalse(MtlsUtils.canBeEnabled(envProvider, propProvider, null));
370+
assertTrue(MtlsUtils.shouldMtlsEndpointBeUsed(envProvider, propProvider, null));
372371
}
373372

374373
@Test
@@ -431,12 +430,13 @@ public String getProperty(String name, String def) {
431430
}
432431

433432
@Test
434-
void canBeEnabled_alwaysPolicy_returnsTrue() throws IOException {
433+
void canBeEnabled_alwaysPolicy_returnsFalse() throws IOException {
435434
EnvironmentProvider envProvider =
436435
name -> "GOOGLE_API_USE_MTLS_ENDPOINT".equals(name) ? "always" : null;
437436
PropertyProvider propProvider = (name, def) -> def;
438437

439-
assertTrue(MtlsUtils.canBeEnabled(envProvider, propProvider, null));
438+
assertFalse(MtlsUtils.canBeEnabled(envProvider, propProvider, null));
439+
assertTrue(MtlsUtils.shouldMtlsEndpointBeUsed(envProvider, propProvider, null));
440440
}
441441

442442
@Test
@@ -537,6 +537,43 @@ public String getEnv(String name) {
537537
assertTrue(result instanceof MtlsHttpTransportFactory);
538538
}
539539

540+
@Test
541+
void
542+
prepareTransportFactoryIfMtlsEnabled_defaultFactory_mtlsAlways_clientCertDisabled_returnsAsIs()
543+
throws IOException {
544+
EnvironmentProvider envProvider =
545+
name -> {
546+
if ("GOOGLE_API_USE_CLIENT_CERTIFICATE".equals(name)) {
547+
return "false";
548+
}
549+
if ("GOOGLE_API_USE_MTLS_ENDPOINT".equals(name)) {
550+
return "always";
551+
}
552+
return null;
553+
};
554+
PropertyProvider propProvider = (name, def) -> def;
555+
556+
HttpTransportFactory result =
557+
MtlsUtils.prepareTransportFactoryIfMtlsEnabled(
558+
OAuth2Utils.HTTP_TRANSPORT_FACTORY, envProvider, propProvider, null);
559+
560+
assertSame(OAuth2Utils.HTTP_TRANSPORT_FACTORY, result);
561+
}
562+
563+
@Test
564+
void prepareTransportFactoryIfMtlsEnabled_defaultFactory_mtlsAlways_missingConfig_returnsAsIs()
565+
throws IOException {
566+
EnvironmentProvider envProvider =
567+
name -> "GOOGLE_API_USE_MTLS_ENDPOINT".equals(name) ? "always" : null;
568+
PropertyProvider propProvider = (name, def) -> def;
569+
570+
HttpTransportFactory result =
571+
MtlsUtils.prepareTransportFactoryIfMtlsEnabled(
572+
OAuth2Utils.HTTP_TRANSPORT_FACTORY, envProvider, propProvider, null);
573+
574+
assertSame(OAuth2Utils.HTTP_TRANSPORT_FACTORY, result);
575+
}
576+
540577
@Test
541578
void prepareTransportFactoryIfMtlsEnabled_defaultFactory_mtlsAuto_noConfig_returnsAsIs()
542579
throws IOException {

0 commit comments

Comments
 (0)