From 6293e1916db8978dd71c69727afc234175d90cc9 Mon Sep 17 00:00:00 2001 From: Diego Alonso Marquez Palacios Date: Tue, 1 Apr 2025 13:48:41 -0400 Subject: [PATCH 1/6] feat: allow cached ADC to be refreshed Fixes https://github.com/googleapis/google-api-java-client/issues/2541 This introduces the new method `public static GoogleCredential getApplicationDefault(boolean resetCachedCredentials)` which will reset the cached credentials when the argument is `true`. --- .../oauth2/DefaultCredentialProvider.java | 23 +++++++++- .../auth/oauth2/GoogleCredential.java | 46 +++++++++++++++++-- .../oauth2/DefaultCredentialProviderTest.java | 14 ++++++ 3 files changed, 78 insertions(+), 5 deletions(-) diff --git a/google-api-client/src/main/java/com/google/api/client/googleapis/auth/oauth2/DefaultCredentialProvider.java b/google-api-client/src/main/java/com/google/api/client/googleapis/auth/oauth2/DefaultCredentialProvider.java index 9f1534a19..a8111c2b0 100644 --- a/google-api-client/src/main/java/com/google/api/client/googleapis/auth/oauth2/DefaultCredentialProvider.java +++ b/google-api-client/src/main/java/com/google/api/client/googleapis/auth/oauth2/DefaultCredentialProvider.java @@ -82,6 +82,7 @@ private static enum Environment { * authorize the whole application. This is the built-in service account if running on Google * Compute Engine or the credentials file from the path in the environment variable * GOOGLE_APPLICATION_CREDENTIALS. + * If the credentials have been cached, the cached credential will be returned. * * @param transport the transport for Http calls. * @param jsonFactory the factory for Json parsing and formatting. @@ -90,8 +91,28 @@ private static enum Environment { */ final GoogleCredential getDefaultCredential(HttpTransport transport, JsonFactory jsonFactory) throws IOException { + return getDefaultCredential(transport, jsonFactory, false); + } + + /** + * {@link Beta}
+ * Returns the Application Default Credentials. + * + *

Returns the Application Default Credentials which are credentials that identify and + * authorize the whole application. This is the built-in service account if running on Google + * Compute Engine or the credentials file from the path in the environment variable + * GOOGLE_APPLICATION_CREDENTIALS. + * + * @param transport the transport for Http calls. + * @param jsonFactory the factory for Json parsing and formatting. + * @param resetCachedCredentials if true, the cached credential will be reset. + * @return the credential instance. + * @throws IOException if the credential cannot be created in the current environment. + */ + final GoogleCredential getDefaultCredential(HttpTransport transport, JsonFactory jsonFactory, boolean resetCachedCredentials) + throws IOException { synchronized (this) { - if (cachedCredential == null) { + if (cachedCredential == null || resetCachedCredentials) { cachedCredential = getDefaultCredentialUnsynchronized(transport, jsonFactory); } if (cachedCredential != null) { diff --git a/google-api-client/src/main/java/com/google/api/client/googleapis/auth/oauth2/GoogleCredential.java b/google-api-client/src/main/java/com/google/api/client/googleapis/auth/oauth2/GoogleCredential.java index 590a422e2..56e701625 100644 --- a/google-api-client/src/main/java/com/google/api/client/googleapis/auth/oauth2/GoogleCredential.java +++ b/google-api-client/src/main/java/com/google/api/client/googleapis/auth/oauth2/GoogleCredential.java @@ -153,7 +153,7 @@ public class GoogleCredential extends Credential { static final String SERVICE_ACCOUNT_FILE_TYPE = "service_account"; @Beta - private static DefaultCredentialProvider defaultCredentialProvider = + private static final DefaultCredentialProvider defaultCredentialProvider = new DefaultCredentialProvider(); /** @@ -170,7 +170,7 @@ public class GoogleCredential extends Credential { */ @Beta public static GoogleCredential getApplicationDefault() throws IOException { - return getApplicationDefault(Utils.getDefaultTransport(), Utils.getDefaultJsonFactory()); + return getApplicationDefault(Utils.getDefaultTransport(), Utils.getDefaultJsonFactory(), false); } /** @@ -181,7 +181,25 @@ public static GoogleCredential getApplicationDefault() throws IOException { * authorize the whole application. This is the built-in service account if running on Google * Compute Engine or the credentials file from the path in the environment variable * GOOGLE_APPLICATION_CREDENTIALS. + * @param resetCachedCredentials whether to reset the cached credentials + * @return the credential instance. + * @throws IOException if the credential cannot be created in the current environment. + */ + @Beta + public static GoogleCredential getApplicationDefault(boolean resetCachedCredentials) throws IOException { + return getApplicationDefault(Utils.getDefaultTransport(), Utils.getDefaultJsonFactory(), resetCachedCredentials); + } + + /** + * {@link Beta}
+ * Returns the Application Default Credentials. * + *

Returns the Application Default Credentials which are credentials that identify and + * authorize the whole application. This is the built-in service account if running on Google + * Compute Engine or the credentials file from the path in the environment variable + * GOOGLE_APPLICATION_CREDENTIALS. + * + * @param resetCachedCredentials whether to reset the cached credentials. * @param transport the transport for Http calls. * @param jsonFactory the factory for Json parsing and formatting. * @return the credential instance. @@ -189,10 +207,30 @@ public static GoogleCredential getApplicationDefault() throws IOException { */ @Beta public static GoogleCredential getApplicationDefault( - HttpTransport transport, JsonFactory jsonFactory) throws IOException { + HttpTransport transport, JsonFactory jsonFactory, boolean resetCachedCredentials) throws IOException { Preconditions.checkNotNull(transport); Preconditions.checkNotNull(jsonFactory); - return defaultCredentialProvider.getDefaultCredential(transport, jsonFactory); + return defaultCredentialProvider.getDefaultCredential(transport, jsonFactory, resetCachedCredentials); + } + + /** + * {@link Beta}
+ * Returns the Application Default Credentials. + * + *

Returns the Application Default Credentials which are credentials that identify and + * authorize the whole application. This is the built-in service account if running on Google + * Compute Engine or the credentials file from the path in the environment variable + * GOOGLE_APPLICATION_CREDENTIALS. + * + * @param transport the transport for Http calls. + * @param jsonFactory the factory for Json parsing and formatting. + * @return the credential instance. + * @throws IOException if the credential cannot be created in the current environment. + */ + @Beta + public static GoogleCredential getApplicationDefault( + HttpTransport transport, JsonFactory jsonFactory) throws IOException { + return getApplicationDefault(transport, jsonFactory, false); } /** diff --git a/google-api-client/src/test/java/com/google/api/client/googleapis/auth/oauth2/DefaultCredentialProviderTest.java b/google-api-client/src/test/java/com/google/api/client/googleapis/auth/oauth2/DefaultCredentialProviderTest.java index d3ea60ccc..2974e764b 100644 --- a/google-api-client/src/test/java/com/google/api/client/googleapis/auth/oauth2/DefaultCredentialProviderTest.java +++ b/google-api-client/src/test/java/com/google/api/client/googleapis/auth/oauth2/DefaultCredentialProviderTest.java @@ -87,6 +87,20 @@ public void testDefaultCredentialAppEngineDeployed() throws IOException { assertSame(JSON_FACTORY, defaultCredential.getJsonFactory()); } + + public void testGetApplicationDefaultResetCacheTrueReturnsNewCredentials() throws IOException { + TestDefaultCredentialProvider testProvider = new TestDefaultCredentialProvider(); + HttpTransport transport = new MockHttpTransport(); + testProvider.addType( + DefaultCredentialProvider.APP_ENGINE_CREDENTIAL_CLASS, MockAppEngineCredential.class); + testProvider.addType(GAE_SIGNAL_CLASS, MockAppEngineSystemProperty.class); + Credential credential1 = testProvider.getDefaultCredential(transport, JSON_FACTORY, false); + Credential credential2 = testProvider.getDefaultCredential(transport, JSON_FACTORY, false); + Credential credential3 = testProvider.getDefaultCredential(transport, JSON_FACTORY, true); + assertSame(credential1, credential2); + assertNotSame(credential2, credential3); + } + public void testDefaultCredentialAppEngineComponentOffAppEngineGivesNotFoundError() { HttpTransport transport = new MockHttpTransport(); TestDefaultCredentialProvider testProvider = new TestDefaultCredentialProvider(); From 7cd8cc1e80dd8a44455d6aa3432a76ba37382cda Mon Sep 17 00:00:00 2001 From: Diego Alonso Marquez Palacios Date: Tue, 1 Apr 2025 13:58:55 -0400 Subject: [PATCH 2/6] format --- .../auth/oauth2/DefaultCredentialProvider.java | 7 ++++--- .../auth/oauth2/GoogleCredential.java | 17 ++++++++++++----- .../oauth2/DefaultCredentialProviderTest.java | 1 - 3 files changed, 16 insertions(+), 9 deletions(-) diff --git a/google-api-client/src/main/java/com/google/api/client/googleapis/auth/oauth2/DefaultCredentialProvider.java b/google-api-client/src/main/java/com/google/api/client/googleapis/auth/oauth2/DefaultCredentialProvider.java index a8111c2b0..7d17d4aed 100644 --- a/google-api-client/src/main/java/com/google/api/client/googleapis/auth/oauth2/DefaultCredentialProvider.java +++ b/google-api-client/src/main/java/com/google/api/client/googleapis/auth/oauth2/DefaultCredentialProvider.java @@ -81,8 +81,8 @@ private static enum Environment { *

Returns the Application Default Credentials which are credentials that identify and * authorize the whole application. This is the built-in service account if running on Google * Compute Engine or the credentials file from the path in the environment variable - * GOOGLE_APPLICATION_CREDENTIALS. - * If the credentials have been cached, the cached credential will be returned. + * GOOGLE_APPLICATION_CREDENTIALS. If the credentials have been cached, the cached credential will + * be returned. * * @param transport the transport for Http calls. * @param jsonFactory the factory for Json parsing and formatting. @@ -109,7 +109,8 @@ final GoogleCredential getDefaultCredential(HttpTransport transport, JsonFactory * @return the credential instance. * @throws IOException if the credential cannot be created in the current environment. */ - final GoogleCredential getDefaultCredential(HttpTransport transport, JsonFactory jsonFactory, boolean resetCachedCredentials) + final GoogleCredential getDefaultCredential( + HttpTransport transport, JsonFactory jsonFactory, boolean resetCachedCredentials) throws IOException { synchronized (this) { if (cachedCredential == null || resetCachedCredentials) { diff --git a/google-api-client/src/main/java/com/google/api/client/googleapis/auth/oauth2/GoogleCredential.java b/google-api-client/src/main/java/com/google/api/client/googleapis/auth/oauth2/GoogleCredential.java index 56e701625..2118f289a 100644 --- a/google-api-client/src/main/java/com/google/api/client/googleapis/auth/oauth2/GoogleCredential.java +++ b/google-api-client/src/main/java/com/google/api/client/googleapis/auth/oauth2/GoogleCredential.java @@ -181,13 +181,16 @@ public static GoogleCredential getApplicationDefault() throws IOException { * authorize the whole application. This is the built-in service account if running on Google * Compute Engine or the credentials file from the path in the environment variable * GOOGLE_APPLICATION_CREDENTIALS. + * * @param resetCachedCredentials whether to reset the cached credentials * @return the credential instance. * @throws IOException if the credential cannot be created in the current environment. */ @Beta - public static GoogleCredential getApplicationDefault(boolean resetCachedCredentials) throws IOException { - return getApplicationDefault(Utils.getDefaultTransport(), Utils.getDefaultJsonFactory(), resetCachedCredentials); + public static GoogleCredential getApplicationDefault(boolean resetCachedCredentials) + throws IOException { + return getApplicationDefault( + Utils.getDefaultTransport(), Utils.getDefaultJsonFactory(), resetCachedCredentials); } /** @@ -207,10 +210,12 @@ public static GoogleCredential getApplicationDefault(boolean resetCachedCredenti */ @Beta public static GoogleCredential getApplicationDefault( - HttpTransport transport, JsonFactory jsonFactory, boolean resetCachedCredentials) throws IOException { + HttpTransport transport, JsonFactory jsonFactory, boolean resetCachedCredentials) + throws IOException { Preconditions.checkNotNull(transport); Preconditions.checkNotNull(jsonFactory); - return defaultCredentialProvider.getDefaultCredential(transport, jsonFactory, resetCachedCredentials); + return defaultCredentialProvider.getDefaultCredential( + transport, jsonFactory, resetCachedCredentials); } /** @@ -607,7 +612,9 @@ public Builder setJsonFactory(JsonFactory jsonFactory) { return (Builder) super.setJsonFactory(jsonFactory); } - /** @since 1.9 */ + /** + * @since 1.9 + */ @Override public Builder setClock(Clock clock) { return (Builder) super.setClock(clock); diff --git a/google-api-client/src/test/java/com/google/api/client/googleapis/auth/oauth2/DefaultCredentialProviderTest.java b/google-api-client/src/test/java/com/google/api/client/googleapis/auth/oauth2/DefaultCredentialProviderTest.java index 2974e764b..b68d5940a 100644 --- a/google-api-client/src/test/java/com/google/api/client/googleapis/auth/oauth2/DefaultCredentialProviderTest.java +++ b/google-api-client/src/test/java/com/google/api/client/googleapis/auth/oauth2/DefaultCredentialProviderTest.java @@ -87,7 +87,6 @@ public void testDefaultCredentialAppEngineDeployed() throws IOException { assertSame(JSON_FACTORY, defaultCredential.getJsonFactory()); } - public void testGetApplicationDefaultResetCacheTrueReturnsNewCredentials() throws IOException { TestDefaultCredentialProvider testProvider = new TestDefaultCredentialProvider(); HttpTransport transport = new MockHttpTransport(); From 9d451e2d83cf208111e804342b31567ea6f46ecd Mon Sep 17 00:00:00 2001 From: Diego Alonso Marquez Palacios Date: Tue, 1 Apr 2025 19:30:16 -0400 Subject: [PATCH 3/6] lint with google-java-format 1.7 --- .../api/client/googleapis/auth/oauth2/GoogleCredential.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/google-api-client/src/main/java/com/google/api/client/googleapis/auth/oauth2/GoogleCredential.java b/google-api-client/src/main/java/com/google/api/client/googleapis/auth/oauth2/GoogleCredential.java index 2118f289a..0b6c39ee8 100644 --- a/google-api-client/src/main/java/com/google/api/client/googleapis/auth/oauth2/GoogleCredential.java +++ b/google-api-client/src/main/java/com/google/api/client/googleapis/auth/oauth2/GoogleCredential.java @@ -140,8 +140,8 @@ * response handler, take a look at the sample usage for {@link HttpExecuteInterceptor} and {@link * HttpUnsuccessfulResponseHandler}, which are interfaces that this class also implements. * - * @since 1.7 * @author Yaniv Inbar + * @since 1.7 * @deprecated Please use * google-auth-library for handling Application Default Credentials and other non-OAuth2 * based authentication. From f6dad950af2756309c960e609d899cddca101c12 Mon Sep 17 00:00:00 2001 From: Diego Alonso Marquez Palacios Date: Fri, 5 Sep 2025 14:03:26 -0400 Subject: [PATCH 4/6] chore: lint --- .../auth/oauth2/GoogleCredential.java | 1872 ++++++++--------- 1 file changed, 935 insertions(+), 937 deletions(-) diff --git a/google-api-client/src/main/java/com/google/api/client/googleapis/auth/oauth2/GoogleCredential.java b/google-api-client/src/main/java/com/google/api/client/googleapis/auth/oauth2/GoogleCredential.java index 0b6c39ee8..7de7e32d4 100644 --- a/google-api-client/src/main/java/com/google/api/client/googleapis/auth/oauth2/GoogleCredential.java +++ b/google-api-client/src/main/java/com/google/api/client/googleapis/auth/oauth2/GoogleCredential.java @@ -1,937 +1,935 @@ -/* - * Copyright 2011 Google Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except - * in compliance with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */ - -package com.google.api.client.googleapis.auth.oauth2; - -import com.google.api.client.auth.oauth2.BearerToken; -import com.google.api.client.auth.oauth2.ClientParametersAuthentication; -import com.google.api.client.auth.oauth2.Credential; -import com.google.api.client.auth.oauth2.CredentialRefreshListener; -import com.google.api.client.auth.oauth2.DataStoreCredentialRefreshListener; -import com.google.api.client.auth.oauth2.TokenRequest; -import com.google.api.client.auth.oauth2.TokenResponse; -import com.google.api.client.googleapis.auth.oauth2.GoogleClientSecrets.Details; -import com.google.api.client.googleapis.util.Utils; -import com.google.api.client.http.GenericUrl; -import com.google.api.client.http.HttpExecuteInterceptor; -import com.google.api.client.http.HttpRequestInitializer; -import com.google.api.client.http.HttpTransport; -import com.google.api.client.http.HttpUnsuccessfulResponseHandler; -import com.google.api.client.json.GenericJson; -import com.google.api.client.json.JsonFactory; -import com.google.api.client.json.JsonObjectParser; -import com.google.api.client.json.webtoken.JsonWebSignature; -import com.google.api.client.json.webtoken.JsonWebToken; -import com.google.api.client.util.Beta; -import com.google.api.client.util.Clock; -import com.google.api.client.util.Joiner; -import com.google.api.client.util.PemReader; -import com.google.api.client.util.PemReader.Section; -import com.google.api.client.util.Preconditions; -import com.google.api.client.util.SecurityUtils; -import com.google.api.client.util.store.DataStoreFactory; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileReader; -import java.io.IOException; -import java.io.InputStream; -import java.io.Reader; -import java.io.StringReader; -import java.security.GeneralSecurityException; -import java.security.KeyFactory; -import java.security.NoSuchAlgorithmException; -import java.security.PrivateKey; -import java.security.spec.InvalidKeySpecException; -import java.security.spec.PKCS8EncodedKeySpec; -import java.util.Collection; -import java.util.Collections; - -/** - * Thread-safe Google-specific implementation of the OAuth 2.0 helper for accessing protected - * resources using an access token, as well as optionally refreshing the access token when it - * expires using a refresh token. - * - *

There are three modes supported: access token only, refresh token flow, and service account - * flow (with or without impersonating a user). - * - *

If all you have is an access token, you simply pass the {@link TokenResponse} to the - * credential using {@link Builder#setFromTokenResponse(TokenResponse)}. Google credential uses - * {@link BearerToken#authorizationHeaderAccessMethod()} as the access method. Sample usage: - * - *

{@code
- * public static GoogleCredential createCredentialWithAccessTokenOnly(TokenResponse tokenResponse) {
- *   return new GoogleCredential().setFromTokenResponse(tokenResponse);
- * }
- * }
- * - *

If you have a refresh token, it is similar to the case of access token only, but you - * additionally need to pass the credential the client secrets using {@link - * Builder#setClientSecrets(GoogleClientSecrets)} or {@link Builder#setClientSecrets(String, - * String)}. Google credential uses {@link GoogleOAuthConstants#TOKEN_SERVER_URL} as the token - * server URL, and {@link ClientParametersAuthentication} with the client ID and secret as the - * client authentication. Sample usage: - * - *

{@code
- * public static GoogleCredential createCredentialWithRefreshToken(
- *     HttpTransport transport, JsonFactory jsonFactory,
- *     GoogleClientSecrets clientSecrets, TokenResponse tokenResponse) {
- *  return new GoogleCredential.Builder().setTransport(transport)
- *                        .setJsonFactory(jsonFactory)
- *                        .setClientSecrets(clientSecrets)
- *                        .build()
- *                        .setFromTokenResponse(tokenResponse);
- * }
- * }
- * - *

The service - * account flow is used when you want to access data owned by your client application. You - * download the private key in a {@code .p12} file from the Google APIs Console. Use {@link - * Builder#setServiceAccountId(String)}, {@link - * Builder#setServiceAccountPrivateKeyFromP12File(File)}, and {@link - * Builder#setServiceAccountScopes(Collection)}. Sample usage: - * - *

{@code
- * public static GoogleCredential createCredentialForServiceAccount(HttpTransport transport,
- *     JsonFactory jsonFactory,
- *     String serviceAccountId, Collection<String> serviceAccountScopes, File p12File)
- *     throws GeneralSecurityException, IOException {
- *   return new GoogleCredential.Builder().setTransport(transport).setJsonFactory(jsonFactory)
- *       .setServiceAccountId(serviceAccountId).setServiceAccountScopes(serviceAccountScopes)
- *       .setServiceAccountPrivateKeyFromP12File(p12File).build();
- * }
- * }
- * - *

You can also use the service account flow to impersonate a user in a domain that you own. This - * is very similar to the service account flow above, but you additionally call {@link - * Builder#setServiceAccountUser(String)}. Sample usage: - * - *

{@code
- * public static GoogleCredential createCredentialForServiceAccountImpersonateUser
- *     (HttpTransport transport, JsonFactory jsonFactory, String serviceAccountId,
- *      Collection<String> serviceAccountScopes, File p12File,
- *      String serviceAccountUser) throws GeneralSecurityException, IOException {
- *   return new GoogleCredential.Builder()
- *       .setTransport(transport)
- *       .setJsonFactory(jsonFactory)
- *       .setServiceAccountId(serviceAccountId)
- *       .setServiceAccountScopes(serviceAccountScopes)
- *       .setServiceAccountPrivateKeyFromP12File(p12File)
- *       .setServiceAccountUser(serviceAccountUser)
- *       .build();
- * }
- * }
- * - *

If you need to persist the access token in a data store, use {@link DataStoreFactory} and - * {@link Builder#addRefreshListener(CredentialRefreshListener)} with {@link - * DataStoreCredentialRefreshListener}. - * - *

If you have a custom request initializer, request execute interceptor, or unsuccessful - * response handler, take a look at the sample usage for {@link HttpExecuteInterceptor} and {@link - * HttpUnsuccessfulResponseHandler}, which are interfaces that this class also implements. - * - * @author Yaniv Inbar - * @since 1.7 - * @deprecated Please use - * google-auth-library for handling Application Default Credentials and other non-OAuth2 - * based authentication. - */ -@Deprecated -public class GoogleCredential extends Credential { - - static final String USER_FILE_TYPE = "authorized_user"; - static final String SERVICE_ACCOUNT_FILE_TYPE = "service_account"; - - @Beta - private static final DefaultCredentialProvider defaultCredentialProvider = - new DefaultCredentialProvider(); - - /** - * {@link Beta}
- * Returns the Application Default Credentials. - * - *

Returns the Application Default Credentials which are credentials that identify and - * authorize the whole application. This is the built-in service account if running on Google - * Compute Engine or the credentials file from the path in the environment variable - * GOOGLE_APPLICATION_CREDENTIALS. - * - * @return the credential instance. - * @throws IOException if the credential cannot be created in the current environment. - */ - @Beta - public static GoogleCredential getApplicationDefault() throws IOException { - return getApplicationDefault(Utils.getDefaultTransport(), Utils.getDefaultJsonFactory(), false); - } - - /** - * {@link Beta}
- * Returns the Application Default Credentials. - * - *

Returns the Application Default Credentials which are credentials that identify and - * authorize the whole application. This is the built-in service account if running on Google - * Compute Engine or the credentials file from the path in the environment variable - * GOOGLE_APPLICATION_CREDENTIALS. - * - * @param resetCachedCredentials whether to reset the cached credentials - * @return the credential instance. - * @throws IOException if the credential cannot be created in the current environment. - */ - @Beta - public static GoogleCredential getApplicationDefault(boolean resetCachedCredentials) - throws IOException { - return getApplicationDefault( - Utils.getDefaultTransport(), Utils.getDefaultJsonFactory(), resetCachedCredentials); - } - - /** - * {@link Beta}
- * Returns the Application Default Credentials. - * - *

Returns the Application Default Credentials which are credentials that identify and - * authorize the whole application. This is the built-in service account if running on Google - * Compute Engine or the credentials file from the path in the environment variable - * GOOGLE_APPLICATION_CREDENTIALS. - * - * @param resetCachedCredentials whether to reset the cached credentials. - * @param transport the transport for Http calls. - * @param jsonFactory the factory for Json parsing and formatting. - * @return the credential instance. - * @throws IOException if the credential cannot be created in the current environment. - */ - @Beta - public static GoogleCredential getApplicationDefault( - HttpTransport transport, JsonFactory jsonFactory, boolean resetCachedCredentials) - throws IOException { - Preconditions.checkNotNull(transport); - Preconditions.checkNotNull(jsonFactory); - return defaultCredentialProvider.getDefaultCredential( - transport, jsonFactory, resetCachedCredentials); - } - - /** - * {@link Beta}
- * Returns the Application Default Credentials. - * - *

Returns the Application Default Credentials which are credentials that identify and - * authorize the whole application. This is the built-in service account if running on Google - * Compute Engine or the credentials file from the path in the environment variable - * GOOGLE_APPLICATION_CREDENTIALS. - * - * @param transport the transport for Http calls. - * @param jsonFactory the factory for Json parsing and formatting. - * @return the credential instance. - * @throws IOException if the credential cannot be created in the current environment. - */ - @Beta - public static GoogleCredential getApplicationDefault( - HttpTransport transport, JsonFactory jsonFactory) throws IOException { - return getApplicationDefault(transport, jsonFactory, false); - } - - /** - * {@link Beta}
- * Return a credential defined by a Json file. - * - *

Important: If you accept a credential configuration (credential JSON/File/Stream) from an - * external source for authentication to Google Cloud Platform, you must validate it before - * providing it to any Google API or library. Providing an unvalidated credential configuration to - * Google APIs can compromise the security of your systems and data. For more information, refer - * to {@link documentation}. - * - * @param credentialStream the stream with the credential definition. - * @return the credential defined by the credentialStream. - * @throws IOException if the credential cannot be created from the stream. - */ - @Beta - public static GoogleCredential fromStream(InputStream credentialStream) throws IOException { - return fromStream(credentialStream, Utils.getDefaultTransport(), Utils.getDefaultJsonFactory()); - } - - /** - * {@link Beta}
- * Return a credential defined by a Json file. - * - *

Important: If you accept a credential configuration (credential JSON/File/Stream) from an - * external source for authentication to Google Cloud Platform, you must validate it before - * providing it to any Google API or library. Providing an unvalidated credential configuration to - * Google APIs can compromise the security of your systems and data. For more information, refer - * to {@link documentation}. - * - * @param credentialStream the stream with the credential definition. - * @param transport the transport for Http calls. - * @param jsonFactory the factory for Json parsing and formatting. - * @return the credential defined by the credentialStream. - * @throws IOException if the credential cannot be created from the stream. - */ - @Beta - public static GoogleCredential fromStream( - InputStream credentialStream, HttpTransport transport, JsonFactory jsonFactory) - throws IOException { - Preconditions.checkNotNull(credentialStream); - Preconditions.checkNotNull(transport); - Preconditions.checkNotNull(jsonFactory); - - JsonObjectParser parser = new JsonObjectParser(jsonFactory); - GenericJson fileContents = - parser.parseAndClose(credentialStream, OAuth2Utils.UTF_8, GenericJson.class); - String fileType = (String) fileContents.get("type"); - if (fileType == null) { - throw new IOException("Error reading credentials from stream, 'type' field not specified."); - } - if (USER_FILE_TYPE.equals(fileType)) { - return fromStreamUser(fileContents, transport, jsonFactory); - } - if (SERVICE_ACCOUNT_FILE_TYPE.equals(fileType)) { - return fromStreamServiceAccount(fileContents, transport, jsonFactory); - } - throw new IOException( - String.format( - "Error reading credentials from stream, 'type' value '%s' not recognized." - + " Expecting '%s' or '%s'.", - fileType, USER_FILE_TYPE, SERVICE_ACCOUNT_FILE_TYPE)); - } - - /** - * Service account ID (typically an e-mail address) or {@code null} if not using the service - * account flow. - */ - private String serviceAccountId; - - /** - * Service account Project ID or {@code null} if not present, either because this is not using the - * service account flow, or is using an older version of the service account configuration. - */ - private String serviceAccountProjectId; - - /** - * Collection of OAuth scopes to use with the service account flow or {@code null} if not using - * the service account flow. - */ - private Collection serviceAccountScopes; - - /** - * Private key to use with the service account flow or {@code null} if not using the service - * account flow. - */ - private PrivateKey serviceAccountPrivateKey; - - /** - * ID of private key to use with the service account flow or {@code null} if not using the service - * account flow. - */ - private String serviceAccountPrivateKeyId; - - /** - * Email address of the user the application is trying to impersonate in the service account flow - * or {@code null} for none or if not using the service account flow. - */ - private String serviceAccountUser; - - /** - * Constructor with the ability to access protected resources, but not refresh tokens. - * - *

To use with the ability to refresh tokens, use {@link Builder}. - */ - public GoogleCredential() { - this(new Builder()); - } - - /** - * @param builder Google credential builder - * @since 1.14 - */ - protected GoogleCredential(Builder builder) { - super(builder); - if (builder.serviceAccountPrivateKey == null) { - Preconditions.checkArgument( - builder.serviceAccountId == null - && builder.serviceAccountScopes == null - && builder.serviceAccountUser == null); - } else { - serviceAccountId = Preconditions.checkNotNull(builder.serviceAccountId); - serviceAccountProjectId = builder.serviceAccountProjectId; - serviceAccountScopes = - (builder.serviceAccountScopes == null) - ? Collections.emptyList() - : Collections.unmodifiableCollection(builder.serviceAccountScopes); - serviceAccountPrivateKey = builder.serviceAccountPrivateKey; - serviceAccountPrivateKeyId = builder.serviceAccountPrivateKeyId; - serviceAccountUser = builder.serviceAccountUser; - } - } - - @Override - public GoogleCredential setAccessToken(String accessToken) { - return (GoogleCredential) super.setAccessToken(accessToken); - } - - @Override - public GoogleCredential setRefreshToken(String refreshToken) { - if (refreshToken != null) { - Preconditions.checkArgument( - getJsonFactory() != null && getTransport() != null && getClientAuthentication() != null, - "Please use the Builder and call setJsonFactory, setTransport and setClientSecrets"); - } - return (GoogleCredential) super.setRefreshToken(refreshToken); - } - - @Override - public GoogleCredential setExpirationTimeMilliseconds(Long expirationTimeMilliseconds) { - return (GoogleCredential) super.setExpirationTimeMilliseconds(expirationTimeMilliseconds); - } - - @Override - public GoogleCredential setExpiresInSeconds(Long expiresIn) { - return (GoogleCredential) super.setExpiresInSeconds(expiresIn); - } - - @Override - public GoogleCredential setFromTokenResponse(TokenResponse tokenResponse) { - return (GoogleCredential) super.setFromTokenResponse(tokenResponse); - } - - @Override - @Beta - protected TokenResponse executeRefreshToken() throws IOException { - if (serviceAccountPrivateKey == null) { - return super.executeRefreshToken(); - } - // service accounts: no refresh token; instead use private key to request new access token - JsonWebSignature.Header header = new JsonWebSignature.Header(); - header.setAlgorithm("RS256"); - header.setType("JWT"); - header.setKeyId(serviceAccountPrivateKeyId); - JsonWebToken.Payload payload = new JsonWebToken.Payload(); - long currentTime = getClock().currentTimeMillis(); - payload.setIssuer(serviceAccountId); - payload.setAudience(getTokenServerEncodedUrl()); - payload.setIssuedAtTimeSeconds(currentTime / 1000); - payload.setExpirationTimeSeconds(currentTime / 1000 + 3600); - payload.setSubject(serviceAccountUser); - payload.put("scope", Joiner.on(' ').join(serviceAccountScopes)); - try { - String assertion = - JsonWebSignature.signUsingRsaSha256( - serviceAccountPrivateKey, getJsonFactory(), header, payload); - TokenRequest request = - new TokenRequest( - getTransport(), - getJsonFactory(), - new GenericUrl(getTokenServerEncodedUrl()), - "urn:ietf:params:oauth:grant-type:jwt-bearer"); - request.put("assertion", assertion); - return request.execute(); - } catch (GeneralSecurityException exception) { - IOException e = new IOException(); - e.initCause(exception); - throw e; - } - } - - /** - * Returns the service account ID (typically an e-mail address) or {@code null} if not using the - * service account flow. - */ - public final String getServiceAccountId() { - return serviceAccountId; - } - - /** - * Returns the service account Project ID or {@code null} if not present, either because this is - * not using the service account flow, or is using an older version of the service account - * configuration. - */ - public final String getServiceAccountProjectId() { - return serviceAccountProjectId; - } - - /** - * Returns a collection of OAuth scopes to use with the service account flow or {@code null} if - * not using the service account flow. - */ - public final Collection getServiceAccountScopes() { - return serviceAccountScopes; - } - - /** - * Returns the space-separated OAuth scopes to use with the service account flow or {@code null} - * if not using the service account flow. - * - * @since 1.15 - */ - public final String getServiceAccountScopesAsString() { - return serviceAccountScopes == null ? null : Joiner.on(' ').join(serviceAccountScopes); - } - - /** - * Returns the private key to use with the service account flow or {@code null} if not using the - * service account flow. - */ - public final PrivateKey getServiceAccountPrivateKey() { - return serviceAccountPrivateKey; - } - - /** - * {@link Beta}
- * Returns the ID of the private key to use with the service account flow or {@code null} if not - * using the service account flow. - */ - @Beta - public final String getServiceAccountPrivateKeyId() { - return serviceAccountPrivateKeyId; - } - - /** - * Returns the email address of the user the application is trying to impersonate in the service - * account flow or {@code null} for none or if not using the service account flow. - */ - public final String getServiceAccountUser() { - return serviceAccountUser; - } - - /** - * {@link Beta}
- * Indicates whether the credential requires scopes to be specified by calling createScoped before - * use. - */ - @Beta - public boolean createScopedRequired() { - if (serviceAccountPrivateKey == null) { - return false; - } - return (serviceAccountScopes == null || serviceAccountScopes.isEmpty()); - } - - /** - * {@link Beta}
- * For credentials that require scopes, creates a copy of the credential with the specified - * scopes. - */ - @Beta - public GoogleCredential createScoped(Collection scopes) { - if (serviceAccountPrivateKey == null) { - return this; - } - return toBuilder().setServiceAccountScopes(scopes).build(); - } - - /** - * {@link Beta}
- * For service accounts that need to delegate to a specific user, create a copy of the credential - * with the specified user. - */ - @Beta - public GoogleCredential createDelegated(String user) { - if (serviceAccountPrivateKey == null) { - return this; - } - return toBuilder().setServiceAccountUser(user).build(); - } - - /** - * {@link Beta}
- * Create a builder from this credential. - */ - @Beta - public Builder toBuilder() { - Builder builder = - new GoogleCredential.Builder() - .setServiceAccountPrivateKey(serviceAccountPrivateKey) - .setServiceAccountPrivateKeyId(serviceAccountPrivateKeyId) - .setServiceAccountId(serviceAccountId) - .setServiceAccountProjectId(serviceAccountProjectId) - .setServiceAccountUser(serviceAccountUser) - .setServiceAccountScopes(serviceAccountScopes) - .setTokenServerEncodedUrl(getTokenServerEncodedUrl()) - .setTransport(getTransport()) - .setJsonFactory(getJsonFactory()) - .setClock(getClock()); - - builder.setClientAuthentication(getClientAuthentication()); - - return builder; - } - - /** - * Google credential builder. - * - *

Implementation is not thread-safe. - */ - public static class Builder extends Credential.Builder { - - /** Service account ID (typically an e-mail address) or {@code null} for none. */ - String serviceAccountId; - - /** Collection of OAuth scopes to use with the service account flow or {@code null} for none. */ - Collection serviceAccountScopes; - - /** Private key to use with the service account flow or {@code null} for none. */ - PrivateKey serviceAccountPrivateKey; - - /** Id of the private key to use with the service account flow or {@code null} for none. */ - String serviceAccountPrivateKeyId; - - /** Project ID associated with the Service Account. */ - String serviceAccountProjectId; - - /** - * Email address of the user the application is trying to impersonate in the service account - * flow or {@code null} for none. - */ - String serviceAccountUser; - - public Builder() { - super(BearerToken.authorizationHeaderAccessMethod()); - setTokenServerEncodedUrl(GoogleOAuthConstants.TOKEN_SERVER_URL); - } - - @Override - public GoogleCredential build() { - return new GoogleCredential(this); - } - - @Override - public Builder setTransport(HttpTransport transport) { - return (Builder) super.setTransport(transport); - } - - @Override - public Builder setJsonFactory(JsonFactory jsonFactory) { - return (Builder) super.setJsonFactory(jsonFactory); - } - - /** - * @since 1.9 - */ - @Override - public Builder setClock(Clock clock) { - return (Builder) super.setClock(clock); - } - - /** - * Sets the client identifier and secret. - * - *

Overriding is only supported for the purpose of calling the super implementation and - * changing the return type, but nothing else. - */ - public Builder setClientSecrets(String clientId, String clientSecret) { - setClientAuthentication(new ClientParametersAuthentication(clientId, clientSecret)); - return this; - } - - /** - * Sets the client secrets. - * - *

Overriding is only supported for the purpose of calling the super implementation and - * changing the return type, but nothing else. - */ - public Builder setClientSecrets(GoogleClientSecrets clientSecrets) { - Details details = clientSecrets.getDetails(); - setClientAuthentication( - new ClientParametersAuthentication(details.getClientId(), details.getClientSecret())); - return this; - } - - /** Returns the service account ID (typically an e-mail address) or {@code null} for none. */ - public final String getServiceAccountId() { - return serviceAccountId; - } - - /** - * Sets the service account ID (typically an e-mail address) or {@code null} for none. - * - *

Overriding is only supported for the purpose of calling the super implementation and - * changing the return type, but nothing else. - */ - public Builder setServiceAccountId(String serviceAccountId) { - this.serviceAccountId = serviceAccountId; - return this; - } - - /** Returns the service account Project ID or {@code null} for none. */ - public final String getServiceAccountProjectId() { - return serviceAccountProjectId; - } - - /** - * Sets the service account Project ID or {@code null} for none. - * - *

Overriding is only supported for the purpose of calling the super implementation and - * changing the return type, but nothing else. - */ - public Builder setServiceAccountProjectId(String serviceAccountProjectId) { - this.serviceAccountProjectId = serviceAccountProjectId; - return this; - } - - /** - * Returns a collection of OAuth scopes to use with the service account flow or {@code null} for - * none. - */ - public final Collection getServiceAccountScopes() { - return serviceAccountScopes; - } - - /** - * Sets the space-separated OAuth scopes to use with the service account flow or {@code null} - * for none. - * - *

Overriding is only supported for the purpose of calling the super implementation and - * changing the return type, but nothing else. - * - * @param serviceAccountScopes collection of scopes to be joined by a space separator (or a - * single value containing multiple space-separated scopes) - * @since 1.15 - */ - public Builder setServiceAccountScopes(Collection serviceAccountScopes) { - this.serviceAccountScopes = serviceAccountScopes; - return this; - } - - /** Returns the private key to use with the service account flow or {@code null} for none. */ - public final PrivateKey getServiceAccountPrivateKey() { - return serviceAccountPrivateKey; - } - - /** - * Sets the private key to use with the service account flow or {@code null} for none. - * - *

Overriding is only supported for the purpose of calling the super implementation and - * changing the return type, but nothing else. - */ - public Builder setServiceAccountPrivateKey(PrivateKey serviceAccountPrivateKey) { - this.serviceAccountPrivateKey = serviceAccountPrivateKey; - return this; - } - - /** - * {@link Beta}
- * Returns the id of the private key to use with the service account flow or {@code null} for - * none. - */ - @Beta - public final String getServiceAccountPrivateKeyId() { - return serviceAccountPrivateKeyId; - } - - /** - * {@link Beta}
- * Sets the id of the private key to use with the service account flow or {@code null} for none. - * - *

Overriding is only supported for the purpose of calling the super implementation and - * changing the return type, but nothing else. - */ - @Beta - public Builder setServiceAccountPrivateKeyId(String serviceAccountPrivateKeyId) { - this.serviceAccountPrivateKeyId = serviceAccountPrivateKeyId; - return this; - } - - /** - * Sets the private key to use with the service account flow or {@code null} for none. - * - *

Overriding is only supported for the purpose of calling the super implementation and - * changing the return type, but nothing else. - * - * @param p12File p12 file object - */ - public Builder setServiceAccountPrivateKeyFromP12File(File p12File) - throws GeneralSecurityException, IOException { - setServiceAccountPrivateKeyFromP12File(new FileInputStream(p12File)); - return this; - } - - /** - * Sets the private key to use with the service account flow or {@code null} for none. - * - *

Overriding is only supported for the purpose of calling the super implementation and - * changing the return type, but nothing else. - * - * @param p12FileInputStream input stream to the p12 file. This file is closed at the end of - * this method in a finally block. - */ - public Builder setServiceAccountPrivateKeyFromP12File(InputStream p12FileInputStream) - throws GeneralSecurityException, IOException { - serviceAccountPrivateKey = - SecurityUtils.loadPrivateKeyFromKeyStore( - SecurityUtils.getPkcs12KeyStore(), - p12FileInputStream, - "notasecret", - "privatekey", - "notasecret"); - return this; - } - - /** - * {@link Beta}
- * Sets the private key to use with the service account flow or {@code null} for none. - * - *

Overriding is only supported for the purpose of calling the super implementation and - * changing the return type, but nothing else. - * - * @param pemFile input stream to the PEM file (closed at the end of this method in a finally - * block) - * @since 1.13 - */ - @Beta - public Builder setServiceAccountPrivateKeyFromPemFile(File pemFile) - throws GeneralSecurityException, IOException { - byte[] bytes = - PemReader.readFirstSectionAndClose(new FileReader(pemFile), "PRIVATE KEY") - .getBase64DecodedBytes(); - serviceAccountPrivateKey = - SecurityUtils.getRsaKeyFactory().generatePrivate(new PKCS8EncodedKeySpec(bytes)); - return this; - } - - /** - * Returns the email address of the user the application is trying to impersonate in the service - * account flow or {@code null} for none. - */ - public final String getServiceAccountUser() { - return serviceAccountUser; - } - - /** - * Sets the email address of the user the application is trying to impersonate in the service - * account flow or {@code null} for none. - * - *

Overriding is only supported for the purpose of calling the super implementation and - * changing the return type, but nothing else. - */ - public Builder setServiceAccountUser(String serviceAccountUser) { - this.serviceAccountUser = serviceAccountUser; - return this; - } - - @Override - public Builder setRequestInitializer(HttpRequestInitializer requestInitializer) { - return (Builder) super.setRequestInitializer(requestInitializer); - } - - @Override - public Builder addRefreshListener(CredentialRefreshListener refreshListener) { - return (Builder) super.addRefreshListener(refreshListener); - } - - @Override - public Builder setRefreshListeners(Collection refreshListeners) { - return (Builder) super.setRefreshListeners(refreshListeners); - } - - @Override - public Builder setTokenServerUrl(GenericUrl tokenServerUrl) { - return (Builder) super.setTokenServerUrl(tokenServerUrl); - } - - @Override - public Builder setTokenServerEncodedUrl(String tokenServerEncodedUrl) { - return (Builder) super.setTokenServerEncodedUrl(tokenServerEncodedUrl); - } - - @Override - public Builder setClientAuthentication(HttpExecuteInterceptor clientAuthentication) { - return (Builder) super.setClientAuthentication(clientAuthentication); - } - } - - @Beta - private static GoogleCredential fromStreamUser( - GenericJson fileContents, HttpTransport transport, JsonFactory jsonFactory) - throws IOException { - String clientId = (String) fileContents.get("client_id"); - String clientSecret = (String) fileContents.get("client_secret"); - String refreshToken = (String) fileContents.get("refresh_token"); - if (clientId == null || clientSecret == null || refreshToken == null) { - throw new IOException( - "Error reading user credential from stream, " - + " expecting 'client_id', 'client_secret' and 'refresh_token'."); - } - - GoogleCredential credential = - new GoogleCredential.Builder() - .setClientSecrets(clientId, clientSecret) - .setTransport(transport) - .setJsonFactory(jsonFactory) - .build(); - credential.setRefreshToken(refreshToken); - - // Do a refresh so we can fail early rather than return an unusable credential - credential.refreshToken(); - return credential; - } - - @Beta - private static GoogleCredential fromStreamServiceAccount( - GenericJson fileContents, HttpTransport transport, JsonFactory jsonFactory) - throws IOException { - String clientId = (String) fileContents.get("client_id"); - String clientEmail = (String) fileContents.get("client_email"); - String privateKeyPem = (String) fileContents.get("private_key"); - String privateKeyId = (String) fileContents.get("private_key_id"); - if (clientId == null || clientEmail == null || privateKeyPem == null || privateKeyId == null) { - throw new IOException( - "Error reading service account credential from stream, " - + "expecting 'client_id', 'client_email', 'private_key' and 'private_key_id'."); - } - - PrivateKey privateKey = privateKeyFromPkcs8(privateKeyPem); - - Collection emptyScopes = Collections.emptyList(); - - Builder credentialBuilder = - new GoogleCredential.Builder() - .setTransport(transport) - .setJsonFactory(jsonFactory) - .setServiceAccountId(clientEmail) - .setServiceAccountScopes(emptyScopes) - .setServiceAccountPrivateKey(privateKey) - .setServiceAccountPrivateKeyId(privateKeyId); - String tokenUri = (String) fileContents.get("token_uri"); - if (tokenUri != null) { - credentialBuilder.setTokenServerEncodedUrl(tokenUri); - } - String projectId = (String) fileContents.get("project_id"); - if (projectId != null) { - credentialBuilder.setServiceAccountProjectId(projectId); - } - - // Don't do a refresh at this point, as it will always fail before the scopes are added. - return credentialBuilder.build(); - } - - @Beta - private static PrivateKey privateKeyFromPkcs8(String privateKeyPem) throws IOException { - Reader reader = new StringReader(privateKeyPem); - Section section = PemReader.readFirstSectionAndClose(reader, "PRIVATE KEY"); - if (section == null) { - throw new IOException("Invalid PKCS8 data."); - } - byte[] bytes = section.getBase64DecodedBytes(); - PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(bytes); - Exception unexpectedException = null; - try { - KeyFactory keyFactory = SecurityUtils.getRsaKeyFactory(); - PrivateKey privateKey = keyFactory.generatePrivate(keySpec); - return privateKey; - } catch (NoSuchAlgorithmException exception) { - unexpectedException = exception; - } catch (InvalidKeySpecException exception) { - unexpectedException = exception; - } - throw OAuth2Utils.exceptionWithCause( - new IOException("Unexpected exception reading PKCS data"), unexpectedException); - } -} +/* + * Copyright 2011 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ + +package com.google.api.client.googleapis.auth.oauth2; + +import com.google.api.client.auth.oauth2.BearerToken; +import com.google.api.client.auth.oauth2.ClientParametersAuthentication; +import com.google.api.client.auth.oauth2.Credential; +import com.google.api.client.auth.oauth2.CredentialRefreshListener; +import com.google.api.client.auth.oauth2.DataStoreCredentialRefreshListener; +import com.google.api.client.auth.oauth2.TokenRequest; +import com.google.api.client.auth.oauth2.TokenResponse; +import com.google.api.client.googleapis.auth.oauth2.GoogleClientSecrets.Details; +import com.google.api.client.googleapis.util.Utils; +import com.google.api.client.http.GenericUrl; +import com.google.api.client.http.HttpExecuteInterceptor; +import com.google.api.client.http.HttpRequestInitializer; +import com.google.api.client.http.HttpTransport; +import com.google.api.client.http.HttpUnsuccessfulResponseHandler; +import com.google.api.client.json.GenericJson; +import com.google.api.client.json.JsonFactory; +import com.google.api.client.json.JsonObjectParser; +import com.google.api.client.json.webtoken.JsonWebSignature; +import com.google.api.client.json.webtoken.JsonWebToken; +import com.google.api.client.util.Beta; +import com.google.api.client.util.Clock; +import com.google.api.client.util.Joiner; +import com.google.api.client.util.PemReader; +import com.google.api.client.util.PemReader.Section; +import com.google.api.client.util.Preconditions; +import com.google.api.client.util.SecurityUtils; +import com.google.api.client.util.store.DataStoreFactory; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.Reader; +import java.io.StringReader; +import java.security.GeneralSecurityException; +import java.security.KeyFactory; +import java.security.NoSuchAlgorithmException; +import java.security.PrivateKey; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.PKCS8EncodedKeySpec; +import java.util.Collection; +import java.util.Collections; + +/** + * Thread-safe Google-specific implementation of the OAuth 2.0 helper for accessing protected + * resources using an access token, as well as optionally refreshing the access token when it + * expires using a refresh token. + * + *

There are three modes supported: access token only, refresh token flow, and service account + * flow (with or without impersonating a user). + * + *

If all you have is an access token, you simply pass the {@link TokenResponse} to the + * credential using {@link Builder#setFromTokenResponse(TokenResponse)}. Google credential uses + * {@link BearerToken#authorizationHeaderAccessMethod()} as the access method. Sample usage: + * + *

{@code
+ * public static GoogleCredential createCredentialWithAccessTokenOnly(TokenResponse tokenResponse) {
+ *   return new GoogleCredential().setFromTokenResponse(tokenResponse);
+ * }
+ * }
+ * + *

If you have a refresh token, it is similar to the case of access token only, but you + * additionally need to pass the credential the client secrets using {@link + * Builder#setClientSecrets(GoogleClientSecrets)} or {@link Builder#setClientSecrets(String, + * String)}. Google credential uses {@link GoogleOAuthConstants#TOKEN_SERVER_URL} as the token + * server URL, and {@link ClientParametersAuthentication} with the client ID and secret as the + * client authentication. Sample usage: + * + *

{@code
+ * public static GoogleCredential createCredentialWithRefreshToken(
+ *     HttpTransport transport, JsonFactory jsonFactory,
+ *     GoogleClientSecrets clientSecrets, TokenResponse tokenResponse) {
+ *  return new GoogleCredential.Builder().setTransport(transport)
+ *                        .setJsonFactory(jsonFactory)
+ *                        .setClientSecrets(clientSecrets)
+ *                        .build()
+ *                        .setFromTokenResponse(tokenResponse);
+ * }
+ * }
+ * + *

The service + * account flow is used when you want to access data owned by your client application. You + * download the private key in a {@code .p12} file from the Google APIs Console. Use {@link + * Builder#setServiceAccountId(String)}, {@link + * Builder#setServiceAccountPrivateKeyFromP12File(File)}, and {@link + * Builder#setServiceAccountScopes(Collection)}. Sample usage: + * + *

{@code
+ * public static GoogleCredential createCredentialForServiceAccount(HttpTransport transport,
+ *     JsonFactory jsonFactory,
+ *     String serviceAccountId, Collection<String> serviceAccountScopes, File p12File)
+ *     throws GeneralSecurityException, IOException {
+ *   return new GoogleCredential.Builder().setTransport(transport).setJsonFactory(jsonFactory)
+ *       .setServiceAccountId(serviceAccountId).setServiceAccountScopes(serviceAccountScopes)
+ *       .setServiceAccountPrivateKeyFromP12File(p12File).build();
+ * }
+ * }
+ * + *

You can also use the service account flow to impersonate a user in a domain that you own. This + * is very similar to the service account flow above, but you additionally call {@link + * Builder#setServiceAccountUser(String)}. Sample usage: + * + *

{@code
+ * public static GoogleCredential createCredentialForServiceAccountImpersonateUser
+ *     (HttpTransport transport, JsonFactory jsonFactory, String serviceAccountId,
+ *      Collection<String> serviceAccountScopes, File p12File,
+ *      String serviceAccountUser) throws GeneralSecurityException, IOException {
+ *   return new GoogleCredential.Builder()
+ *       .setTransport(transport)
+ *       .setJsonFactory(jsonFactory)
+ *       .setServiceAccountId(serviceAccountId)
+ *       .setServiceAccountScopes(serviceAccountScopes)
+ *       .setServiceAccountPrivateKeyFromP12File(p12File)
+ *       .setServiceAccountUser(serviceAccountUser)
+ *       .build();
+ * }
+ * }
+ * + *

If you need to persist the access token in a data store, use {@link DataStoreFactory} and + * {@link Builder#addRefreshListener(CredentialRefreshListener)} with {@link + * DataStoreCredentialRefreshListener}. + * + *

If you have a custom request initializer, request execute interceptor, or unsuccessful + * response handler, take a look at the sample usage for {@link HttpExecuteInterceptor} and {@link + * HttpUnsuccessfulResponseHandler}, which are interfaces that this class also implements. + * + * @author Yaniv Inbar + * @since 1.7 + * @deprecated Please use + * google-auth-library for handling Application Default Credentials and other non-OAuth2 + * based authentication. + */ +@Deprecated +public class GoogleCredential extends Credential { + + static final String USER_FILE_TYPE = "authorized_user"; + static final String SERVICE_ACCOUNT_FILE_TYPE = "service_account"; + + @Beta + private static final DefaultCredentialProvider defaultCredentialProvider = + new DefaultCredentialProvider(); + + /** + * {@link Beta}
+ * Returns the Application Default Credentials. + * + *

Returns the Application Default Credentials which are credentials that identify and + * authorize the whole application. This is the built-in service account if running on Google + * Compute Engine or the credentials file from the path in the environment variable + * GOOGLE_APPLICATION_CREDENTIALS. + * + * @return the credential instance. + * @throws IOException if the credential cannot be created in the current environment. + */ + @Beta + public static GoogleCredential getApplicationDefault() throws IOException { + return getApplicationDefault(Utils.getDefaultTransport(), Utils.getDefaultJsonFactory(), false); + } + + /** + * {@link Beta}
+ * Returns the Application Default Credentials. + * + *

Returns the Application Default Credentials which are credentials that identify and + * authorize the whole application. This is the built-in service account if running on Google + * Compute Engine or the credentials file from the path in the environment variable + * GOOGLE_APPLICATION_CREDENTIALS. + * + * @param resetCachedCredentials whether to reset the cached credentials + * @return the credential instance. + * @throws IOException if the credential cannot be created in the current environment. + */ + @Beta + public static GoogleCredential getApplicationDefault(boolean resetCachedCredentials) + throws IOException { + return getApplicationDefault( + Utils.getDefaultTransport(), Utils.getDefaultJsonFactory(), resetCachedCredentials); + } + + /** + * {@link Beta}
+ * Returns the Application Default Credentials. + * + *

Returns the Application Default Credentials which are credentials that identify and + * authorize the whole application. This is the built-in service account if running on Google + * Compute Engine or the credentials file from the path in the environment variable + * GOOGLE_APPLICATION_CREDENTIALS. + * + * @param resetCachedCredentials whether to reset the cached credentials. + * @param transport the transport for Http calls. + * @param jsonFactory the factory for Json parsing and formatting. + * @return the credential instance. + * @throws IOException if the credential cannot be created in the current environment. + */ + @Beta + public static GoogleCredential getApplicationDefault( + HttpTransport transport, JsonFactory jsonFactory, boolean resetCachedCredentials) + throws IOException { + Preconditions.checkNotNull(transport); + Preconditions.checkNotNull(jsonFactory); + return defaultCredentialProvider.getDefaultCredential( + transport, jsonFactory, resetCachedCredentials); + } + + /** + * {@link Beta}
+ * Returns the Application Default Credentials. + * + *

Returns the Application Default Credentials which are credentials that identify and + * authorize the whole application. This is the built-in service account if running on Google + * Compute Engine or the credentials file from the path in the environment variable + * GOOGLE_APPLICATION_CREDENTIALS. + * + * @param transport the transport for Http calls. + * @param jsonFactory the factory for Json parsing and formatting. + * @return the credential instance. + * @throws IOException if the credential cannot be created in the current environment. + */ + @Beta + public static GoogleCredential getApplicationDefault( + HttpTransport transport, JsonFactory jsonFactory) throws IOException { + return getApplicationDefault(transport, jsonFactory, false); + } + + /** + * {@link Beta}
+ * Return a credential defined by a Json file. + * + *

Important: If you accept a credential configuration (credential JSON/File/Stream) from an + * external source for authentication to Google Cloud Platform, you must validate it before + * providing it to any Google API or library. Providing an unvalidated credential configuration to + * Google APIs can compromise the security of your systems and data. For more information, refer + * to {@link documentation}. + * + * @param credentialStream the stream with the credential definition. + * @return the credential defined by the credentialStream. + * @throws IOException if the credential cannot be created from the stream. + */ + @Beta + public static GoogleCredential fromStream(InputStream credentialStream) throws IOException { + return fromStream(credentialStream, Utils.getDefaultTransport(), Utils.getDefaultJsonFactory()); + } + + /** + * {@link Beta}
+ * Return a credential defined by a Json file. + * + *

Important: If you accept a credential configuration (credential JSON/File/Stream) from an + * external source for authentication to Google Cloud Platform, you must validate it before + * providing it to any Google API or library. Providing an unvalidated credential configuration to + * Google APIs can compromise the security of your systems and data. For more information, refer + * to {@link documentation}. + * + * @param credentialStream the stream with the credential definition. + * @param transport the transport for Http calls. + * @param jsonFactory the factory for Json parsing and formatting. + * @return the credential defined by the credentialStream. + * @throws IOException if the credential cannot be created from the stream. + */ + @Beta + public static GoogleCredential fromStream( + InputStream credentialStream, HttpTransport transport, JsonFactory jsonFactory) + throws IOException { + Preconditions.checkNotNull(credentialStream); + Preconditions.checkNotNull(transport); + Preconditions.checkNotNull(jsonFactory); + + JsonObjectParser parser = new JsonObjectParser(jsonFactory); + GenericJson fileContents = + parser.parseAndClose(credentialStream, OAuth2Utils.UTF_8, GenericJson.class); + String fileType = (String) fileContents.get("type"); + if (fileType == null) { + throw new IOException("Error reading credentials from stream, 'type' field not specified."); + } + if (USER_FILE_TYPE.equals(fileType)) { + return fromStreamUser(fileContents, transport, jsonFactory); + } + if (SERVICE_ACCOUNT_FILE_TYPE.equals(fileType)) { + return fromStreamServiceAccount(fileContents, transport, jsonFactory); + } + throw new IOException( + String.format( + "Error reading credentials from stream, 'type' value '%s' not recognized." + + " Expecting '%s' or '%s'.", + fileType, USER_FILE_TYPE, SERVICE_ACCOUNT_FILE_TYPE)); + } + + /** + * Service account ID (typically an e-mail address) or {@code null} if not using the service + * account flow. + */ + private String serviceAccountId; + + /** + * Service account Project ID or {@code null} if not present, either because this is not using the + * service account flow, or is using an older version of the service account configuration. + */ + private String serviceAccountProjectId; + + /** + * Collection of OAuth scopes to use with the service account flow or {@code null} if not using + * the service account flow. + */ + private Collection serviceAccountScopes; + + /** + * Private key to use with the service account flow or {@code null} if not using the service + * account flow. + */ + private PrivateKey serviceAccountPrivateKey; + + /** + * ID of private key to use with the service account flow or {@code null} if not using the service + * account flow. + */ + private String serviceAccountPrivateKeyId; + + /** + * Email address of the user the application is trying to impersonate in the service account flow + * or {@code null} for none or if not using the service account flow. + */ + private String serviceAccountUser; + + /** + * Constructor with the ability to access protected resources, but not refresh tokens. + * + *

To use with the ability to refresh tokens, use {@link Builder}. + */ + public GoogleCredential() { + this(new Builder()); + } + + /** + * @param builder Google credential builder + * @since 1.14 + */ + protected GoogleCredential(Builder builder) { + super(builder); + if (builder.serviceAccountPrivateKey == null) { + Preconditions.checkArgument( + builder.serviceAccountId == null + && builder.serviceAccountScopes == null + && builder.serviceAccountUser == null); + } else { + serviceAccountId = Preconditions.checkNotNull(builder.serviceAccountId); + serviceAccountProjectId = builder.serviceAccountProjectId; + serviceAccountScopes = + (builder.serviceAccountScopes == null) + ? Collections.emptyList() + : Collections.unmodifiableCollection(builder.serviceAccountScopes); + serviceAccountPrivateKey = builder.serviceAccountPrivateKey; + serviceAccountPrivateKeyId = builder.serviceAccountPrivateKeyId; + serviceAccountUser = builder.serviceAccountUser; + } + } + + @Override + public GoogleCredential setAccessToken(String accessToken) { + return (GoogleCredential) super.setAccessToken(accessToken); + } + + @Override + public GoogleCredential setRefreshToken(String refreshToken) { + if (refreshToken != null) { + Preconditions.checkArgument( + getJsonFactory() != null && getTransport() != null && getClientAuthentication() != null, + "Please use the Builder and call setJsonFactory, setTransport and setClientSecrets"); + } + return (GoogleCredential) super.setRefreshToken(refreshToken); + } + + @Override + public GoogleCredential setExpirationTimeMilliseconds(Long expirationTimeMilliseconds) { + return (GoogleCredential) super.setExpirationTimeMilliseconds(expirationTimeMilliseconds); + } + + @Override + public GoogleCredential setExpiresInSeconds(Long expiresIn) { + return (GoogleCredential) super.setExpiresInSeconds(expiresIn); + } + + @Override + public GoogleCredential setFromTokenResponse(TokenResponse tokenResponse) { + return (GoogleCredential) super.setFromTokenResponse(tokenResponse); + } + + @Override + @Beta + protected TokenResponse executeRefreshToken() throws IOException { + if (serviceAccountPrivateKey == null) { + return super.executeRefreshToken(); + } + // service accounts: no refresh token; instead use private key to request new access token + JsonWebSignature.Header header = new JsonWebSignature.Header(); + header.setAlgorithm("RS256"); + header.setType("JWT"); + header.setKeyId(serviceAccountPrivateKeyId); + JsonWebToken.Payload payload = new JsonWebToken.Payload(); + long currentTime = getClock().currentTimeMillis(); + payload.setIssuer(serviceAccountId); + payload.setAudience(getTokenServerEncodedUrl()); + payload.setIssuedAtTimeSeconds(currentTime / 1000); + payload.setExpirationTimeSeconds(currentTime / 1000 + 3600); + payload.setSubject(serviceAccountUser); + payload.put("scope", Joiner.on(' ').join(serviceAccountScopes)); + try { + String assertion = + JsonWebSignature.signUsingRsaSha256( + serviceAccountPrivateKey, getJsonFactory(), header, payload); + TokenRequest request = + new TokenRequest( + getTransport(), + getJsonFactory(), + new GenericUrl(getTokenServerEncodedUrl()), + "urn:ietf:params:oauth:grant-type:jwt-bearer"); + request.put("assertion", assertion); + return request.execute(); + } catch (GeneralSecurityException exception) { + IOException e = new IOException(); + e.initCause(exception); + throw e; + } + } + + /** + * Returns the service account ID (typically an e-mail address) or {@code null} if not using the + * service account flow. + */ + public final String getServiceAccountId() { + return serviceAccountId; + } + + /** + * Returns the service account Project ID or {@code null} if not present, either because this is + * not using the service account flow, or is using an older version of the service account + * configuration. + */ + public final String getServiceAccountProjectId() { + return serviceAccountProjectId; + } + + /** + * Returns a collection of OAuth scopes to use with the service account flow or {@code null} if + * not using the service account flow. + */ + public final Collection getServiceAccountScopes() { + return serviceAccountScopes; + } + + /** + * Returns the space-separated OAuth scopes to use with the service account flow or {@code null} + * if not using the service account flow. + * + * @since 1.15 + */ + public final String getServiceAccountScopesAsString() { + return serviceAccountScopes == null ? null : Joiner.on(' ').join(serviceAccountScopes); + } + + /** + * Returns the private key to use with the service account flow or {@code null} if not using the + * service account flow. + */ + public final PrivateKey getServiceAccountPrivateKey() { + return serviceAccountPrivateKey; + } + + /** + * {@link Beta}
+ * Returns the ID of the private key to use with the service account flow or {@code null} if not + * using the service account flow. + */ + @Beta + public final String getServiceAccountPrivateKeyId() { + return serviceAccountPrivateKeyId; + } + + /** + * Returns the email address of the user the application is trying to impersonate in the service + * account flow or {@code null} for none or if not using the service account flow. + */ + public final String getServiceAccountUser() { + return serviceAccountUser; + } + + /** + * {@link Beta}
+ * Indicates whether the credential requires scopes to be specified by calling createScoped before + * use. + */ + @Beta + public boolean createScopedRequired() { + if (serviceAccountPrivateKey == null) { + return false; + } + return (serviceAccountScopes == null || serviceAccountScopes.isEmpty()); + } + + /** + * {@link Beta}
+ * For credentials that require scopes, creates a copy of the credential with the specified + * scopes. + */ + @Beta + public GoogleCredential createScoped(Collection scopes) { + if (serviceAccountPrivateKey == null) { + return this; + } + return toBuilder().setServiceAccountScopes(scopes).build(); + } + + /** + * {@link Beta}
+ * For service accounts that need to delegate to a specific user, create a copy of the credential + * with the specified user. + */ + @Beta + public GoogleCredential createDelegated(String user) { + if (serviceAccountPrivateKey == null) { + return this; + } + return toBuilder().setServiceAccountUser(user).build(); + } + + /** + * {@link Beta}
+ * Create a builder from this credential. + */ + @Beta + public Builder toBuilder() { + Builder builder = + new GoogleCredential.Builder() + .setServiceAccountPrivateKey(serviceAccountPrivateKey) + .setServiceAccountPrivateKeyId(serviceAccountPrivateKeyId) + .setServiceAccountId(serviceAccountId) + .setServiceAccountProjectId(serviceAccountProjectId) + .setServiceAccountUser(serviceAccountUser) + .setServiceAccountScopes(serviceAccountScopes) + .setTokenServerEncodedUrl(getTokenServerEncodedUrl()) + .setTransport(getTransport()) + .setJsonFactory(getJsonFactory()) + .setClock(getClock()); + + builder.setClientAuthentication(getClientAuthentication()); + + return builder; + } + + /** + * Google credential builder. + * + *

Implementation is not thread-safe. + */ + public static class Builder extends Credential.Builder { + + /** Service account ID (typically an e-mail address) or {@code null} for none. */ + String serviceAccountId; + + /** Collection of OAuth scopes to use with the service account flow or {@code null} for none. */ + Collection serviceAccountScopes; + + /** Private key to use with the service account flow or {@code null} for none. */ + PrivateKey serviceAccountPrivateKey; + + /** Id of the private key to use with the service account flow or {@code null} for none. */ + String serviceAccountPrivateKeyId; + + /** Project ID associated with the Service Account. */ + String serviceAccountProjectId; + + /** + * Email address of the user the application is trying to impersonate in the service account + * flow or {@code null} for none. + */ + String serviceAccountUser; + + public Builder() { + super(BearerToken.authorizationHeaderAccessMethod()); + setTokenServerEncodedUrl(GoogleOAuthConstants.TOKEN_SERVER_URL); + } + + @Override + public GoogleCredential build() { + return new GoogleCredential(this); + } + + @Override + public Builder setTransport(HttpTransport transport) { + return (Builder) super.setTransport(transport); + } + + @Override + public Builder setJsonFactory(JsonFactory jsonFactory) { + return (Builder) super.setJsonFactory(jsonFactory); + } + + /** @since 1.9 */ + @Override + public Builder setClock(Clock clock) { + return (Builder) super.setClock(clock); + } + + /** + * Sets the client identifier and secret. + * + *

Overriding is only supported for the purpose of calling the super implementation and + * changing the return type, but nothing else. + */ + public Builder setClientSecrets(String clientId, String clientSecret) { + setClientAuthentication(new ClientParametersAuthentication(clientId, clientSecret)); + return this; + } + + /** + * Sets the client secrets. + * + *

Overriding is only supported for the purpose of calling the super implementation and + * changing the return type, but nothing else. + */ + public Builder setClientSecrets(GoogleClientSecrets clientSecrets) { + Details details = clientSecrets.getDetails(); + setClientAuthentication( + new ClientParametersAuthentication(details.getClientId(), details.getClientSecret())); + return this; + } + + /** Returns the service account ID (typically an e-mail address) or {@code null} for none. */ + public final String getServiceAccountId() { + return serviceAccountId; + } + + /** + * Sets the service account ID (typically an e-mail address) or {@code null} for none. + * + *

Overriding is only supported for the purpose of calling the super implementation and + * changing the return type, but nothing else. + */ + public Builder setServiceAccountId(String serviceAccountId) { + this.serviceAccountId = serviceAccountId; + return this; + } + + /** Returns the service account Project ID or {@code null} for none. */ + public final String getServiceAccountProjectId() { + return serviceAccountProjectId; + } + + /** + * Sets the service account Project ID or {@code null} for none. + * + *

Overriding is only supported for the purpose of calling the super implementation and + * changing the return type, but nothing else. + */ + public Builder setServiceAccountProjectId(String serviceAccountProjectId) { + this.serviceAccountProjectId = serviceAccountProjectId; + return this; + } + + /** + * Returns a collection of OAuth scopes to use with the service account flow or {@code null} for + * none. + */ + public final Collection getServiceAccountScopes() { + return serviceAccountScopes; + } + + /** + * Sets the space-separated OAuth scopes to use with the service account flow or {@code null} + * for none. + * + *

Overriding is only supported for the purpose of calling the super implementation and + * changing the return type, but nothing else. + * + * @param serviceAccountScopes collection of scopes to be joined by a space separator (or a + * single value containing multiple space-separated scopes) + * @since 1.15 + */ + public Builder setServiceAccountScopes(Collection serviceAccountScopes) { + this.serviceAccountScopes = serviceAccountScopes; + return this; + } + + /** Returns the private key to use with the service account flow or {@code null} for none. */ + public final PrivateKey getServiceAccountPrivateKey() { + return serviceAccountPrivateKey; + } + + /** + * Sets the private key to use with the service account flow or {@code null} for none. + * + *

Overriding is only supported for the purpose of calling the super implementation and + * changing the return type, but nothing else. + */ + public Builder setServiceAccountPrivateKey(PrivateKey serviceAccountPrivateKey) { + this.serviceAccountPrivateKey = serviceAccountPrivateKey; + return this; + } + + /** + * {@link Beta}
+ * Returns the id of the private key to use with the service account flow or {@code null} for + * none. + */ + @Beta + public final String getServiceAccountPrivateKeyId() { + return serviceAccountPrivateKeyId; + } + + /** + * {@link Beta}
+ * Sets the id of the private key to use with the service account flow or {@code null} for none. + * + *

Overriding is only supported for the purpose of calling the super implementation and + * changing the return type, but nothing else. + */ + @Beta + public Builder setServiceAccountPrivateKeyId(String serviceAccountPrivateKeyId) { + this.serviceAccountPrivateKeyId = serviceAccountPrivateKeyId; + return this; + } + + /** + * Sets the private key to use with the service account flow or {@code null} for none. + * + *

Overriding is only supported for the purpose of calling the super implementation and + * changing the return type, but nothing else. + * + * @param p12File p12 file object + */ + public Builder setServiceAccountPrivateKeyFromP12File(File p12File) + throws GeneralSecurityException, IOException { + setServiceAccountPrivateKeyFromP12File(new FileInputStream(p12File)); + return this; + } + + /** + * Sets the private key to use with the service account flow or {@code null} for none. + * + *

Overriding is only supported for the purpose of calling the super implementation and + * changing the return type, but nothing else. + * + * @param p12FileInputStream input stream to the p12 file. This file is closed at the end of + * this method in a finally block. + */ + public Builder setServiceAccountPrivateKeyFromP12File(InputStream p12FileInputStream) + throws GeneralSecurityException, IOException { + serviceAccountPrivateKey = + SecurityUtils.loadPrivateKeyFromKeyStore( + SecurityUtils.getPkcs12KeyStore(), + p12FileInputStream, + "notasecret", + "privatekey", + "notasecret"); + return this; + } + + /** + * {@link Beta}
+ * Sets the private key to use with the service account flow or {@code null} for none. + * + *

Overriding is only supported for the purpose of calling the super implementation and + * changing the return type, but nothing else. + * + * @param pemFile input stream to the PEM file (closed at the end of this method in a finally + * block) + * @since 1.13 + */ + @Beta + public Builder setServiceAccountPrivateKeyFromPemFile(File pemFile) + throws GeneralSecurityException, IOException { + byte[] bytes = + PemReader.readFirstSectionAndClose(new FileReader(pemFile), "PRIVATE KEY") + .getBase64DecodedBytes(); + serviceAccountPrivateKey = + SecurityUtils.getRsaKeyFactory().generatePrivate(new PKCS8EncodedKeySpec(bytes)); + return this; + } + + /** + * Returns the email address of the user the application is trying to impersonate in the service + * account flow or {@code null} for none. + */ + public final String getServiceAccountUser() { + return serviceAccountUser; + } + + /** + * Sets the email address of the user the application is trying to impersonate in the service + * account flow or {@code null} for none. + * + *

Overriding is only supported for the purpose of calling the super implementation and + * changing the return type, but nothing else. + */ + public Builder setServiceAccountUser(String serviceAccountUser) { + this.serviceAccountUser = serviceAccountUser; + return this; + } + + @Override + public Builder setRequestInitializer(HttpRequestInitializer requestInitializer) { + return (Builder) super.setRequestInitializer(requestInitializer); + } + + @Override + public Builder addRefreshListener(CredentialRefreshListener refreshListener) { + return (Builder) super.addRefreshListener(refreshListener); + } + + @Override + public Builder setRefreshListeners(Collection refreshListeners) { + return (Builder) super.setRefreshListeners(refreshListeners); + } + + @Override + public Builder setTokenServerUrl(GenericUrl tokenServerUrl) { + return (Builder) super.setTokenServerUrl(tokenServerUrl); + } + + @Override + public Builder setTokenServerEncodedUrl(String tokenServerEncodedUrl) { + return (Builder) super.setTokenServerEncodedUrl(tokenServerEncodedUrl); + } + + @Override + public Builder setClientAuthentication(HttpExecuteInterceptor clientAuthentication) { + return (Builder) super.setClientAuthentication(clientAuthentication); + } + } + + @Beta + private static GoogleCredential fromStreamUser( + GenericJson fileContents, HttpTransport transport, JsonFactory jsonFactory) + throws IOException { + String clientId = (String) fileContents.get("client_id"); + String clientSecret = (String) fileContents.get("client_secret"); + String refreshToken = (String) fileContents.get("refresh_token"); + if (clientId == null || clientSecret == null || refreshToken == null) { + throw new IOException( + "Error reading user credential from stream, " + + " expecting 'client_id', 'client_secret' and 'refresh_token'."); + } + + GoogleCredential credential = + new GoogleCredential.Builder() + .setClientSecrets(clientId, clientSecret) + .setTransport(transport) + .setJsonFactory(jsonFactory) + .build(); + credential.setRefreshToken(refreshToken); + + // Do a refresh so we can fail early rather than return an unusable credential + credential.refreshToken(); + return credential; + } + + @Beta + private static GoogleCredential fromStreamServiceAccount( + GenericJson fileContents, HttpTransport transport, JsonFactory jsonFactory) + throws IOException { + String clientId = (String) fileContents.get("client_id"); + String clientEmail = (String) fileContents.get("client_email"); + String privateKeyPem = (String) fileContents.get("private_key"); + String privateKeyId = (String) fileContents.get("private_key_id"); + if (clientId == null || clientEmail == null || privateKeyPem == null || privateKeyId == null) { + throw new IOException( + "Error reading service account credential from stream, " + + "expecting 'client_id', 'client_email', 'private_key' and 'private_key_id'."); + } + + PrivateKey privateKey = privateKeyFromPkcs8(privateKeyPem); + + Collection emptyScopes = Collections.emptyList(); + + Builder credentialBuilder = + new GoogleCredential.Builder() + .setTransport(transport) + .setJsonFactory(jsonFactory) + .setServiceAccountId(clientEmail) + .setServiceAccountScopes(emptyScopes) + .setServiceAccountPrivateKey(privateKey) + .setServiceAccountPrivateKeyId(privateKeyId); + String tokenUri = (String) fileContents.get("token_uri"); + if (tokenUri != null) { + credentialBuilder.setTokenServerEncodedUrl(tokenUri); + } + String projectId = (String) fileContents.get("project_id"); + if (projectId != null) { + credentialBuilder.setServiceAccountProjectId(projectId); + } + + // Don't do a refresh at this point, as it will always fail before the scopes are added. + return credentialBuilder.build(); + } + + @Beta + private static PrivateKey privateKeyFromPkcs8(String privateKeyPem) throws IOException { + Reader reader = new StringReader(privateKeyPem); + Section section = PemReader.readFirstSectionAndClose(reader, "PRIVATE KEY"); + if (section == null) { + throw new IOException("Invalid PKCS8 data."); + } + byte[] bytes = section.getBase64DecodedBytes(); + PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(bytes); + Exception unexpectedException = null; + try { + KeyFactory keyFactory = SecurityUtils.getRsaKeyFactory(); + PrivateKey privateKey = keyFactory.generatePrivate(keySpec); + return privateKey; + } catch (NoSuchAlgorithmException exception) { + unexpectedException = exception; + } catch (InvalidKeySpecException exception) { + unexpectedException = exception; + } + throw OAuth2Utils.exceptionWithCause( + new IOException("Unexpected exception reading PKCS data"), unexpectedException); + } +} From cd1a5ed2b2d5750796ad0813adcecafe5d613951 Mon Sep 17 00:00:00 2001 From: Diego Alonso Marquez Palacios Date: Fri, 5 Sep 2025 15:22:28 -0400 Subject: [PATCH 5/6] chore: LF line endings --- .../auth/oauth2/GoogleCredential.java | 1872 +++++++++-------- 1 file changed, 937 insertions(+), 935 deletions(-) diff --git a/google-api-client/src/main/java/com/google/api/client/googleapis/auth/oauth2/GoogleCredential.java b/google-api-client/src/main/java/com/google/api/client/googleapis/auth/oauth2/GoogleCredential.java index 7de7e32d4..0b6c39ee8 100644 --- a/google-api-client/src/main/java/com/google/api/client/googleapis/auth/oauth2/GoogleCredential.java +++ b/google-api-client/src/main/java/com/google/api/client/googleapis/auth/oauth2/GoogleCredential.java @@ -1,935 +1,937 @@ -/* - * Copyright 2011 Google Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except - * in compliance with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */ - -package com.google.api.client.googleapis.auth.oauth2; - -import com.google.api.client.auth.oauth2.BearerToken; -import com.google.api.client.auth.oauth2.ClientParametersAuthentication; -import com.google.api.client.auth.oauth2.Credential; -import com.google.api.client.auth.oauth2.CredentialRefreshListener; -import com.google.api.client.auth.oauth2.DataStoreCredentialRefreshListener; -import com.google.api.client.auth.oauth2.TokenRequest; -import com.google.api.client.auth.oauth2.TokenResponse; -import com.google.api.client.googleapis.auth.oauth2.GoogleClientSecrets.Details; -import com.google.api.client.googleapis.util.Utils; -import com.google.api.client.http.GenericUrl; -import com.google.api.client.http.HttpExecuteInterceptor; -import com.google.api.client.http.HttpRequestInitializer; -import com.google.api.client.http.HttpTransport; -import com.google.api.client.http.HttpUnsuccessfulResponseHandler; -import com.google.api.client.json.GenericJson; -import com.google.api.client.json.JsonFactory; -import com.google.api.client.json.JsonObjectParser; -import com.google.api.client.json.webtoken.JsonWebSignature; -import com.google.api.client.json.webtoken.JsonWebToken; -import com.google.api.client.util.Beta; -import com.google.api.client.util.Clock; -import com.google.api.client.util.Joiner; -import com.google.api.client.util.PemReader; -import com.google.api.client.util.PemReader.Section; -import com.google.api.client.util.Preconditions; -import com.google.api.client.util.SecurityUtils; -import com.google.api.client.util.store.DataStoreFactory; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileReader; -import java.io.IOException; -import java.io.InputStream; -import java.io.Reader; -import java.io.StringReader; -import java.security.GeneralSecurityException; -import java.security.KeyFactory; -import java.security.NoSuchAlgorithmException; -import java.security.PrivateKey; -import java.security.spec.InvalidKeySpecException; -import java.security.spec.PKCS8EncodedKeySpec; -import java.util.Collection; -import java.util.Collections; - -/** - * Thread-safe Google-specific implementation of the OAuth 2.0 helper for accessing protected - * resources using an access token, as well as optionally refreshing the access token when it - * expires using a refresh token. - * - *

There are three modes supported: access token only, refresh token flow, and service account - * flow (with or without impersonating a user). - * - *

If all you have is an access token, you simply pass the {@link TokenResponse} to the - * credential using {@link Builder#setFromTokenResponse(TokenResponse)}. Google credential uses - * {@link BearerToken#authorizationHeaderAccessMethod()} as the access method. Sample usage: - * - *

{@code
- * public static GoogleCredential createCredentialWithAccessTokenOnly(TokenResponse tokenResponse) {
- *   return new GoogleCredential().setFromTokenResponse(tokenResponse);
- * }
- * }
- * - *

If you have a refresh token, it is similar to the case of access token only, but you - * additionally need to pass the credential the client secrets using {@link - * Builder#setClientSecrets(GoogleClientSecrets)} or {@link Builder#setClientSecrets(String, - * String)}. Google credential uses {@link GoogleOAuthConstants#TOKEN_SERVER_URL} as the token - * server URL, and {@link ClientParametersAuthentication} with the client ID and secret as the - * client authentication. Sample usage: - * - *

{@code
- * public static GoogleCredential createCredentialWithRefreshToken(
- *     HttpTransport transport, JsonFactory jsonFactory,
- *     GoogleClientSecrets clientSecrets, TokenResponse tokenResponse) {
- *  return new GoogleCredential.Builder().setTransport(transport)
- *                        .setJsonFactory(jsonFactory)
- *                        .setClientSecrets(clientSecrets)
- *                        .build()
- *                        .setFromTokenResponse(tokenResponse);
- * }
- * }
- * - *

The service - * account flow is used when you want to access data owned by your client application. You - * download the private key in a {@code .p12} file from the Google APIs Console. Use {@link - * Builder#setServiceAccountId(String)}, {@link - * Builder#setServiceAccountPrivateKeyFromP12File(File)}, and {@link - * Builder#setServiceAccountScopes(Collection)}. Sample usage: - * - *

{@code
- * public static GoogleCredential createCredentialForServiceAccount(HttpTransport transport,
- *     JsonFactory jsonFactory,
- *     String serviceAccountId, Collection<String> serviceAccountScopes, File p12File)
- *     throws GeneralSecurityException, IOException {
- *   return new GoogleCredential.Builder().setTransport(transport).setJsonFactory(jsonFactory)
- *       .setServiceAccountId(serviceAccountId).setServiceAccountScopes(serviceAccountScopes)
- *       .setServiceAccountPrivateKeyFromP12File(p12File).build();
- * }
- * }
- * - *

You can also use the service account flow to impersonate a user in a domain that you own. This - * is very similar to the service account flow above, but you additionally call {@link - * Builder#setServiceAccountUser(String)}. Sample usage: - * - *

{@code
- * public static GoogleCredential createCredentialForServiceAccountImpersonateUser
- *     (HttpTransport transport, JsonFactory jsonFactory, String serviceAccountId,
- *      Collection<String> serviceAccountScopes, File p12File,
- *      String serviceAccountUser) throws GeneralSecurityException, IOException {
- *   return new GoogleCredential.Builder()
- *       .setTransport(transport)
- *       .setJsonFactory(jsonFactory)
- *       .setServiceAccountId(serviceAccountId)
- *       .setServiceAccountScopes(serviceAccountScopes)
- *       .setServiceAccountPrivateKeyFromP12File(p12File)
- *       .setServiceAccountUser(serviceAccountUser)
- *       .build();
- * }
- * }
- * - *

If you need to persist the access token in a data store, use {@link DataStoreFactory} and - * {@link Builder#addRefreshListener(CredentialRefreshListener)} with {@link - * DataStoreCredentialRefreshListener}. - * - *

If you have a custom request initializer, request execute interceptor, or unsuccessful - * response handler, take a look at the sample usage for {@link HttpExecuteInterceptor} and {@link - * HttpUnsuccessfulResponseHandler}, which are interfaces that this class also implements. - * - * @author Yaniv Inbar - * @since 1.7 - * @deprecated Please use - * google-auth-library for handling Application Default Credentials and other non-OAuth2 - * based authentication. - */ -@Deprecated -public class GoogleCredential extends Credential { - - static final String USER_FILE_TYPE = "authorized_user"; - static final String SERVICE_ACCOUNT_FILE_TYPE = "service_account"; - - @Beta - private static final DefaultCredentialProvider defaultCredentialProvider = - new DefaultCredentialProvider(); - - /** - * {@link Beta}
- * Returns the Application Default Credentials. - * - *

Returns the Application Default Credentials which are credentials that identify and - * authorize the whole application. This is the built-in service account if running on Google - * Compute Engine or the credentials file from the path in the environment variable - * GOOGLE_APPLICATION_CREDENTIALS. - * - * @return the credential instance. - * @throws IOException if the credential cannot be created in the current environment. - */ - @Beta - public static GoogleCredential getApplicationDefault() throws IOException { - return getApplicationDefault(Utils.getDefaultTransport(), Utils.getDefaultJsonFactory(), false); - } - - /** - * {@link Beta}
- * Returns the Application Default Credentials. - * - *

Returns the Application Default Credentials which are credentials that identify and - * authorize the whole application. This is the built-in service account if running on Google - * Compute Engine or the credentials file from the path in the environment variable - * GOOGLE_APPLICATION_CREDENTIALS. - * - * @param resetCachedCredentials whether to reset the cached credentials - * @return the credential instance. - * @throws IOException if the credential cannot be created in the current environment. - */ - @Beta - public static GoogleCredential getApplicationDefault(boolean resetCachedCredentials) - throws IOException { - return getApplicationDefault( - Utils.getDefaultTransport(), Utils.getDefaultJsonFactory(), resetCachedCredentials); - } - - /** - * {@link Beta}
- * Returns the Application Default Credentials. - * - *

Returns the Application Default Credentials which are credentials that identify and - * authorize the whole application. This is the built-in service account if running on Google - * Compute Engine or the credentials file from the path in the environment variable - * GOOGLE_APPLICATION_CREDENTIALS. - * - * @param resetCachedCredentials whether to reset the cached credentials. - * @param transport the transport for Http calls. - * @param jsonFactory the factory for Json parsing and formatting. - * @return the credential instance. - * @throws IOException if the credential cannot be created in the current environment. - */ - @Beta - public static GoogleCredential getApplicationDefault( - HttpTransport transport, JsonFactory jsonFactory, boolean resetCachedCredentials) - throws IOException { - Preconditions.checkNotNull(transport); - Preconditions.checkNotNull(jsonFactory); - return defaultCredentialProvider.getDefaultCredential( - transport, jsonFactory, resetCachedCredentials); - } - - /** - * {@link Beta}
- * Returns the Application Default Credentials. - * - *

Returns the Application Default Credentials which are credentials that identify and - * authorize the whole application. This is the built-in service account if running on Google - * Compute Engine or the credentials file from the path in the environment variable - * GOOGLE_APPLICATION_CREDENTIALS. - * - * @param transport the transport for Http calls. - * @param jsonFactory the factory for Json parsing and formatting. - * @return the credential instance. - * @throws IOException if the credential cannot be created in the current environment. - */ - @Beta - public static GoogleCredential getApplicationDefault( - HttpTransport transport, JsonFactory jsonFactory) throws IOException { - return getApplicationDefault(transport, jsonFactory, false); - } - - /** - * {@link Beta}
- * Return a credential defined by a Json file. - * - *

Important: If you accept a credential configuration (credential JSON/File/Stream) from an - * external source for authentication to Google Cloud Platform, you must validate it before - * providing it to any Google API or library. Providing an unvalidated credential configuration to - * Google APIs can compromise the security of your systems and data. For more information, refer - * to {@link documentation}. - * - * @param credentialStream the stream with the credential definition. - * @return the credential defined by the credentialStream. - * @throws IOException if the credential cannot be created from the stream. - */ - @Beta - public static GoogleCredential fromStream(InputStream credentialStream) throws IOException { - return fromStream(credentialStream, Utils.getDefaultTransport(), Utils.getDefaultJsonFactory()); - } - - /** - * {@link Beta}
- * Return a credential defined by a Json file. - * - *

Important: If you accept a credential configuration (credential JSON/File/Stream) from an - * external source for authentication to Google Cloud Platform, you must validate it before - * providing it to any Google API or library. Providing an unvalidated credential configuration to - * Google APIs can compromise the security of your systems and data. For more information, refer - * to {@link documentation}. - * - * @param credentialStream the stream with the credential definition. - * @param transport the transport for Http calls. - * @param jsonFactory the factory for Json parsing and formatting. - * @return the credential defined by the credentialStream. - * @throws IOException if the credential cannot be created from the stream. - */ - @Beta - public static GoogleCredential fromStream( - InputStream credentialStream, HttpTransport transport, JsonFactory jsonFactory) - throws IOException { - Preconditions.checkNotNull(credentialStream); - Preconditions.checkNotNull(transport); - Preconditions.checkNotNull(jsonFactory); - - JsonObjectParser parser = new JsonObjectParser(jsonFactory); - GenericJson fileContents = - parser.parseAndClose(credentialStream, OAuth2Utils.UTF_8, GenericJson.class); - String fileType = (String) fileContents.get("type"); - if (fileType == null) { - throw new IOException("Error reading credentials from stream, 'type' field not specified."); - } - if (USER_FILE_TYPE.equals(fileType)) { - return fromStreamUser(fileContents, transport, jsonFactory); - } - if (SERVICE_ACCOUNT_FILE_TYPE.equals(fileType)) { - return fromStreamServiceAccount(fileContents, transport, jsonFactory); - } - throw new IOException( - String.format( - "Error reading credentials from stream, 'type' value '%s' not recognized." - + " Expecting '%s' or '%s'.", - fileType, USER_FILE_TYPE, SERVICE_ACCOUNT_FILE_TYPE)); - } - - /** - * Service account ID (typically an e-mail address) or {@code null} if not using the service - * account flow. - */ - private String serviceAccountId; - - /** - * Service account Project ID or {@code null} if not present, either because this is not using the - * service account flow, or is using an older version of the service account configuration. - */ - private String serviceAccountProjectId; - - /** - * Collection of OAuth scopes to use with the service account flow or {@code null} if not using - * the service account flow. - */ - private Collection serviceAccountScopes; - - /** - * Private key to use with the service account flow or {@code null} if not using the service - * account flow. - */ - private PrivateKey serviceAccountPrivateKey; - - /** - * ID of private key to use with the service account flow or {@code null} if not using the service - * account flow. - */ - private String serviceAccountPrivateKeyId; - - /** - * Email address of the user the application is trying to impersonate in the service account flow - * or {@code null} for none or if not using the service account flow. - */ - private String serviceAccountUser; - - /** - * Constructor with the ability to access protected resources, but not refresh tokens. - * - *

To use with the ability to refresh tokens, use {@link Builder}. - */ - public GoogleCredential() { - this(new Builder()); - } - - /** - * @param builder Google credential builder - * @since 1.14 - */ - protected GoogleCredential(Builder builder) { - super(builder); - if (builder.serviceAccountPrivateKey == null) { - Preconditions.checkArgument( - builder.serviceAccountId == null - && builder.serviceAccountScopes == null - && builder.serviceAccountUser == null); - } else { - serviceAccountId = Preconditions.checkNotNull(builder.serviceAccountId); - serviceAccountProjectId = builder.serviceAccountProjectId; - serviceAccountScopes = - (builder.serviceAccountScopes == null) - ? Collections.emptyList() - : Collections.unmodifiableCollection(builder.serviceAccountScopes); - serviceAccountPrivateKey = builder.serviceAccountPrivateKey; - serviceAccountPrivateKeyId = builder.serviceAccountPrivateKeyId; - serviceAccountUser = builder.serviceAccountUser; - } - } - - @Override - public GoogleCredential setAccessToken(String accessToken) { - return (GoogleCredential) super.setAccessToken(accessToken); - } - - @Override - public GoogleCredential setRefreshToken(String refreshToken) { - if (refreshToken != null) { - Preconditions.checkArgument( - getJsonFactory() != null && getTransport() != null && getClientAuthentication() != null, - "Please use the Builder and call setJsonFactory, setTransport and setClientSecrets"); - } - return (GoogleCredential) super.setRefreshToken(refreshToken); - } - - @Override - public GoogleCredential setExpirationTimeMilliseconds(Long expirationTimeMilliseconds) { - return (GoogleCredential) super.setExpirationTimeMilliseconds(expirationTimeMilliseconds); - } - - @Override - public GoogleCredential setExpiresInSeconds(Long expiresIn) { - return (GoogleCredential) super.setExpiresInSeconds(expiresIn); - } - - @Override - public GoogleCredential setFromTokenResponse(TokenResponse tokenResponse) { - return (GoogleCredential) super.setFromTokenResponse(tokenResponse); - } - - @Override - @Beta - protected TokenResponse executeRefreshToken() throws IOException { - if (serviceAccountPrivateKey == null) { - return super.executeRefreshToken(); - } - // service accounts: no refresh token; instead use private key to request new access token - JsonWebSignature.Header header = new JsonWebSignature.Header(); - header.setAlgorithm("RS256"); - header.setType("JWT"); - header.setKeyId(serviceAccountPrivateKeyId); - JsonWebToken.Payload payload = new JsonWebToken.Payload(); - long currentTime = getClock().currentTimeMillis(); - payload.setIssuer(serviceAccountId); - payload.setAudience(getTokenServerEncodedUrl()); - payload.setIssuedAtTimeSeconds(currentTime / 1000); - payload.setExpirationTimeSeconds(currentTime / 1000 + 3600); - payload.setSubject(serviceAccountUser); - payload.put("scope", Joiner.on(' ').join(serviceAccountScopes)); - try { - String assertion = - JsonWebSignature.signUsingRsaSha256( - serviceAccountPrivateKey, getJsonFactory(), header, payload); - TokenRequest request = - new TokenRequest( - getTransport(), - getJsonFactory(), - new GenericUrl(getTokenServerEncodedUrl()), - "urn:ietf:params:oauth:grant-type:jwt-bearer"); - request.put("assertion", assertion); - return request.execute(); - } catch (GeneralSecurityException exception) { - IOException e = new IOException(); - e.initCause(exception); - throw e; - } - } - - /** - * Returns the service account ID (typically an e-mail address) or {@code null} if not using the - * service account flow. - */ - public final String getServiceAccountId() { - return serviceAccountId; - } - - /** - * Returns the service account Project ID or {@code null} if not present, either because this is - * not using the service account flow, or is using an older version of the service account - * configuration. - */ - public final String getServiceAccountProjectId() { - return serviceAccountProjectId; - } - - /** - * Returns a collection of OAuth scopes to use with the service account flow or {@code null} if - * not using the service account flow. - */ - public final Collection getServiceAccountScopes() { - return serviceAccountScopes; - } - - /** - * Returns the space-separated OAuth scopes to use with the service account flow or {@code null} - * if not using the service account flow. - * - * @since 1.15 - */ - public final String getServiceAccountScopesAsString() { - return serviceAccountScopes == null ? null : Joiner.on(' ').join(serviceAccountScopes); - } - - /** - * Returns the private key to use with the service account flow or {@code null} if not using the - * service account flow. - */ - public final PrivateKey getServiceAccountPrivateKey() { - return serviceAccountPrivateKey; - } - - /** - * {@link Beta}
- * Returns the ID of the private key to use with the service account flow or {@code null} if not - * using the service account flow. - */ - @Beta - public final String getServiceAccountPrivateKeyId() { - return serviceAccountPrivateKeyId; - } - - /** - * Returns the email address of the user the application is trying to impersonate in the service - * account flow or {@code null} for none or if not using the service account flow. - */ - public final String getServiceAccountUser() { - return serviceAccountUser; - } - - /** - * {@link Beta}
- * Indicates whether the credential requires scopes to be specified by calling createScoped before - * use. - */ - @Beta - public boolean createScopedRequired() { - if (serviceAccountPrivateKey == null) { - return false; - } - return (serviceAccountScopes == null || serviceAccountScopes.isEmpty()); - } - - /** - * {@link Beta}
- * For credentials that require scopes, creates a copy of the credential with the specified - * scopes. - */ - @Beta - public GoogleCredential createScoped(Collection scopes) { - if (serviceAccountPrivateKey == null) { - return this; - } - return toBuilder().setServiceAccountScopes(scopes).build(); - } - - /** - * {@link Beta}
- * For service accounts that need to delegate to a specific user, create a copy of the credential - * with the specified user. - */ - @Beta - public GoogleCredential createDelegated(String user) { - if (serviceAccountPrivateKey == null) { - return this; - } - return toBuilder().setServiceAccountUser(user).build(); - } - - /** - * {@link Beta}
- * Create a builder from this credential. - */ - @Beta - public Builder toBuilder() { - Builder builder = - new GoogleCredential.Builder() - .setServiceAccountPrivateKey(serviceAccountPrivateKey) - .setServiceAccountPrivateKeyId(serviceAccountPrivateKeyId) - .setServiceAccountId(serviceAccountId) - .setServiceAccountProjectId(serviceAccountProjectId) - .setServiceAccountUser(serviceAccountUser) - .setServiceAccountScopes(serviceAccountScopes) - .setTokenServerEncodedUrl(getTokenServerEncodedUrl()) - .setTransport(getTransport()) - .setJsonFactory(getJsonFactory()) - .setClock(getClock()); - - builder.setClientAuthentication(getClientAuthentication()); - - return builder; - } - - /** - * Google credential builder. - * - *

Implementation is not thread-safe. - */ - public static class Builder extends Credential.Builder { - - /** Service account ID (typically an e-mail address) or {@code null} for none. */ - String serviceAccountId; - - /** Collection of OAuth scopes to use with the service account flow or {@code null} for none. */ - Collection serviceAccountScopes; - - /** Private key to use with the service account flow or {@code null} for none. */ - PrivateKey serviceAccountPrivateKey; - - /** Id of the private key to use with the service account flow or {@code null} for none. */ - String serviceAccountPrivateKeyId; - - /** Project ID associated with the Service Account. */ - String serviceAccountProjectId; - - /** - * Email address of the user the application is trying to impersonate in the service account - * flow or {@code null} for none. - */ - String serviceAccountUser; - - public Builder() { - super(BearerToken.authorizationHeaderAccessMethod()); - setTokenServerEncodedUrl(GoogleOAuthConstants.TOKEN_SERVER_URL); - } - - @Override - public GoogleCredential build() { - return new GoogleCredential(this); - } - - @Override - public Builder setTransport(HttpTransport transport) { - return (Builder) super.setTransport(transport); - } - - @Override - public Builder setJsonFactory(JsonFactory jsonFactory) { - return (Builder) super.setJsonFactory(jsonFactory); - } - - /** @since 1.9 */ - @Override - public Builder setClock(Clock clock) { - return (Builder) super.setClock(clock); - } - - /** - * Sets the client identifier and secret. - * - *

Overriding is only supported for the purpose of calling the super implementation and - * changing the return type, but nothing else. - */ - public Builder setClientSecrets(String clientId, String clientSecret) { - setClientAuthentication(new ClientParametersAuthentication(clientId, clientSecret)); - return this; - } - - /** - * Sets the client secrets. - * - *

Overriding is only supported for the purpose of calling the super implementation and - * changing the return type, but nothing else. - */ - public Builder setClientSecrets(GoogleClientSecrets clientSecrets) { - Details details = clientSecrets.getDetails(); - setClientAuthentication( - new ClientParametersAuthentication(details.getClientId(), details.getClientSecret())); - return this; - } - - /** Returns the service account ID (typically an e-mail address) or {@code null} for none. */ - public final String getServiceAccountId() { - return serviceAccountId; - } - - /** - * Sets the service account ID (typically an e-mail address) or {@code null} for none. - * - *

Overriding is only supported for the purpose of calling the super implementation and - * changing the return type, but nothing else. - */ - public Builder setServiceAccountId(String serviceAccountId) { - this.serviceAccountId = serviceAccountId; - return this; - } - - /** Returns the service account Project ID or {@code null} for none. */ - public final String getServiceAccountProjectId() { - return serviceAccountProjectId; - } - - /** - * Sets the service account Project ID or {@code null} for none. - * - *

Overriding is only supported for the purpose of calling the super implementation and - * changing the return type, but nothing else. - */ - public Builder setServiceAccountProjectId(String serviceAccountProjectId) { - this.serviceAccountProjectId = serviceAccountProjectId; - return this; - } - - /** - * Returns a collection of OAuth scopes to use with the service account flow or {@code null} for - * none. - */ - public final Collection getServiceAccountScopes() { - return serviceAccountScopes; - } - - /** - * Sets the space-separated OAuth scopes to use with the service account flow or {@code null} - * for none. - * - *

Overriding is only supported for the purpose of calling the super implementation and - * changing the return type, but nothing else. - * - * @param serviceAccountScopes collection of scopes to be joined by a space separator (or a - * single value containing multiple space-separated scopes) - * @since 1.15 - */ - public Builder setServiceAccountScopes(Collection serviceAccountScopes) { - this.serviceAccountScopes = serviceAccountScopes; - return this; - } - - /** Returns the private key to use with the service account flow or {@code null} for none. */ - public final PrivateKey getServiceAccountPrivateKey() { - return serviceAccountPrivateKey; - } - - /** - * Sets the private key to use with the service account flow or {@code null} for none. - * - *

Overriding is only supported for the purpose of calling the super implementation and - * changing the return type, but nothing else. - */ - public Builder setServiceAccountPrivateKey(PrivateKey serviceAccountPrivateKey) { - this.serviceAccountPrivateKey = serviceAccountPrivateKey; - return this; - } - - /** - * {@link Beta}
- * Returns the id of the private key to use with the service account flow or {@code null} for - * none. - */ - @Beta - public final String getServiceAccountPrivateKeyId() { - return serviceAccountPrivateKeyId; - } - - /** - * {@link Beta}
- * Sets the id of the private key to use with the service account flow or {@code null} for none. - * - *

Overriding is only supported for the purpose of calling the super implementation and - * changing the return type, but nothing else. - */ - @Beta - public Builder setServiceAccountPrivateKeyId(String serviceAccountPrivateKeyId) { - this.serviceAccountPrivateKeyId = serviceAccountPrivateKeyId; - return this; - } - - /** - * Sets the private key to use with the service account flow or {@code null} for none. - * - *

Overriding is only supported for the purpose of calling the super implementation and - * changing the return type, but nothing else. - * - * @param p12File p12 file object - */ - public Builder setServiceAccountPrivateKeyFromP12File(File p12File) - throws GeneralSecurityException, IOException { - setServiceAccountPrivateKeyFromP12File(new FileInputStream(p12File)); - return this; - } - - /** - * Sets the private key to use with the service account flow or {@code null} for none. - * - *

Overriding is only supported for the purpose of calling the super implementation and - * changing the return type, but nothing else. - * - * @param p12FileInputStream input stream to the p12 file. This file is closed at the end of - * this method in a finally block. - */ - public Builder setServiceAccountPrivateKeyFromP12File(InputStream p12FileInputStream) - throws GeneralSecurityException, IOException { - serviceAccountPrivateKey = - SecurityUtils.loadPrivateKeyFromKeyStore( - SecurityUtils.getPkcs12KeyStore(), - p12FileInputStream, - "notasecret", - "privatekey", - "notasecret"); - return this; - } - - /** - * {@link Beta}
- * Sets the private key to use with the service account flow or {@code null} for none. - * - *

Overriding is only supported for the purpose of calling the super implementation and - * changing the return type, but nothing else. - * - * @param pemFile input stream to the PEM file (closed at the end of this method in a finally - * block) - * @since 1.13 - */ - @Beta - public Builder setServiceAccountPrivateKeyFromPemFile(File pemFile) - throws GeneralSecurityException, IOException { - byte[] bytes = - PemReader.readFirstSectionAndClose(new FileReader(pemFile), "PRIVATE KEY") - .getBase64DecodedBytes(); - serviceAccountPrivateKey = - SecurityUtils.getRsaKeyFactory().generatePrivate(new PKCS8EncodedKeySpec(bytes)); - return this; - } - - /** - * Returns the email address of the user the application is trying to impersonate in the service - * account flow or {@code null} for none. - */ - public final String getServiceAccountUser() { - return serviceAccountUser; - } - - /** - * Sets the email address of the user the application is trying to impersonate in the service - * account flow or {@code null} for none. - * - *

Overriding is only supported for the purpose of calling the super implementation and - * changing the return type, but nothing else. - */ - public Builder setServiceAccountUser(String serviceAccountUser) { - this.serviceAccountUser = serviceAccountUser; - return this; - } - - @Override - public Builder setRequestInitializer(HttpRequestInitializer requestInitializer) { - return (Builder) super.setRequestInitializer(requestInitializer); - } - - @Override - public Builder addRefreshListener(CredentialRefreshListener refreshListener) { - return (Builder) super.addRefreshListener(refreshListener); - } - - @Override - public Builder setRefreshListeners(Collection refreshListeners) { - return (Builder) super.setRefreshListeners(refreshListeners); - } - - @Override - public Builder setTokenServerUrl(GenericUrl tokenServerUrl) { - return (Builder) super.setTokenServerUrl(tokenServerUrl); - } - - @Override - public Builder setTokenServerEncodedUrl(String tokenServerEncodedUrl) { - return (Builder) super.setTokenServerEncodedUrl(tokenServerEncodedUrl); - } - - @Override - public Builder setClientAuthentication(HttpExecuteInterceptor clientAuthentication) { - return (Builder) super.setClientAuthentication(clientAuthentication); - } - } - - @Beta - private static GoogleCredential fromStreamUser( - GenericJson fileContents, HttpTransport transport, JsonFactory jsonFactory) - throws IOException { - String clientId = (String) fileContents.get("client_id"); - String clientSecret = (String) fileContents.get("client_secret"); - String refreshToken = (String) fileContents.get("refresh_token"); - if (clientId == null || clientSecret == null || refreshToken == null) { - throw new IOException( - "Error reading user credential from stream, " - + " expecting 'client_id', 'client_secret' and 'refresh_token'."); - } - - GoogleCredential credential = - new GoogleCredential.Builder() - .setClientSecrets(clientId, clientSecret) - .setTransport(transport) - .setJsonFactory(jsonFactory) - .build(); - credential.setRefreshToken(refreshToken); - - // Do a refresh so we can fail early rather than return an unusable credential - credential.refreshToken(); - return credential; - } - - @Beta - private static GoogleCredential fromStreamServiceAccount( - GenericJson fileContents, HttpTransport transport, JsonFactory jsonFactory) - throws IOException { - String clientId = (String) fileContents.get("client_id"); - String clientEmail = (String) fileContents.get("client_email"); - String privateKeyPem = (String) fileContents.get("private_key"); - String privateKeyId = (String) fileContents.get("private_key_id"); - if (clientId == null || clientEmail == null || privateKeyPem == null || privateKeyId == null) { - throw new IOException( - "Error reading service account credential from stream, " - + "expecting 'client_id', 'client_email', 'private_key' and 'private_key_id'."); - } - - PrivateKey privateKey = privateKeyFromPkcs8(privateKeyPem); - - Collection emptyScopes = Collections.emptyList(); - - Builder credentialBuilder = - new GoogleCredential.Builder() - .setTransport(transport) - .setJsonFactory(jsonFactory) - .setServiceAccountId(clientEmail) - .setServiceAccountScopes(emptyScopes) - .setServiceAccountPrivateKey(privateKey) - .setServiceAccountPrivateKeyId(privateKeyId); - String tokenUri = (String) fileContents.get("token_uri"); - if (tokenUri != null) { - credentialBuilder.setTokenServerEncodedUrl(tokenUri); - } - String projectId = (String) fileContents.get("project_id"); - if (projectId != null) { - credentialBuilder.setServiceAccountProjectId(projectId); - } - - // Don't do a refresh at this point, as it will always fail before the scopes are added. - return credentialBuilder.build(); - } - - @Beta - private static PrivateKey privateKeyFromPkcs8(String privateKeyPem) throws IOException { - Reader reader = new StringReader(privateKeyPem); - Section section = PemReader.readFirstSectionAndClose(reader, "PRIVATE KEY"); - if (section == null) { - throw new IOException("Invalid PKCS8 data."); - } - byte[] bytes = section.getBase64DecodedBytes(); - PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(bytes); - Exception unexpectedException = null; - try { - KeyFactory keyFactory = SecurityUtils.getRsaKeyFactory(); - PrivateKey privateKey = keyFactory.generatePrivate(keySpec); - return privateKey; - } catch (NoSuchAlgorithmException exception) { - unexpectedException = exception; - } catch (InvalidKeySpecException exception) { - unexpectedException = exception; - } - throw OAuth2Utils.exceptionWithCause( - new IOException("Unexpected exception reading PKCS data"), unexpectedException); - } -} +/* + * Copyright 2011 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ + +package com.google.api.client.googleapis.auth.oauth2; + +import com.google.api.client.auth.oauth2.BearerToken; +import com.google.api.client.auth.oauth2.ClientParametersAuthentication; +import com.google.api.client.auth.oauth2.Credential; +import com.google.api.client.auth.oauth2.CredentialRefreshListener; +import com.google.api.client.auth.oauth2.DataStoreCredentialRefreshListener; +import com.google.api.client.auth.oauth2.TokenRequest; +import com.google.api.client.auth.oauth2.TokenResponse; +import com.google.api.client.googleapis.auth.oauth2.GoogleClientSecrets.Details; +import com.google.api.client.googleapis.util.Utils; +import com.google.api.client.http.GenericUrl; +import com.google.api.client.http.HttpExecuteInterceptor; +import com.google.api.client.http.HttpRequestInitializer; +import com.google.api.client.http.HttpTransport; +import com.google.api.client.http.HttpUnsuccessfulResponseHandler; +import com.google.api.client.json.GenericJson; +import com.google.api.client.json.JsonFactory; +import com.google.api.client.json.JsonObjectParser; +import com.google.api.client.json.webtoken.JsonWebSignature; +import com.google.api.client.json.webtoken.JsonWebToken; +import com.google.api.client.util.Beta; +import com.google.api.client.util.Clock; +import com.google.api.client.util.Joiner; +import com.google.api.client.util.PemReader; +import com.google.api.client.util.PemReader.Section; +import com.google.api.client.util.Preconditions; +import com.google.api.client.util.SecurityUtils; +import com.google.api.client.util.store.DataStoreFactory; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.Reader; +import java.io.StringReader; +import java.security.GeneralSecurityException; +import java.security.KeyFactory; +import java.security.NoSuchAlgorithmException; +import java.security.PrivateKey; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.PKCS8EncodedKeySpec; +import java.util.Collection; +import java.util.Collections; + +/** + * Thread-safe Google-specific implementation of the OAuth 2.0 helper for accessing protected + * resources using an access token, as well as optionally refreshing the access token when it + * expires using a refresh token. + * + *

There are three modes supported: access token only, refresh token flow, and service account + * flow (with or without impersonating a user). + * + *

If all you have is an access token, you simply pass the {@link TokenResponse} to the + * credential using {@link Builder#setFromTokenResponse(TokenResponse)}. Google credential uses + * {@link BearerToken#authorizationHeaderAccessMethod()} as the access method. Sample usage: + * + *

{@code
+ * public static GoogleCredential createCredentialWithAccessTokenOnly(TokenResponse tokenResponse) {
+ *   return new GoogleCredential().setFromTokenResponse(tokenResponse);
+ * }
+ * }
+ * + *

If you have a refresh token, it is similar to the case of access token only, but you + * additionally need to pass the credential the client secrets using {@link + * Builder#setClientSecrets(GoogleClientSecrets)} or {@link Builder#setClientSecrets(String, + * String)}. Google credential uses {@link GoogleOAuthConstants#TOKEN_SERVER_URL} as the token + * server URL, and {@link ClientParametersAuthentication} with the client ID and secret as the + * client authentication. Sample usage: + * + *

{@code
+ * public static GoogleCredential createCredentialWithRefreshToken(
+ *     HttpTransport transport, JsonFactory jsonFactory,
+ *     GoogleClientSecrets clientSecrets, TokenResponse tokenResponse) {
+ *  return new GoogleCredential.Builder().setTransport(transport)
+ *                        .setJsonFactory(jsonFactory)
+ *                        .setClientSecrets(clientSecrets)
+ *                        .build()
+ *                        .setFromTokenResponse(tokenResponse);
+ * }
+ * }
+ * + *

The service + * account flow is used when you want to access data owned by your client application. You + * download the private key in a {@code .p12} file from the Google APIs Console. Use {@link + * Builder#setServiceAccountId(String)}, {@link + * Builder#setServiceAccountPrivateKeyFromP12File(File)}, and {@link + * Builder#setServiceAccountScopes(Collection)}. Sample usage: + * + *

{@code
+ * public static GoogleCredential createCredentialForServiceAccount(HttpTransport transport,
+ *     JsonFactory jsonFactory,
+ *     String serviceAccountId, Collection<String> serviceAccountScopes, File p12File)
+ *     throws GeneralSecurityException, IOException {
+ *   return new GoogleCredential.Builder().setTransport(transport).setJsonFactory(jsonFactory)
+ *       .setServiceAccountId(serviceAccountId).setServiceAccountScopes(serviceAccountScopes)
+ *       .setServiceAccountPrivateKeyFromP12File(p12File).build();
+ * }
+ * }
+ * + *

You can also use the service account flow to impersonate a user in a domain that you own. This + * is very similar to the service account flow above, but you additionally call {@link + * Builder#setServiceAccountUser(String)}. Sample usage: + * + *

{@code
+ * public static GoogleCredential createCredentialForServiceAccountImpersonateUser
+ *     (HttpTransport transport, JsonFactory jsonFactory, String serviceAccountId,
+ *      Collection<String> serviceAccountScopes, File p12File,
+ *      String serviceAccountUser) throws GeneralSecurityException, IOException {
+ *   return new GoogleCredential.Builder()
+ *       .setTransport(transport)
+ *       .setJsonFactory(jsonFactory)
+ *       .setServiceAccountId(serviceAccountId)
+ *       .setServiceAccountScopes(serviceAccountScopes)
+ *       .setServiceAccountPrivateKeyFromP12File(p12File)
+ *       .setServiceAccountUser(serviceAccountUser)
+ *       .build();
+ * }
+ * }
+ * + *

If you need to persist the access token in a data store, use {@link DataStoreFactory} and + * {@link Builder#addRefreshListener(CredentialRefreshListener)} with {@link + * DataStoreCredentialRefreshListener}. + * + *

If you have a custom request initializer, request execute interceptor, or unsuccessful + * response handler, take a look at the sample usage for {@link HttpExecuteInterceptor} and {@link + * HttpUnsuccessfulResponseHandler}, which are interfaces that this class also implements. + * + * @author Yaniv Inbar + * @since 1.7 + * @deprecated Please use + * google-auth-library for handling Application Default Credentials and other non-OAuth2 + * based authentication. + */ +@Deprecated +public class GoogleCredential extends Credential { + + static final String USER_FILE_TYPE = "authorized_user"; + static final String SERVICE_ACCOUNT_FILE_TYPE = "service_account"; + + @Beta + private static final DefaultCredentialProvider defaultCredentialProvider = + new DefaultCredentialProvider(); + + /** + * {@link Beta}
+ * Returns the Application Default Credentials. + * + *

Returns the Application Default Credentials which are credentials that identify and + * authorize the whole application. This is the built-in service account if running on Google + * Compute Engine or the credentials file from the path in the environment variable + * GOOGLE_APPLICATION_CREDENTIALS. + * + * @return the credential instance. + * @throws IOException if the credential cannot be created in the current environment. + */ + @Beta + public static GoogleCredential getApplicationDefault() throws IOException { + return getApplicationDefault(Utils.getDefaultTransport(), Utils.getDefaultJsonFactory(), false); + } + + /** + * {@link Beta}
+ * Returns the Application Default Credentials. + * + *

Returns the Application Default Credentials which are credentials that identify and + * authorize the whole application. This is the built-in service account if running on Google + * Compute Engine or the credentials file from the path in the environment variable + * GOOGLE_APPLICATION_CREDENTIALS. + * + * @param resetCachedCredentials whether to reset the cached credentials + * @return the credential instance. + * @throws IOException if the credential cannot be created in the current environment. + */ + @Beta + public static GoogleCredential getApplicationDefault(boolean resetCachedCredentials) + throws IOException { + return getApplicationDefault( + Utils.getDefaultTransport(), Utils.getDefaultJsonFactory(), resetCachedCredentials); + } + + /** + * {@link Beta}
+ * Returns the Application Default Credentials. + * + *

Returns the Application Default Credentials which are credentials that identify and + * authorize the whole application. This is the built-in service account if running on Google + * Compute Engine or the credentials file from the path in the environment variable + * GOOGLE_APPLICATION_CREDENTIALS. + * + * @param resetCachedCredentials whether to reset the cached credentials. + * @param transport the transport for Http calls. + * @param jsonFactory the factory for Json parsing and formatting. + * @return the credential instance. + * @throws IOException if the credential cannot be created in the current environment. + */ + @Beta + public static GoogleCredential getApplicationDefault( + HttpTransport transport, JsonFactory jsonFactory, boolean resetCachedCredentials) + throws IOException { + Preconditions.checkNotNull(transport); + Preconditions.checkNotNull(jsonFactory); + return defaultCredentialProvider.getDefaultCredential( + transport, jsonFactory, resetCachedCredentials); + } + + /** + * {@link Beta}
+ * Returns the Application Default Credentials. + * + *

Returns the Application Default Credentials which are credentials that identify and + * authorize the whole application. This is the built-in service account if running on Google + * Compute Engine or the credentials file from the path in the environment variable + * GOOGLE_APPLICATION_CREDENTIALS. + * + * @param transport the transport for Http calls. + * @param jsonFactory the factory for Json parsing and formatting. + * @return the credential instance. + * @throws IOException if the credential cannot be created in the current environment. + */ + @Beta + public static GoogleCredential getApplicationDefault( + HttpTransport transport, JsonFactory jsonFactory) throws IOException { + return getApplicationDefault(transport, jsonFactory, false); + } + + /** + * {@link Beta}
+ * Return a credential defined by a Json file. + * + *

Important: If you accept a credential configuration (credential JSON/File/Stream) from an + * external source for authentication to Google Cloud Platform, you must validate it before + * providing it to any Google API or library. Providing an unvalidated credential configuration to + * Google APIs can compromise the security of your systems and data. For more information, refer + * to {@link documentation}. + * + * @param credentialStream the stream with the credential definition. + * @return the credential defined by the credentialStream. + * @throws IOException if the credential cannot be created from the stream. + */ + @Beta + public static GoogleCredential fromStream(InputStream credentialStream) throws IOException { + return fromStream(credentialStream, Utils.getDefaultTransport(), Utils.getDefaultJsonFactory()); + } + + /** + * {@link Beta}
+ * Return a credential defined by a Json file. + * + *

Important: If you accept a credential configuration (credential JSON/File/Stream) from an + * external source for authentication to Google Cloud Platform, you must validate it before + * providing it to any Google API or library. Providing an unvalidated credential configuration to + * Google APIs can compromise the security of your systems and data. For more information, refer + * to {@link documentation}. + * + * @param credentialStream the stream with the credential definition. + * @param transport the transport for Http calls. + * @param jsonFactory the factory for Json parsing and formatting. + * @return the credential defined by the credentialStream. + * @throws IOException if the credential cannot be created from the stream. + */ + @Beta + public static GoogleCredential fromStream( + InputStream credentialStream, HttpTransport transport, JsonFactory jsonFactory) + throws IOException { + Preconditions.checkNotNull(credentialStream); + Preconditions.checkNotNull(transport); + Preconditions.checkNotNull(jsonFactory); + + JsonObjectParser parser = new JsonObjectParser(jsonFactory); + GenericJson fileContents = + parser.parseAndClose(credentialStream, OAuth2Utils.UTF_8, GenericJson.class); + String fileType = (String) fileContents.get("type"); + if (fileType == null) { + throw new IOException("Error reading credentials from stream, 'type' field not specified."); + } + if (USER_FILE_TYPE.equals(fileType)) { + return fromStreamUser(fileContents, transport, jsonFactory); + } + if (SERVICE_ACCOUNT_FILE_TYPE.equals(fileType)) { + return fromStreamServiceAccount(fileContents, transport, jsonFactory); + } + throw new IOException( + String.format( + "Error reading credentials from stream, 'type' value '%s' not recognized." + + " Expecting '%s' or '%s'.", + fileType, USER_FILE_TYPE, SERVICE_ACCOUNT_FILE_TYPE)); + } + + /** + * Service account ID (typically an e-mail address) or {@code null} if not using the service + * account flow. + */ + private String serviceAccountId; + + /** + * Service account Project ID or {@code null} if not present, either because this is not using the + * service account flow, or is using an older version of the service account configuration. + */ + private String serviceAccountProjectId; + + /** + * Collection of OAuth scopes to use with the service account flow or {@code null} if not using + * the service account flow. + */ + private Collection serviceAccountScopes; + + /** + * Private key to use with the service account flow or {@code null} if not using the service + * account flow. + */ + private PrivateKey serviceAccountPrivateKey; + + /** + * ID of private key to use with the service account flow or {@code null} if not using the service + * account flow. + */ + private String serviceAccountPrivateKeyId; + + /** + * Email address of the user the application is trying to impersonate in the service account flow + * or {@code null} for none or if not using the service account flow. + */ + private String serviceAccountUser; + + /** + * Constructor with the ability to access protected resources, but not refresh tokens. + * + *

To use with the ability to refresh tokens, use {@link Builder}. + */ + public GoogleCredential() { + this(new Builder()); + } + + /** + * @param builder Google credential builder + * @since 1.14 + */ + protected GoogleCredential(Builder builder) { + super(builder); + if (builder.serviceAccountPrivateKey == null) { + Preconditions.checkArgument( + builder.serviceAccountId == null + && builder.serviceAccountScopes == null + && builder.serviceAccountUser == null); + } else { + serviceAccountId = Preconditions.checkNotNull(builder.serviceAccountId); + serviceAccountProjectId = builder.serviceAccountProjectId; + serviceAccountScopes = + (builder.serviceAccountScopes == null) + ? Collections.emptyList() + : Collections.unmodifiableCollection(builder.serviceAccountScopes); + serviceAccountPrivateKey = builder.serviceAccountPrivateKey; + serviceAccountPrivateKeyId = builder.serviceAccountPrivateKeyId; + serviceAccountUser = builder.serviceAccountUser; + } + } + + @Override + public GoogleCredential setAccessToken(String accessToken) { + return (GoogleCredential) super.setAccessToken(accessToken); + } + + @Override + public GoogleCredential setRefreshToken(String refreshToken) { + if (refreshToken != null) { + Preconditions.checkArgument( + getJsonFactory() != null && getTransport() != null && getClientAuthentication() != null, + "Please use the Builder and call setJsonFactory, setTransport and setClientSecrets"); + } + return (GoogleCredential) super.setRefreshToken(refreshToken); + } + + @Override + public GoogleCredential setExpirationTimeMilliseconds(Long expirationTimeMilliseconds) { + return (GoogleCredential) super.setExpirationTimeMilliseconds(expirationTimeMilliseconds); + } + + @Override + public GoogleCredential setExpiresInSeconds(Long expiresIn) { + return (GoogleCredential) super.setExpiresInSeconds(expiresIn); + } + + @Override + public GoogleCredential setFromTokenResponse(TokenResponse tokenResponse) { + return (GoogleCredential) super.setFromTokenResponse(tokenResponse); + } + + @Override + @Beta + protected TokenResponse executeRefreshToken() throws IOException { + if (serviceAccountPrivateKey == null) { + return super.executeRefreshToken(); + } + // service accounts: no refresh token; instead use private key to request new access token + JsonWebSignature.Header header = new JsonWebSignature.Header(); + header.setAlgorithm("RS256"); + header.setType("JWT"); + header.setKeyId(serviceAccountPrivateKeyId); + JsonWebToken.Payload payload = new JsonWebToken.Payload(); + long currentTime = getClock().currentTimeMillis(); + payload.setIssuer(serviceAccountId); + payload.setAudience(getTokenServerEncodedUrl()); + payload.setIssuedAtTimeSeconds(currentTime / 1000); + payload.setExpirationTimeSeconds(currentTime / 1000 + 3600); + payload.setSubject(serviceAccountUser); + payload.put("scope", Joiner.on(' ').join(serviceAccountScopes)); + try { + String assertion = + JsonWebSignature.signUsingRsaSha256( + serviceAccountPrivateKey, getJsonFactory(), header, payload); + TokenRequest request = + new TokenRequest( + getTransport(), + getJsonFactory(), + new GenericUrl(getTokenServerEncodedUrl()), + "urn:ietf:params:oauth:grant-type:jwt-bearer"); + request.put("assertion", assertion); + return request.execute(); + } catch (GeneralSecurityException exception) { + IOException e = new IOException(); + e.initCause(exception); + throw e; + } + } + + /** + * Returns the service account ID (typically an e-mail address) or {@code null} if not using the + * service account flow. + */ + public final String getServiceAccountId() { + return serviceAccountId; + } + + /** + * Returns the service account Project ID or {@code null} if not present, either because this is + * not using the service account flow, or is using an older version of the service account + * configuration. + */ + public final String getServiceAccountProjectId() { + return serviceAccountProjectId; + } + + /** + * Returns a collection of OAuth scopes to use with the service account flow or {@code null} if + * not using the service account flow. + */ + public final Collection getServiceAccountScopes() { + return serviceAccountScopes; + } + + /** + * Returns the space-separated OAuth scopes to use with the service account flow or {@code null} + * if not using the service account flow. + * + * @since 1.15 + */ + public final String getServiceAccountScopesAsString() { + return serviceAccountScopes == null ? null : Joiner.on(' ').join(serviceAccountScopes); + } + + /** + * Returns the private key to use with the service account flow or {@code null} if not using the + * service account flow. + */ + public final PrivateKey getServiceAccountPrivateKey() { + return serviceAccountPrivateKey; + } + + /** + * {@link Beta}
+ * Returns the ID of the private key to use with the service account flow or {@code null} if not + * using the service account flow. + */ + @Beta + public final String getServiceAccountPrivateKeyId() { + return serviceAccountPrivateKeyId; + } + + /** + * Returns the email address of the user the application is trying to impersonate in the service + * account flow or {@code null} for none or if not using the service account flow. + */ + public final String getServiceAccountUser() { + return serviceAccountUser; + } + + /** + * {@link Beta}
+ * Indicates whether the credential requires scopes to be specified by calling createScoped before + * use. + */ + @Beta + public boolean createScopedRequired() { + if (serviceAccountPrivateKey == null) { + return false; + } + return (serviceAccountScopes == null || serviceAccountScopes.isEmpty()); + } + + /** + * {@link Beta}
+ * For credentials that require scopes, creates a copy of the credential with the specified + * scopes. + */ + @Beta + public GoogleCredential createScoped(Collection scopes) { + if (serviceAccountPrivateKey == null) { + return this; + } + return toBuilder().setServiceAccountScopes(scopes).build(); + } + + /** + * {@link Beta}
+ * For service accounts that need to delegate to a specific user, create a copy of the credential + * with the specified user. + */ + @Beta + public GoogleCredential createDelegated(String user) { + if (serviceAccountPrivateKey == null) { + return this; + } + return toBuilder().setServiceAccountUser(user).build(); + } + + /** + * {@link Beta}
+ * Create a builder from this credential. + */ + @Beta + public Builder toBuilder() { + Builder builder = + new GoogleCredential.Builder() + .setServiceAccountPrivateKey(serviceAccountPrivateKey) + .setServiceAccountPrivateKeyId(serviceAccountPrivateKeyId) + .setServiceAccountId(serviceAccountId) + .setServiceAccountProjectId(serviceAccountProjectId) + .setServiceAccountUser(serviceAccountUser) + .setServiceAccountScopes(serviceAccountScopes) + .setTokenServerEncodedUrl(getTokenServerEncodedUrl()) + .setTransport(getTransport()) + .setJsonFactory(getJsonFactory()) + .setClock(getClock()); + + builder.setClientAuthentication(getClientAuthentication()); + + return builder; + } + + /** + * Google credential builder. + * + *

Implementation is not thread-safe. + */ + public static class Builder extends Credential.Builder { + + /** Service account ID (typically an e-mail address) or {@code null} for none. */ + String serviceAccountId; + + /** Collection of OAuth scopes to use with the service account flow or {@code null} for none. */ + Collection serviceAccountScopes; + + /** Private key to use with the service account flow or {@code null} for none. */ + PrivateKey serviceAccountPrivateKey; + + /** Id of the private key to use with the service account flow or {@code null} for none. */ + String serviceAccountPrivateKeyId; + + /** Project ID associated with the Service Account. */ + String serviceAccountProjectId; + + /** + * Email address of the user the application is trying to impersonate in the service account + * flow or {@code null} for none. + */ + String serviceAccountUser; + + public Builder() { + super(BearerToken.authorizationHeaderAccessMethod()); + setTokenServerEncodedUrl(GoogleOAuthConstants.TOKEN_SERVER_URL); + } + + @Override + public GoogleCredential build() { + return new GoogleCredential(this); + } + + @Override + public Builder setTransport(HttpTransport transport) { + return (Builder) super.setTransport(transport); + } + + @Override + public Builder setJsonFactory(JsonFactory jsonFactory) { + return (Builder) super.setJsonFactory(jsonFactory); + } + + /** + * @since 1.9 + */ + @Override + public Builder setClock(Clock clock) { + return (Builder) super.setClock(clock); + } + + /** + * Sets the client identifier and secret. + * + *

Overriding is only supported for the purpose of calling the super implementation and + * changing the return type, but nothing else. + */ + public Builder setClientSecrets(String clientId, String clientSecret) { + setClientAuthentication(new ClientParametersAuthentication(clientId, clientSecret)); + return this; + } + + /** + * Sets the client secrets. + * + *

Overriding is only supported for the purpose of calling the super implementation and + * changing the return type, but nothing else. + */ + public Builder setClientSecrets(GoogleClientSecrets clientSecrets) { + Details details = clientSecrets.getDetails(); + setClientAuthentication( + new ClientParametersAuthentication(details.getClientId(), details.getClientSecret())); + return this; + } + + /** Returns the service account ID (typically an e-mail address) or {@code null} for none. */ + public final String getServiceAccountId() { + return serviceAccountId; + } + + /** + * Sets the service account ID (typically an e-mail address) or {@code null} for none. + * + *

Overriding is only supported for the purpose of calling the super implementation and + * changing the return type, but nothing else. + */ + public Builder setServiceAccountId(String serviceAccountId) { + this.serviceAccountId = serviceAccountId; + return this; + } + + /** Returns the service account Project ID or {@code null} for none. */ + public final String getServiceAccountProjectId() { + return serviceAccountProjectId; + } + + /** + * Sets the service account Project ID or {@code null} for none. + * + *

Overriding is only supported for the purpose of calling the super implementation and + * changing the return type, but nothing else. + */ + public Builder setServiceAccountProjectId(String serviceAccountProjectId) { + this.serviceAccountProjectId = serviceAccountProjectId; + return this; + } + + /** + * Returns a collection of OAuth scopes to use with the service account flow or {@code null} for + * none. + */ + public final Collection getServiceAccountScopes() { + return serviceAccountScopes; + } + + /** + * Sets the space-separated OAuth scopes to use with the service account flow or {@code null} + * for none. + * + *

Overriding is only supported for the purpose of calling the super implementation and + * changing the return type, but nothing else. + * + * @param serviceAccountScopes collection of scopes to be joined by a space separator (or a + * single value containing multiple space-separated scopes) + * @since 1.15 + */ + public Builder setServiceAccountScopes(Collection serviceAccountScopes) { + this.serviceAccountScopes = serviceAccountScopes; + return this; + } + + /** Returns the private key to use with the service account flow or {@code null} for none. */ + public final PrivateKey getServiceAccountPrivateKey() { + return serviceAccountPrivateKey; + } + + /** + * Sets the private key to use with the service account flow or {@code null} for none. + * + *

Overriding is only supported for the purpose of calling the super implementation and + * changing the return type, but nothing else. + */ + public Builder setServiceAccountPrivateKey(PrivateKey serviceAccountPrivateKey) { + this.serviceAccountPrivateKey = serviceAccountPrivateKey; + return this; + } + + /** + * {@link Beta}
+ * Returns the id of the private key to use with the service account flow or {@code null} for + * none. + */ + @Beta + public final String getServiceAccountPrivateKeyId() { + return serviceAccountPrivateKeyId; + } + + /** + * {@link Beta}
+ * Sets the id of the private key to use with the service account flow or {@code null} for none. + * + *

Overriding is only supported for the purpose of calling the super implementation and + * changing the return type, but nothing else. + */ + @Beta + public Builder setServiceAccountPrivateKeyId(String serviceAccountPrivateKeyId) { + this.serviceAccountPrivateKeyId = serviceAccountPrivateKeyId; + return this; + } + + /** + * Sets the private key to use with the service account flow or {@code null} for none. + * + *

Overriding is only supported for the purpose of calling the super implementation and + * changing the return type, but nothing else. + * + * @param p12File p12 file object + */ + public Builder setServiceAccountPrivateKeyFromP12File(File p12File) + throws GeneralSecurityException, IOException { + setServiceAccountPrivateKeyFromP12File(new FileInputStream(p12File)); + return this; + } + + /** + * Sets the private key to use with the service account flow or {@code null} for none. + * + *

Overriding is only supported for the purpose of calling the super implementation and + * changing the return type, but nothing else. + * + * @param p12FileInputStream input stream to the p12 file. This file is closed at the end of + * this method in a finally block. + */ + public Builder setServiceAccountPrivateKeyFromP12File(InputStream p12FileInputStream) + throws GeneralSecurityException, IOException { + serviceAccountPrivateKey = + SecurityUtils.loadPrivateKeyFromKeyStore( + SecurityUtils.getPkcs12KeyStore(), + p12FileInputStream, + "notasecret", + "privatekey", + "notasecret"); + return this; + } + + /** + * {@link Beta}
+ * Sets the private key to use with the service account flow or {@code null} for none. + * + *

Overriding is only supported for the purpose of calling the super implementation and + * changing the return type, but nothing else. + * + * @param pemFile input stream to the PEM file (closed at the end of this method in a finally + * block) + * @since 1.13 + */ + @Beta + public Builder setServiceAccountPrivateKeyFromPemFile(File pemFile) + throws GeneralSecurityException, IOException { + byte[] bytes = + PemReader.readFirstSectionAndClose(new FileReader(pemFile), "PRIVATE KEY") + .getBase64DecodedBytes(); + serviceAccountPrivateKey = + SecurityUtils.getRsaKeyFactory().generatePrivate(new PKCS8EncodedKeySpec(bytes)); + return this; + } + + /** + * Returns the email address of the user the application is trying to impersonate in the service + * account flow or {@code null} for none. + */ + public final String getServiceAccountUser() { + return serviceAccountUser; + } + + /** + * Sets the email address of the user the application is trying to impersonate in the service + * account flow or {@code null} for none. + * + *

Overriding is only supported for the purpose of calling the super implementation and + * changing the return type, but nothing else. + */ + public Builder setServiceAccountUser(String serviceAccountUser) { + this.serviceAccountUser = serviceAccountUser; + return this; + } + + @Override + public Builder setRequestInitializer(HttpRequestInitializer requestInitializer) { + return (Builder) super.setRequestInitializer(requestInitializer); + } + + @Override + public Builder addRefreshListener(CredentialRefreshListener refreshListener) { + return (Builder) super.addRefreshListener(refreshListener); + } + + @Override + public Builder setRefreshListeners(Collection refreshListeners) { + return (Builder) super.setRefreshListeners(refreshListeners); + } + + @Override + public Builder setTokenServerUrl(GenericUrl tokenServerUrl) { + return (Builder) super.setTokenServerUrl(tokenServerUrl); + } + + @Override + public Builder setTokenServerEncodedUrl(String tokenServerEncodedUrl) { + return (Builder) super.setTokenServerEncodedUrl(tokenServerEncodedUrl); + } + + @Override + public Builder setClientAuthentication(HttpExecuteInterceptor clientAuthentication) { + return (Builder) super.setClientAuthentication(clientAuthentication); + } + } + + @Beta + private static GoogleCredential fromStreamUser( + GenericJson fileContents, HttpTransport transport, JsonFactory jsonFactory) + throws IOException { + String clientId = (String) fileContents.get("client_id"); + String clientSecret = (String) fileContents.get("client_secret"); + String refreshToken = (String) fileContents.get("refresh_token"); + if (clientId == null || clientSecret == null || refreshToken == null) { + throw new IOException( + "Error reading user credential from stream, " + + " expecting 'client_id', 'client_secret' and 'refresh_token'."); + } + + GoogleCredential credential = + new GoogleCredential.Builder() + .setClientSecrets(clientId, clientSecret) + .setTransport(transport) + .setJsonFactory(jsonFactory) + .build(); + credential.setRefreshToken(refreshToken); + + // Do a refresh so we can fail early rather than return an unusable credential + credential.refreshToken(); + return credential; + } + + @Beta + private static GoogleCredential fromStreamServiceAccount( + GenericJson fileContents, HttpTransport transport, JsonFactory jsonFactory) + throws IOException { + String clientId = (String) fileContents.get("client_id"); + String clientEmail = (String) fileContents.get("client_email"); + String privateKeyPem = (String) fileContents.get("private_key"); + String privateKeyId = (String) fileContents.get("private_key_id"); + if (clientId == null || clientEmail == null || privateKeyPem == null || privateKeyId == null) { + throw new IOException( + "Error reading service account credential from stream, " + + "expecting 'client_id', 'client_email', 'private_key' and 'private_key_id'."); + } + + PrivateKey privateKey = privateKeyFromPkcs8(privateKeyPem); + + Collection emptyScopes = Collections.emptyList(); + + Builder credentialBuilder = + new GoogleCredential.Builder() + .setTransport(transport) + .setJsonFactory(jsonFactory) + .setServiceAccountId(clientEmail) + .setServiceAccountScopes(emptyScopes) + .setServiceAccountPrivateKey(privateKey) + .setServiceAccountPrivateKeyId(privateKeyId); + String tokenUri = (String) fileContents.get("token_uri"); + if (tokenUri != null) { + credentialBuilder.setTokenServerEncodedUrl(tokenUri); + } + String projectId = (String) fileContents.get("project_id"); + if (projectId != null) { + credentialBuilder.setServiceAccountProjectId(projectId); + } + + // Don't do a refresh at this point, as it will always fail before the scopes are added. + return credentialBuilder.build(); + } + + @Beta + private static PrivateKey privateKeyFromPkcs8(String privateKeyPem) throws IOException { + Reader reader = new StringReader(privateKeyPem); + Section section = PemReader.readFirstSectionAndClose(reader, "PRIVATE KEY"); + if (section == null) { + throw new IOException("Invalid PKCS8 data."); + } + byte[] bytes = section.getBase64DecodedBytes(); + PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(bytes); + Exception unexpectedException = null; + try { + KeyFactory keyFactory = SecurityUtils.getRsaKeyFactory(); + PrivateKey privateKey = keyFactory.generatePrivate(keySpec); + return privateKey; + } catch (NoSuchAlgorithmException exception) { + unexpectedException = exception; + } catch (InvalidKeySpecException exception) { + unexpectedException = exception; + } + throw OAuth2Utils.exceptionWithCause( + new IOException("Unexpected exception reading PKCS data"), unexpectedException); + } +} From 34c6b271a5646ed52c47370ed3a57f76cbbf5979 Mon Sep 17 00:00:00 2001 From: Diego Alonso Marquez Palacios Date: Fri, 5 Sep 2025 15:25:27 -0400 Subject: [PATCH 6/6] chore: lint ii --- .../api/client/googleapis/auth/oauth2/GoogleCredential.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/google-api-client/src/main/java/com/google/api/client/googleapis/auth/oauth2/GoogleCredential.java b/google-api-client/src/main/java/com/google/api/client/googleapis/auth/oauth2/GoogleCredential.java index 0b6c39ee8..949b4b8c1 100644 --- a/google-api-client/src/main/java/com/google/api/client/googleapis/auth/oauth2/GoogleCredential.java +++ b/google-api-client/src/main/java/com/google/api/client/googleapis/auth/oauth2/GoogleCredential.java @@ -612,9 +612,7 @@ public Builder setJsonFactory(JsonFactory jsonFactory) { return (Builder) super.setJsonFactory(jsonFactory); } - /** - * @since 1.9 - */ + /** @since 1.9 */ @Override public Builder setClock(Clock clock) { return (Builder) super.setClock(clock);