Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
59 commits
Select commit Hold shift + click to select a range
dc5b79c
fix: allow for ES algorithm in GdchCredentials
diegomarquezp Mar 4, 2026
0c5e423
test: partially adapt tests
diegomarquezp Mar 4, 2026
5851b3e
test: finish adjusting tests
diegomarquezp Mar 4, 2026
258ac9d
chore: format
diegomarquezp Mar 4, 2026
e8511bb
fix: restore credential name
diegomarquezp Mar 4, 2026
a4d0f93
docs: restore license
diegomarquezp Mar 4, 2026
e3d5302
fix: restore removed code
diegomarquezp Mar 4, 2026
e6635c6
test: increase coverage
diegomarquezp Mar 5, 2026
cc49dba
test(gdch): parameterize test
diegomarquezp Mar 10, 2026
f58f593
test: remove unused var
diegomarquezp Mar 10, 2026
42f2662
chore: remove unused throw clause
diegomarquezp Mar 10, 2026
91a3751
test: parameterize more
diegomarquezp Mar 10, 2026
b83a511
fix: remove unused parameter
diegomarquezp Mar 10, 2026
625741e
fix: make variables final as intended
diegomarquezp Mar 10, 2026
1e0a7c3
fix: remove unused throw clause
diegomarquezp Mar 10, 2026
24cd25d
fix: remove unused throw clause
diegomarquezp Mar 10, 2026
027a242
fix: make OAuth2Credentials clock package private for production code
diegomarquezp Mar 10, 2026
a8b459b
fix: use non deprecated base 64 encoder
diegomarquezp Mar 10, 2026
4e8af24
test: parameterize flagged tests
diegomarquezp Mar 10, 2026
f257a81
chore: format
diegomarquezp Mar 10, 2026
c523df2
test: fix assertion
diegomarquezp Mar 10, 2026
ed026a4
build: remove unused dependency
diegomarquezp Mar 10, 2026
1528e76
test: run linux gce only on linux envs
diegomarquezp Mar 10, 2026
7300e59
fix: sonarqube flags (use java.util.Base64)
diegomarquezp Mar 10, 2026
382d8ae
fix: improve error message template
diegomarquezp Mar 16, 2026
c348ee1
fix: keep overload of "with audience" that takes an URI
diegomarquezp Mar 16, 2026
6323455
fix: restore public getter of getApiAudience
diegomarquezp Mar 16, 2026
1aa7715
docs: add javadoc for signing logic
diegomarquezp Mar 17, 2026
4f69872
test: test private signature and decode methods
diegomarquezp Mar 18, 2026
f4b53ee
fix: add null and empty check for audience string
diegomarquezp Mar 18, 2026
b3085a2
docs: add javadoc for audience getters
diegomarquezp Mar 18, 2026
39ef931
fix: use enum for possible algorithms
diegomarquezp Mar 18, 2026
3003076
fix: use obsolete javadoc instead of @deprecated
diegomarquezp Mar 18, 2026
36b7fa4
refactor: use OAuth2Utils validate methods in GdchCredentials
diegomarquezp Mar 20, 2026
6805756
fix: restore GoogleAuthException throwing in GdchCredentials
diegomarquezp Mar 23, 2026
212cb89
refactor: downgrade Pkcs8Algorithm and privateKeyFromPkcs8 to package…
diegomarquezp Mar 23, 2026
8f23285
refactor: split parseBody into parseJson and parseQuery in test utili…
diegomarquezp Mar 23, 2026
90fe11d
refactor: remove validation reflection by making signUsingEsSha256 pa…
diegomarquezp Mar 23, 2026
dcd97cb
test: use hardcoded string literal for gdch api audience in test
diegomarquezp Mar 24, 2026
1c5e7bb
test: refactor to use assertThrows in GdchCredentialsTest and remove …
diegomarquezp Mar 24, 2026
571c9ac
fix: add comment about EC algorithm support in GdchCredentials
diegomarquezp Mar 24, 2026
6d713fe
fix: update GDCH audience error message to be more descriptive
diegomarquezp Mar 24, 2026
d005709
refactor: rename getApiAudienceString to getGdchAudience
diegomarquezp Mar 24, 2026
0aa9ab9
fix: Remove unused import
diegomarquezp Mar 24, 2026
3fe0ca5
docs: update GDCH audience getter javadocs
diegomarquezp Mar 24, 2026
06982d4
test: add null-checks to builder and corresponding tests
diegomarquezp Mar 24, 2026
bd7af8d
refactor: consolidate token type constants using OAuth2Utils
diegomarquezp Mar 24, 2026
583378e
refactor: throw GoogleAuthException for signing and transcoding errors
diegomarquezp Mar 24, 2026
5f59678
docs: add javadoc to related test utils
diegomarquezp Mar 24, 2026
8fefc7b
fix: use GoogleAuthException
diegomarquezp Mar 24, 2026
fa51f57
test: use assertThrows where applicable
diegomarquezp Mar 24, 2026
f0873d3
refactor: replace Preconditions with Strings.isNullOrEmpty for audien…
diegomarquezp Mar 24, 2026
66f8342
fix: consistent exception message
diegomarquezp Mar 24, 2026
5ec685d
Merge origin/main and resolve conflicts
diegomarquezp Mar 24, 2026
4c75b1d
chore: format
diegomarquezp Mar 24, 2026
6980520
test: use lowercase os name
diegomarquezp Mar 24, 2026
d0547e3
chore: address review comments for PR #1896
diegomarquezp Mar 26, 2026
00ff824
chore: format
diegomarquezp Mar 26, 2026
a57def2
Finalizing GDCH credentials support by addressing reviewer comments
diegomarquezp Mar 26, 2026
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
274 changes: 242 additions & 32 deletions oauth2_http/java/com/google/auth/oauth2/GdchCredentials.java

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ public class OAuth2Credentials extends Credentials {
// Change listeners are not serialized
private transient List<CredentialsChangedListener> changeListeners;
// Until we expose this to the users it can remain transient and non-serializable
@VisibleForTesting transient Clock clock = Clock.SYSTEM;
transient Clock clock = Clock.SYSTEM;

/**
* Returns the credentials instance from the given access token.
Expand Down
22 changes: 20 additions & 2 deletions oauth2_http/java/com/google/auth/oauth2/OAuth2Utils.java
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,6 @@
import com.google.api.client.json.gson.GsonFactory;
import com.google.api.client.util.PemReader;
import com.google.api.client.util.PemReader.Section;
import com.google.api.client.util.SecurityUtils;
import com.google.api.core.InternalApi;
import com.google.auth.http.AuthHttpConstants;
import com.google.auth.http.HttpTransportFactory;
Expand Down Expand Up @@ -82,6 +81,11 @@
@InternalApi
public class OAuth2Utils {

enum Pkcs8Algorithm {
RSA,
EC
}

static final String SIGNATURE_ALGORITHM = "SHA256withRSA";

public static final String TOKEN_TYPE_ACCESS_TOKEN =
Expand Down Expand Up @@ -269,6 +273,20 @@ static Map<String, Object> validateMap(Map<String, Object> map, String key, Stri
* key creation.
*/
public static PrivateKey privateKeyFromPkcs8(String privateKeyPkcs8) throws IOException {
return privateKeyFromPkcs8(privateKeyPkcs8, Pkcs8Algorithm.RSA);
}

/**
* Converts a PKCS#8 string to a private key of the specified algorithm.
*
* @param privateKeyPkcs8 the PKCS#8 string.
* @param algorithm the algorithm of the private key.
* @return the private key.
* @throws IOException if the PKCS#8 data is invalid or if an unexpected exception occurs during
* key creation.
*/
public static PrivateKey privateKeyFromPkcs8(String privateKeyPkcs8, Pkcs8Algorithm algorithm)
throws IOException {
Reader reader = new StringReader(privateKeyPkcs8);
Section section = PemReader.readFirstSectionAndClose(reader, "PRIVATE KEY");
if (section == null) {
Expand All @@ -278,7 +296,7 @@ public static PrivateKey privateKeyFromPkcs8(String privateKeyPkcs8) throws IOEx
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(bytes);
Exception unexpectedException;
try {
KeyFactory keyFactory = SecurityUtils.getRsaKeyFactory();
KeyFactory keyFactory = KeyFactory.getInstance(algorithm.toString());
return keyFactory.generatePrivate(keySpec);
} catch (NoSuchAlgorithmException | InvalidKeySpecException exception) {
unexpectedException = exception;
Expand Down
24 changes: 24 additions & 0 deletions oauth2_http/javatests/com/google/auth/TestUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,13 @@ public static InputStream stringToInputStream(String text) {
}
}

/**
* Parses a URI query string into a map of key-value pairs.
*
* @param query The URI query string (e.g., "key1=val1&key2=val2").
* @return A map of decoded keys to decoded values.
* @throws IOException If the query string is malformed.
*/
public static Map<String, String> parseQuery(String query) throws IOException {
Map<String, String> map = new HashMap<>();
Iterable<String> entries = Splitter.on('&').split(query);
Expand All @@ -116,6 +123,23 @@ public static Map<String, String> parseQuery(String query) throws IOException {
return map;
}

/**
* Parses a JSON string into a map of key-value pairs.
*
* @param content The JSON string representation of a flat object.
* @return A map of keys to string representations of their values.
* @throws IOException If the JSON is malformed.
*/
public static Map<String, String> parseJson(String content) throws IOException {
GenericJson json = JSON_FACTORY.fromString(content, GenericJson.class);
Map<String, String> map = new HashMap<>();
for (Map.Entry<String, Object> entry : json.entrySet()) {
Object value = entry.getValue();
map.put(entry.getKey(), value == null ? null : value.toString());
}
return map;
}

public static String errorJson(String message) throws IOException {
GenericJson errorResponse = new GenericJson();
errorResponse.setFactory(JSON_FACTORY);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ class DefaultCredentialsProviderTest {
private static final String SA_PRIVATE_KEY_ID = "d84a4fefcf50791d4a90f2d7af17469d6282df9d";
private static final String SA_PRIVATE_KEY_PKCS8 =
ServiceAccountCredentialsTest.PRIVATE_KEY_PKCS8;
private static final String GDCH_SA_FORMAT_VERSION = GdchCredentials.SUPPORTED_FORMAT_VERSION;

private static final String GDCH_SA_PROJECT_ID = "gdch-service-account-project-id";
private static final String GDCH_SA_PRIVATE_KEY_ID = "d84a4fefcf50791d4a90f2d7af17469d6282df9d";
private static final String GDCH_SA_PRIVATE_KEY_PKC8 = GdchCredentialsTest.PRIVATE_KEY_PKCS8;
Expand All @@ -94,7 +94,7 @@ class DefaultCredentialsProviderTest {
private static final String GDCH_SA_CA_CERT_FILE_NAME = "cert.pem";
private static final String GDCH_SA_CA_CERT_PATH =
GdchCredentialsTest.class.getClassLoader().getResource(GDCH_SA_CA_CERT_FILE_NAME).getPath();
private static final URI GDCH_SA_API_AUDIENCE = URI.create("https://gdch-api-audience");
private static final String GDCH_SA_API_AUDIENCE = "https://gdch-api-audience";
private static final Collection<String> SCOPES = Collections.singletonList("dummy.scope");
private static final URI CALL_URI = URI.create("http://googleapis.com/testapi/v1/foo");
private static final String QUOTA_PROJECT = "sample-quota-project-id";
Expand Down Expand Up @@ -180,48 +180,33 @@ void getDefaultCredentials_noCredentials_singleGceTestRequest() {
}

@Test
void getDefaultCredentials_noCredentials_linuxNotGce() throws IOException {
TestDefaultCredentialsProvider testProvider = new TestDefaultCredentialsProvider();
testProvider.setProperty("os.name", "Linux");
String productFilePath = SMBIOS_PATH_LINUX;
InputStream productStream = new ByteArrayInputStream("test".getBytes());
testProvider.addFile(productFilePath, productStream);

assertFalse(ComputeEngineCredentials.checkStaticGceDetection(testProvider));
void getDefaultCredentials_noCredentials_linuxNotGce() {
checkStaticGceDetection("linux", "test", false);
}

@Test
void getDefaultCredentials_static_linux() throws IOException {
TestDefaultCredentialsProvider testProvider = new TestDefaultCredentialsProvider();
testProvider.setProperty("os.name", "Linux");
String productFilePath = SMBIOS_PATH_LINUX;
File productFile = new File(productFilePath);
InputStream productStream = new ByteArrayInputStream("Googlekdjsfhg".getBytes());
testProvider.addFile(productFile.getAbsolutePath(), productStream);

assertTrue(ComputeEngineCredentials.checkStaticGceDetection(testProvider));
void getDefaultCredentials_static_linux() {
checkStaticGceDetection("linux", "Googlekdjsfhg", true);
}

@Test
void getDefaultCredentials_static_windows_configuredAsLinux_notGce() throws IOException {
TestDefaultCredentialsProvider testProvider = new TestDefaultCredentialsProvider();
testProvider.setProperty("os.name", "windows");
String productFilePath = SMBIOS_PATH_LINUX;
InputStream productStream = new ByteArrayInputStream("Googlekdjsfhg".getBytes());
testProvider.addFile(productFilePath, productStream);

assertFalse(ComputeEngineCredentials.checkStaticGceDetection(testProvider));
void getDefaultCredentials_static_windows_configuredAsLinux_notGce() {
checkStaticGceDetection("windows", "Googlekdjsfhg", false);
}

@Test
void getDefaultCredentials_static_unsupportedPlatform_notGce() throws IOException {
void getDefaultCredentials_static_unsupportedPlatform_notGce() {
checkStaticGceDetection("macos", "Googlekdjsfhg", false);
}

private void checkStaticGceDetection(String osName, String productContent, boolean expected) {
TestDefaultCredentialsProvider testProvider = new TestDefaultCredentialsProvider();
testProvider.setProperty("os.name", "macos");
testProvider.setProperty("os.name", osName);
String productFilePath = SMBIOS_PATH_LINUX;
InputStream productStream = new ByteArrayInputStream("Googlekdjsfhg".getBytes());
InputStream productStream = new ByteArrayInputStream(productContent.getBytes());
testProvider.addFile(productFilePath, productStream);

assertFalse(ComputeEngineCredentials.checkStaticGceDetection(testProvider));
assertEquals(expected, ComputeEngineCredentials.checkStaticGceDetection(testProvider));
}

@Test
Expand Down Expand Up @@ -390,7 +375,7 @@ void getDefaultCredentials_GdchServiceAccount() throws IOException {
MockTokenServerTransportFactory transportFactory = new MockTokenServerTransportFactory();
InputStream gdchServiceAccountStream =
GdchCredentialsTest.writeGdchServiceAccountStream(
GDCH_SA_FORMAT_VERSION,
GdchCredentials.SUPPORTED_JSON_FORMAT_VERSION,
GDCH_SA_PROJECT_ID,
GDCH_SA_PRIVATE_KEY_ID,
GDCH_SA_PRIVATE_KEY_PKC8,
Expand Down
Loading
Loading