Skip to content

Commit 1fcbaa2

Browse files
committed
fix(auth): handle missing APPDATA on Windows gracefully
Other languages (such as Python and Node.js) handle missing HOME or APPDATA directories by falling back gracefully or failing with a structured Error/Exception. This change brings Java into parity by ensuring an orderly IOException is explicitly thrown by getWellKnownCredentialsFile instead of triggering an opaque NullPointerException when APPDATA is null. Fixes #12565
1 parent 051aea7 commit 1fcbaa2

4 files changed

Lines changed: 31 additions & 5 deletions

File tree

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -239,7 +239,7 @@ private final GoogleCredentials getDefaultCredentialsUnsynchronized(
239239
return credentials;
240240
}
241241

242-
private final File getWellKnownCredentialsFile() {
242+
private final File getWellKnownCredentialsFile() throws IOException {
243243
return GoogleAuthUtils.getWellKnownCredentialsFile(this);
244244
}
245245

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

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
package com.google.auth.oauth2;
3333

3434
import java.io.File;
35+
import java.io.IOException;
3536

3637
/**
3738
* This public class provides shared utilities for common OAuth2 utils or ADC. It also exposes
@@ -45,7 +46,7 @@ public class GoogleAuthUtils {
4546
* @return the path to the well-known Application Default Credentials file location
4647
*/
4748
public static final String getWellKnownCredentialsPath() {
48-
return getWellKnownCredentialsFile(DefaultCredentialsProvider.DEFAULT).getAbsolutePath();
49+
return getWellKnownCredentialsPath(DefaultCredentialsProvider.DEFAULT);
4950
}
5051

5152
/**
@@ -54,7 +55,11 @@ public static final String getWellKnownCredentialsPath() {
5455
* @return the path to the well-known Application Default Credentials file location
5556
*/
5657
static final String getWellKnownCredentialsPath(DefaultCredentialsProvider provider) {
57-
return getWellKnownCredentialsFile(provider).getAbsolutePath();
58+
try {
59+
return getWellKnownCredentialsFile(provider).getAbsolutePath();
60+
} catch (IOException e) {
61+
throw new RuntimeException(e);
62+
}
5863
}
5964

6065
/**
@@ -64,13 +69,17 @@ static final String getWellKnownCredentialsPath(DefaultCredentialsProvider provi
6469
* purposes)
6570
* @return the well-known Application Default Credentials file
6671
*/
67-
static final File getWellKnownCredentialsFile(DefaultCredentialsProvider provider) {
72+
static final File getWellKnownCredentialsFile(DefaultCredentialsProvider provider) throws IOException {
6873
File cloudConfigPath;
6974
String envPath = provider.getEnv("CLOUDSDK_CONFIG");
7075
if (envPath != null) {
7176
cloudConfigPath = new File(envPath);
7277
} else if (provider.getOsName().indexOf("windows") >= 0) {
73-
File appDataPath = new File(provider.getEnv("APPDATA"));
78+
String appData = provider.getEnv("APPDATA");
79+
if (appData == null) {
80+
throw new IOException(DefaultCredentialsProvider.CLOUDSDK_MISSING_CREDENTIALS);
81+
}
82+
File appDataPath = new File(appData);
7483
cloudConfigPath = new File(appDataPath, provider.CLOUDSDK_CONFIG_DIRECTORY);
7584
} else {
7685
File configPath = new File(provider.getProperty("user.home", ""), ".config");

google-auth-library-java/oauth2_http/javatests/com/google/auth/oauth2/DefaultCredentialsProviderTest.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,21 @@ void getDefaultCredentials_noCredentials_throws() {
115115
assertEquals(DefaultCredentialsProvider.CLOUDSDK_MISSING_CREDENTIALS, message);
116116
}
117117

118+
@Test
119+
void getDefaultCredentials_windowsMissingAppData_throws() {
120+
// When APPDATA is unset on Windows, the ADC resolution should fail gracefully
121+
// with a structured missing credentials exception, rather than crashing with an NPE.
122+
// This matches the expected behavior seen in Node.js and Python implementations.
123+
MockHttpTransportFactory transportFactory = new MockHttpTransportFactory();
124+
TestDefaultCredentialsProvider testProvider = new TestDefaultCredentialsProvider();
125+
testProvider.setProperty("os.name", "windows");
126+
testProvider.setEnv("APPDATA", null);
127+
128+
IOException e =
129+
assertThrows(IOException.class, () -> testProvider.getDefaultCredentials(transportFactory));
130+
assertEquals(DefaultCredentialsProvider.CLOUDSDK_MISSING_CREDENTIALS, e.getMessage());
131+
}
132+
118133
@Test
119134
void getDefaultCredentials_noCredentialsSandbox_throwsNonSecurity() {
120135
MockHttpTransportFactory transportFactory = new MockHttpTransportFactory();

google-auth-library-java/oauth2_http/javatests/com/google/auth/oauth2/GoogleAuthUtilsTest.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@
3737
import java.io.File;
3838
import org.junit.jupiter.api.Test;
3939

40+
import java.io.IOException;
41+
4042
class GoogleAuthUtilsTest {
4143

4244
@Test

0 commit comments

Comments
 (0)