diff --git a/.github/generated-files-bot.yml b/.github/generated-files-bot.yml index 20f3acc28..47c2ba132 100644 --- a/.github/generated-files-bot.yml +++ b/.github/generated-files-bot.yml @@ -5,3 +5,7 @@ externalManifests: - type: json file: '.github/readme/synth.metadata/synth.metadata' jsonpath: '$.generatedFiles[*]' +ignoreAuthors: +- 'renovate-bot' +- 'yoshi-automation' +- 'release-please[bot]' diff --git a/.github/workflows/formatting.yaml b/.github/workflows/formatting.yaml deleted file mode 100644 index 6844407b4..000000000 --- a/.github/workflows/formatting.yaml +++ /dev/null @@ -1,25 +0,0 @@ -on: - pull_request_target: - types: [opened, synchronize] - branches: - - master -name: format -jobs: - format-code: - runs-on: ubuntu-latest - env: - ACCESS_TOKEN: ${{ secrets.YOSHI_CODE_BOT_TOKEN }} - steps: - - uses: actions/checkout@v2 - with: - ref: ${{github.event.pull_request.head.ref}} - repository: ${{github.event.pull_request.head.repo.full_name}} - - uses: actions/setup-java@v1 - with: - java-version: 11 - - run: "mvn com.coveo:fmt-maven-plugin:format" - - uses: googleapis/code-suggester@v1 - with: - command: review - pull_number: ${{ github.event.pull_request.number }} - git_dir: '.' diff --git a/.kokoro/release/publish_javadoc.cfg b/.kokoro/release/publish_javadoc.cfg index 83b2ca7d6..c39d4346a 100644 --- a/.kokoro/release/publish_javadoc.cfg +++ b/.kokoro/release/publish_javadoc.cfg @@ -27,3 +27,6 @@ before_action { } } } + +# Downloads docfx doclet resource. This will be in ${KOKORO_GFILE_DIR}/ +gfile_resources: "/bigstore/cloud-devrel-kokoro-resources/docfx" \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 200746f71..6d6af2769 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,40 @@ # Changelog +## [0.23.0](https://www.github.com/googleapis/google-auth-library-java/compare/v0.22.2...v0.23.0) (2021-01-26) + + +### ⚠ BREAKING CHANGES + +* privatize deprecated constructor (#473) + +### Features + +* allow custom lifespan for impersonated creds ([#515](https://www.github.com/googleapis/google-auth-library-java/issues/515)) ([0707ed4](https://www.github.com/googleapis/google-auth-library-java/commit/0707ed4bbb40fb775f196004ee30f8c695fe662b)) +* allow custom scopes for compute engine creds ([#514](https://www.github.com/googleapis/google-auth-library-java/issues/514)) ([edc8d6e](https://www.github.com/googleapis/google-auth-library-java/commit/edc8d6e0e7ca2c6749d026ba42854a09c4879fd6)) +* allow set lifetime for service account creds ([#516](https://www.github.com/googleapis/google-auth-library-java/issues/516)) ([427f2d5](https://www.github.com/googleapis/google-auth-library-java/commit/427f2d5610f0e8184a21b24531d2549a68c0b546)) +* promote IdToken and JWT features ([#538](https://www.github.com/googleapis/google-auth-library-java/issues/538)) ([b514fe0](https://www.github.com/googleapis/google-auth-library-java/commit/b514fe0cebe5a294e0cf97b7b5349e6a523dc7b2)) + + +### Bug Fixes + +* per google style, logger is lower case ([#529](https://www.github.com/googleapis/google-auth-library-java/issues/529)) ([ecfc6a2](https://www.github.com/googleapis/google-auth-library-java/commit/ecfc6a2ea6060e06629b5d422b23b842b917f55e)) +* privatize deprecated constructor ([#473](https://www.github.com/googleapis/google-auth-library-java/issues/473)) ([5804ff0](https://www.github.com/googleapis/google-auth-library-java/commit/5804ff03a531268831ac797ab262638a3119c14f)) +* remove deprecated methods ([#537](https://www.github.com/googleapis/google-auth-library-java/issues/537)) ([427963e](https://www.github.com/googleapis/google-auth-library-java/commit/427963e04702d8b73eca5ed555539b11bbe97342)) +* replace non-precondition use of Preconditions ([#539](https://www.github.com/googleapis/google-auth-library-java/issues/539)) ([f2ab4f1](https://www.github.com/googleapis/google-auth-library-java/commit/f2ab4f14262d54de0fde85494cfd92cf01a30cbe)) +* switch to GSON ([#531](https://www.github.com/googleapis/google-auth-library-java/issues/531)) ([1b98d5c](https://www.github.com/googleapis/google-auth-library-java/commit/1b98d5c86fc5e56187c977e7f43c39bb62483d40)) +* use default timeout if given 0 for ImpersonatedCredentials ([#527](https://www.github.com/googleapis/google-auth-library-java/issues/527)) ([ec74870](https://www.github.com/googleapis/google-auth-library-java/commit/ec74870c372a33d4157b45bb5d59ad7464fb2238)) + + +### Dependencies + +* update dependency com.google.appengine:appengine-api-1.0-sdk to v1.9.84 ([#422](https://www.github.com/googleapis/google-auth-library-java/issues/422)) ([b262c45](https://www.github.com/googleapis/google-auth-library-java/commit/b262c4587b058e6837429ee05f1b6a63620ee598)) +* update dependency com.google.guava:guava to v30.1-android ([#522](https://www.github.com/googleapis/google-auth-library-java/issues/522)) ([4090d1c](https://www.github.com/googleapis/google-auth-library-java/commit/4090d1cb50041bceb1cd975d1a9249a412df936f)) + + +### Documentation + +* fix wording in jwtWithClaims Javadoc ([#536](https://www.github.com/googleapis/google-auth-library-java/issues/536)) ([af21727](https://www.github.com/googleapis/google-auth-library-java/commit/af21727815263fb5ffc07ede953cf042fac3ac2b)) + ### [0.22.2](https://www.github.com/googleapis/google-auth-library-java/compare/v0.22.1...v0.22.2) (2020-12-11) diff --git a/README.md b/README.md index dff1cb18b..9d11eef77 100644 --- a/README.md +++ b/README.md @@ -32,7 +32,7 @@ If you are using Maven, add this to your pom.xml file (notice that you can repla com.google.auth google-auth-library-oauth2-http - 0.22.2 + 0.23.0 ``` [//]: # ({x-version-update-end}) @@ -42,7 +42,7 @@ If you are using Gradle, add this to your dependencies [//]: # ({x-version-update-start:google-auth-library-oauth2-http:released}) ```Groovy -compile 'com.google.auth:google-auth-library-oauth2-http:0.22.2' +compile 'com.google.auth:google-auth-library-oauth2-http:0.23.0' ``` [//]: # ({x-version-update-end}) @@ -50,7 +50,7 @@ If you are using SBT, add this to your dependencies [//]: # ({x-version-update-start:google-auth-library-oauth2-http:released}) ```Scala -libraryDependencies += "com.google.auth" % "google-auth-library-oauth2-http" % "0.22.2" +libraryDependencies += "com.google.auth" % "google-auth-library-oauth2-http" % "0.23.0" ``` [//]: # ({x-version-update-end}) diff --git a/appengine/java/com/google/auth/appengine/AppEngineCredentials.java b/appengine/java/com/google/auth/appengine/AppEngineCredentials.java index dbae97052..5b3024cf2 100644 --- a/appengine/java/com/google/auth/appengine/AppEngineCredentials.java +++ b/appengine/java/com/google/auth/appengine/AppEngineCredentials.java @@ -35,7 +35,6 @@ import com.google.appengine.api.appidentity.AppIdentityService.GetAccessTokenResult; import com.google.appengine.api.appidentity.AppIdentityServiceFactory; import com.google.auth.ServiceAccountSigner; -import com.google.auth.http.HttpTransportFactory; import com.google.auth.oauth2.AccessToken; import com.google.auth.oauth2.GoogleCredentials; import com.google.common.base.MoreObjects; @@ -46,7 +45,6 @@ import java.util.Collection; import java.util.Date; import java.util.Objects; -import java.util.logging.Logger; /** * OAuth2 credentials representing the built-in service account for Google App Engine. You should @@ -56,11 +54,6 @@ */ public class AppEngineCredentials extends GoogleCredentials implements ServiceAccountSigner { - private static final Logger LOGGER = Logger.getLogger(AppEngineCredentials.class.getName()); - private static final String APPLICATION_DEFAULT_CREDENTIALS_WARNING = - "You are attempting to " - + "fetch Application Default Credentials from com.google.auth.appengine.AppEngineCredentials." - + " This method will not return a com.google.auth.appengine.AppEngineCredentials instance."; private static final long serialVersionUID = -2627708355455064660L; private final String appIdentityServiceClassName; @@ -69,27 +62,6 @@ public class AppEngineCredentials extends GoogleCredentials implements ServiceAc private transient AppIdentityService appIdentityService; - /** - * @deprecated AppEngineCredentials should be instantiated via its Builder. See - * https://github.com/googleapis/google-auth-library-java#google-auth-library-appengine - */ - @Deprecated - public static GoogleCredentials getApplicationDefault() throws IOException { - LOGGER.warning(APPLICATION_DEFAULT_CREDENTIALS_WARNING); - return GoogleCredentials.getApplicationDefault(); - } - - /** - * @deprecated AppEngineCredentials should be instantiated via its Builder. See - * https://github.com/googleapis/google-auth-library-java#google-auth-library-appengine - */ - @Deprecated - public static GoogleCredentials getApplicationDefault(HttpTransportFactory transportFactory) - throws IOException { - LOGGER.warning(APPLICATION_DEFAULT_CREDENTIALS_WARNING); - return GoogleCredentials.getApplicationDefault(transportFactory); - } - private AppEngineCredentials(Collection scopes, AppIdentityService appIdentityService) { this.scopes = scopes == null ? ImmutableSet.of() : ImmutableList.copyOf(scopes); this.appIdentityService = diff --git a/appengine/javatests/com/google/auth/appengine/AppEngineCredentialsTest.java b/appengine/javatests/com/google/auth/appengine/AppEngineCredentialsTest.java index 3c121215c..d37038d5e 100644 --- a/appengine/javatests/com/google/auth/appengine/AppEngineCredentialsTest.java +++ b/appengine/javatests/com/google/auth/appengine/AppEngineCredentialsTest.java @@ -39,9 +39,7 @@ import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; -import com.google.api.client.http.HttpTransport; import com.google.auth.Credentials; -import com.google.auth.http.HttpTransportFactory; import com.google.auth.oauth2.AccessToken; import com.google.auth.oauth2.BaseSerializationTest; import com.google.auth.oauth2.GoogleCredentials; @@ -53,9 +51,6 @@ import java.util.Date; import java.util.List; import java.util.Map; -import java.util.logging.Handler; -import java.util.logging.LogRecord; -import java.util.logging.Logger; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; @@ -264,61 +259,4 @@ private static void assertContainsBearerToken(Map> metadata } assertTrue("Bearer token not found", found); } - - @Test - @SuppressWarnings("deprecation") - public void warnsDefaultCredentials() { - Logger logger = Logger.getLogger(AppEngineCredentials.class.getName()); - LogHandler handler = new LogHandler(); - logger.addHandler(handler); - - try { - Credentials unused = AppEngineCredentials.getApplicationDefault(); - } catch (IOException ex) { - // ignore - this may just fail for not being in a supported environment - } - - LogRecord message = handler.getRecord(); - assertTrue(message.getMessage().contains("You are attempting to")); - } - - @Test - @SuppressWarnings("deprecation") - public void warnsDefaultCredentialsWithTransport() { - Logger logger = Logger.getLogger(AppEngineCredentials.class.getName()); - LogHandler handler = new LogHandler(); - logger.addHandler(handler); - - try { - Credentials unused = - AppEngineCredentials.getApplicationDefault( - new HttpTransportFactory() { - @Override - public HttpTransport create() { - return null; - } - }); - } catch (IOException ex) { - // ignore - this may just fail for not being in a supported environment - } - - LogRecord message = handler.getRecord(); - assertTrue(message.getMessage().contains("You are attempting to")); - } - - private class LogHandler extends Handler { - LogRecord lastRecord; - - public void publish(LogRecord record) { - lastRecord = record; - } - - public LogRecord getRecord() { - return lastRecord; - } - - public void close() {} - - public void flush() {} - } } diff --git a/appengine/pom.xml b/appengine/pom.xml index 30d35b882..c486e5f69 100644 --- a/appengine/pom.xml +++ b/appengine/pom.xml @@ -5,7 +5,7 @@ com.google.auth google-auth-library-parent - 0.22.2 + 0.23.0 ../pom.xml @@ -58,10 +58,6 @@ com.google.auth google-auth-library-oauth2-http - - com.google.http-client - google-http-client - com.google.appengine appengine-api-1.0-sdk diff --git a/bom/pom.xml b/bom/pom.xml index 90a9da324..9f87f73d5 100644 --- a/bom/pom.xml +++ b/bom/pom.xml @@ -3,7 +3,7 @@ 4.0.0 com.google.auth google-auth-library-bom - 0.22.2 + 0.23.0 pom Google Auth Library for Java BOM diff --git a/credentials/pom.xml b/credentials/pom.xml index d77eda92d..eb92df55d 100644 --- a/credentials/pom.xml +++ b/credentials/pom.xml @@ -4,7 +4,7 @@ com.google.auth google-auth-library-parent - 0.22.2 + 0.23.0 ../pom.xml diff --git a/oauth2_http/clirr-ignored-differences.xml b/oauth2_http/clirr-ignored-differences.xml new file mode 100644 index 000000000..76b573848 --- /dev/null +++ b/oauth2_http/clirr-ignored-differences.xml @@ -0,0 +1,11 @@ + + + + + 7009 + com/google/auth/oauth2/ServiceAccountJwtAccessCredentials + ServiceAccountJwtAccessCredentials(java.lang.String, java.lang.String, java.security.PrivateKey, java.lang.String) + public + private + + diff --git a/oauth2_http/java/com/google/auth/oauth2/ClientId.java b/oauth2_http/java/com/google/auth/oauth2/ClientId.java index 05fcde836..80c3452f4 100644 --- a/oauth2_http/java/com/google/auth/oauth2/ClientId.java +++ b/oauth2_http/java/com/google/auth/oauth2/ClientId.java @@ -72,9 +72,9 @@ public static ClientId of(String clientId, String clientSecret) { /** * Constructs a Client ID from JSON from a downloaded file. * - * @param json The JSON from the downloaded file. - * @return the ClientId instance based on the JSON. - * @throws IOException The JSON could not be parsed. + * @param json the JSON from the downloaded file + * @return the ClientId instance based on the JSON + * @throws IOException the JSON could not be parsed */ public static ClientId fromJson(Map json) throws IOException { Object rawDetail = null; @@ -105,9 +105,9 @@ public static ClientId fromJson(Map json) throws IOException { /** * Constructs a Client ID from JSON file stored as a resource. * - * @param relativeClass A class in the same namespace as the resource. - * @param resourceName The name of the resource - * @return The constructed ClientID instance based on the JSON in the resource. + * @param relativeClass a class in the same namespace as the resource + * @param resourceName the name of the resource + * @return the constructed ClientID instance based on the JSON in the resource * @throws IOException The JSON could not be loaded or parsed. */ public static ClientId fromResource(Class relativeClass, String resourceName) @@ -119,9 +119,9 @@ public static ClientId fromResource(Class relativeClass, String resourceName) /** * Constructs a Client ID from JSON file stream. * - * @param stream Stream of the downloaded JSON file. - * @return The constructed ClientID instance based on the JSON in the stream. - * @throws IOException The JSON could not be read or parsed. + * @param stream the downloaded JSON file + * @return the constructed ClientID instance based on the JSON in the stream + * @throws IOException the JSON could not be read or parsed */ public static ClientId fromStream(InputStream stream) throws IOException { Preconditions.checkNotNull(stream); diff --git a/oauth2_http/java/com/google/auth/oauth2/ComputeEngineCredentials.java b/oauth2_http/java/com/google/auth/oauth2/ComputeEngineCredentials.java index e65e94ea6..ede42cee9 100644 --- a/oauth2_http/java/com/google/auth/oauth2/ComputeEngineCredentials.java +++ b/oauth2_http/java/com/google/auth/oauth2/ComputeEngineCredentials.java @@ -42,13 +42,17 @@ import com.google.api.client.util.GenericData; import com.google.auth.ServiceAccountSigner; import com.google.auth.http.HttpTransportFactory; -import com.google.common.annotations.Beta; +import com.google.common.base.Joiner; import com.google.common.base.MoreObjects; +import com.google.common.collect.ImmutableSet; import java.io.IOException; import java.io.InputStream; import java.io.ObjectInputStream; import java.net.SocketTimeoutException; import java.net.UnknownHostException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; import java.util.Collections; import java.util.Date; import java.util.List; @@ -94,6 +98,8 @@ public class ComputeEngineCredentials extends GoogleCredentials private final String transportFactoryClassName; + private final Collection scopes; + private transient HttpTransportFactory transportFactory; private transient String serviceAccountEmail; @@ -102,13 +108,28 @@ public class ComputeEngineCredentials extends GoogleCredentials * * @param transportFactory HTTP transport factory, creates the transport used to get access * tokens. + * @param scopes scope strings for the APIs to be called. May be null or an empty collection. */ - private ComputeEngineCredentials(HttpTransportFactory transportFactory) { + private ComputeEngineCredentials( + HttpTransportFactory transportFactory, Collection scopes) { this.transportFactory = firstNonNull( transportFactory, getFromServiceLoader(HttpTransportFactory.class, OAuth2Utils.HTTP_TRANSPORT_FACTORY)); this.transportFactoryClassName = this.transportFactory.getClass().getName(); + if (scopes == null) { + this.scopes = ImmutableSet.of(); + } else { + List scopeList = new ArrayList(scopes); + scopeList.removeAll(Arrays.asList("", null)); + this.scopes = ImmutableSet.copyOf(scopeList); + } + } + + /** Clones the compute engine account with the specified scopes. */ + @Override + public GoogleCredentials createScoped(Collection newScopes) { + return new ComputeEngineCredentials(this.transportFactory, newScopes); } /** @@ -117,13 +138,30 @@ private ComputeEngineCredentials(HttpTransportFactory transportFactory) { * @return new ComputeEngineCredentials */ public static ComputeEngineCredentials create() { - return new ComputeEngineCredentials(null); + return new ComputeEngineCredentials(null, null); + } + + public final Collection getScopes() { + return scopes; + } + + /** + * If scopes is specified, add "?scopes=comma-separated-list-of-scopes" to the token url. + * + * @return token url with the given scopes + */ + String createTokenUrlWithScopes() { + GenericUrl tokenUrl = new GenericUrl(getTokenServerEncodedUrl()); + if (!scopes.isEmpty()) { + tokenUrl.set("scopes", Joiner.on(',').join(scopes)); + } + return tokenUrl.toString(); } /** Refresh the access token by getting it from the GCE metadata server */ @Override public AccessToken refreshAccessToken() throws IOException { - HttpResponse response = getMetadataResponse(getTokenServerEncodedUrl()); + HttpResponse response = getMetadataResponse(createTokenUrlWithScopes()); int statusCode = response.getStatusCode(); if (statusCode == HttpStatusCodes.STATUS_CODE_NOT_FOUND) { throw new IOException( @@ -173,7 +211,6 @@ public AccessToken refreshAccessToken() throws IOException { * @throws IOException if the attempt to get an IdToken failed * @return IdToken object which includes the raw id_token, JsonWebSignature */ - @Beta @Override public IdToken idTokenWithAudience(String targetAudience, List options) throws IOException { @@ -307,7 +344,8 @@ public boolean equals(Object obj) { return false; } ComputeEngineCredentials other = (ComputeEngineCredentials) obj; - return Objects.equals(this.transportFactoryClassName, other.transportFactoryClassName); + return Objects.equals(this.transportFactoryClassName, other.transportFactoryClassName) + && Objects.equals(this.scopes, other.scopes); } private void readObject(ObjectInputStream input) throws IOException, ClassNotFoundException { @@ -399,11 +437,13 @@ private String getDefaultServiceAccount() throws IOException { public static class Builder extends GoogleCredentials.Builder { private HttpTransportFactory transportFactory; + private Collection scopes; protected Builder() {} protected Builder(ComputeEngineCredentials credentials) { this.transportFactory = credentials.transportFactory; + this.scopes = credentials.scopes; } public Builder setHttpTransportFactory(HttpTransportFactory transportFactory) { @@ -411,12 +451,21 @@ public Builder setHttpTransportFactory(HttpTransportFactory transportFactory) { return this; } + public Builder setScopes(Collection scopes) { + this.scopes = scopes; + return this; + } + public HttpTransportFactory getHttpTransportFactory() { return transportFactory; } + public Collection getScopes() { + return scopes; + } + public ComputeEngineCredentials build() { - return new ComputeEngineCredentials(transportFactory); + return new ComputeEngineCredentials(transportFactory, scopes); } } } diff --git a/oauth2_http/java/com/google/auth/oauth2/GoogleCredentials.java b/oauth2_http/java/com/google/auth/oauth2/GoogleCredentials.java index c32e72f47..c9ea810fb 100644 --- a/oauth2_http/java/com/google/auth/oauth2/GoogleCredentials.java +++ b/oauth2_http/java/com/google/auth/oauth2/GoogleCredentials.java @@ -39,6 +39,7 @@ import com.google.common.collect.ImmutableList; import java.io.IOException; import java.io.InputStream; +import java.nio.charset.StandardCharsets; import java.util.Collection; import java.util.Collections; import java.util.HashMap; @@ -153,7 +154,7 @@ public static GoogleCredentials fromStream( JsonFactory jsonFactory = OAuth2Utils.JSON_FACTORY; JsonObjectParser parser = new JsonObjectParser(jsonFactory); GenericJson fileContents = - parser.parseAndClose(credentialsStream, OAuth2Utils.UTF_8, GenericJson.class); + parser.parseAndClose(credentialsStream, StandardCharsets.UTF_8, GenericJson.class); String fileType = (String) fileContents.get("type"); if (fileType == null) { diff --git a/oauth2_http/java/com/google/auth/oauth2/IdToken.java b/oauth2_http/java/com/google/auth/oauth2/IdToken.java index 51986799a..ccd670bba 100644 --- a/oauth2_http/java/com/google/auth/oauth2/IdToken.java +++ b/oauth2_http/java/com/google/auth/oauth2/IdToken.java @@ -33,7 +33,6 @@ import com.google.api.client.json.JsonFactory; import com.google.api.client.json.webtoken.JsonWebSignature; -import com.google.common.annotations.Beta; import com.google.common.base.MoreObjects; import java.io.IOException; import java.io.ObjectInputStream; @@ -43,7 +42,6 @@ import java.util.Objects; /** Represents a temporary IdToken and its JsonWebSignature object */ -@Beta public class IdToken extends AccessToken implements Serializable { private static final long serialVersionUID = -8514239465808977353L; diff --git a/oauth2_http/java/com/google/auth/oauth2/IdTokenCredentials.java b/oauth2_http/java/com/google/auth/oauth2/IdTokenCredentials.java index 6b827c5f9..5629d4e81 100644 --- a/oauth2_http/java/com/google/auth/oauth2/IdTokenCredentials.java +++ b/oauth2_http/java/com/google/auth/oauth2/IdTokenCredentials.java @@ -32,7 +32,6 @@ package com.google.auth.oauth2; import com.google.api.client.util.Preconditions; -import com.google.common.annotations.Beta; import com.google.common.base.MoreObjects; import java.io.IOException; import java.util.List; @@ -101,7 +100,6 @@ * System.out.println(tokenCredential.getIdToken().getJsonWebSignature().getPayload().getExpirationTimeSeconds()); * */ -@Beta public class IdTokenCredentials extends OAuth2Credentials { private static final long serialVersionUID = -2133257318957588431L; diff --git a/oauth2_http/java/com/google/auth/oauth2/IdTokenProvider.java b/oauth2_http/java/com/google/auth/oauth2/IdTokenProvider.java index 83a8d6242..c5b9e9c44 100644 --- a/oauth2_http/java/com/google/auth/oauth2/IdTokenProvider.java +++ b/oauth2_http/java/com/google/auth/oauth2/IdTokenProvider.java @@ -31,12 +31,10 @@ package com.google.auth.oauth2; -import com.google.common.annotations.Beta; import java.io.IOException; import java.util.List; /** Interface for an Google OIDC token provider. This type represents a google issued OIDC token. */ -@Beta public interface IdTokenProvider { /** diff --git a/oauth2_http/java/com/google/auth/oauth2/ImpersonatedCredentials.java b/oauth2_http/java/com/google/auth/oauth2/ImpersonatedCredentials.java index 63ee972ec..6ddb116cc 100644 --- a/oauth2_http/java/com/google/auth/oauth2/ImpersonatedCredentials.java +++ b/oauth2_http/java/com/google/auth/oauth2/ImpersonatedCredentials.java @@ -45,7 +45,6 @@ import com.google.auth.ServiceAccountSigner; import com.google.auth.http.HttpCredentialsAdapter; import com.google.auth.http.HttpTransportFactory; -import com.google.common.annotations.Beta; import com.google.common.base.MoreObjects; import com.google.common.collect.ImmutableMap; import java.io.IOException; @@ -61,11 +60,11 @@ /** * ImpersonatedCredentials allowing credentials issued to a user or service account to impersonate - * another.
- * The source project using ImpersonatedCredentials must enable the "IAMCredentials" API.
- * Also, the target service account must grant the orginating principal the "Service Account Token - * Creator" IAM role.
- * Usage:
+ * another. The source project using ImpersonatedCredentials must enable the "IAMCredentials" API. + * Also, the target service account must grant the originating principal the "Service Account Token + * Creator" IAM role. + * + *

Usage: * *

  * String credPath = "/path/to/svc_account.json";
@@ -90,16 +89,13 @@ public class ImpersonatedCredentials extends GoogleCredentials
 
   private static final long serialVersionUID = -2133257318957488431L;
   private static final String RFC3339 = "yyyy-MM-dd'T'HH:mm:ss'Z'";
-  private static final int ONE_HOUR_IN_SECONDS = 3600;
+  private static final int TWELVE_HOURS_IN_SECONDS = 43200;
+  private static final int DEFAULT_LIFETIME_IN_SECONDS = 3600;
   private static final String CLOUD_PLATFORM_SCOPE =
       "https://www.googleapis.com/auth/cloud-platform";
   private static final String IAM_ACCESS_TOKEN_ENDPOINT =
       "https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/%s:generateAccessToken";
 
-  private static final String SCOPE_EMPTY_ERROR = "Scopes cannot be null";
-  private static final String LIFETIME_EXCEEDED_ERROR =
-      "lifetime must be less than or equal to 3600";
-
   private GoogleCredentials sourceCredentials;
   private String targetPrincipal;
   private List delegates;
@@ -110,19 +106,24 @@ public class ImpersonatedCredentials extends GoogleCredentials
   private transient HttpTransportFactory transportFactory;
 
   /**
-   * @param sourceCredentials The source credential used as to acquire the impersonated credentials
-   * @param targetPrincipal The service account to impersonate.
-   * @param delegates The chained list of delegates required to grant the final access_token. If
+   * @param sourceCredentials the source credential used as to acquire the impersonated credentials
+   * @param targetPrincipal the service account to impersonate
+   * @param delegates the chained list of delegates required to grant the final access_token. If
    *     set, the sequence of identities must have "Service Account Token Creator" capability
    *     granted to the preceding identity. For example, if set to [serviceAccountB,
    *     serviceAccountC], the sourceCredential must have the Token Creator role on serviceAccountB.
    *     serviceAccountB must have the Token Creator on serviceAccountC. Finally, C must have Token
-   *     Creator on target_principal. If left unset, sourceCredential must have that role on
+   *     Creator on target_principal. If unset, sourceCredential must have that role on
    *     targetPrincipal.
-   * @param scopes Scopes to request during the authorization grant.
-   * @param lifetime Number of seconds the delegated credential should be valid for (up to 3600).
-   * @param transportFactory HTTP transport factory, creates the transport used to get access
-   *     tokens.
+   * @param scopes scopes to request during the authorization grant
+   * @param lifetime number of seconds the delegated credential should be valid. By default this
+   *     value should be at most 3600. However, you can follow these
+   *     instructions to set up the service account and extend the maximum lifetime to 43200 (12
+   *     hours). If the given lifetime is 0, default value 3600 will be used instead when creating
+   *     the credentials.
+   * @param transportFactory HTTP transport factory that creates the transport used to get access
+   *     tokens
    * @return new credentials
    */
   public static ImpersonatedCredentials create(
@@ -143,17 +144,24 @@ public static ImpersonatedCredentials create(
   }
 
   /**
-   * @param sourceCredentials The source credential used as to acquire the impersonated credentials
-   * @param targetPrincipal The service account to impersonate.
-   * @param delegates The chained list of delegates required to grant the final access_token. If
+   * @param sourceCredentials the source credential used as to acquire the impersonated credentials
+   * @param targetPrincipal the service account to impersonate
+   * @param delegates the chained list of delegates required to grant the final access_token. If
    *     set, the sequence of identities must have "Service Account Token Creator" capability
    *     granted to the preceding identity. For example, if set to [serviceAccountB,
    *     serviceAccountC], the sourceCredential must have the Token Creator role on serviceAccountB.
    *     serviceAccountB must have the Token Creator on serviceAccountC. Finally, C must have Token
    *     Creator on target_principal. If left unset, sourceCredential must have that role on
    *     targetPrincipal.
-   * @param scopes Scopes to request during the authorization grant.
-   * @param lifetime Number of seconds the delegated credential should be valid for (up to 3600).
+   * @param scopes scopes to request during the authorization grant
+   * @param lifetime number of seconds the delegated credential should be valid. By default this
+   *     value should be at most 3600. However, you can follow these
+   *     instructions to set up the service account and extend the maximum lifetime to 43200 (12
+   *     hours).
+   *     https://cloud.google.com/iam/docs/creating-short-lived-service-account-credentials#sa-credentials-oauth
+   *     If the given lifetime is 0, default value 3600 will be used instead when creating the
+   *     credentials.
    * @return new credentials
    */
   public static ImpersonatedCredentials create(
@@ -174,13 +182,17 @@ public static ImpersonatedCredentials create(
   /**
    * Returns the email field of the serviceAccount that is being impersonated.
    *
-   * @return email address of the impersonated service account.
+   * @return email address of the impersonated service account
    */
   @Override
   public String getAccount() {
     return this.targetPrincipal;
   }
 
+  int getLifetime() {
+    return this.lifetime;
+  }
+
   /**
    * Signs the provided bytes using the private key associated with the impersonated service account
    *
@@ -216,7 +228,10 @@ private ImpersonatedCredentials(Builder builder) {
       this.delegates = new ArrayList();
     }
     if (this.scopes == null) {
-      throw new IllegalStateException(SCOPE_EMPTY_ERROR);
+      throw new IllegalStateException("Scopes cannot be null");
+    }
+    if (this.lifetime > TWELVE_HOURS_IN_SECONDS) {
+      throw new IllegalStateException("lifetime must be less than or equal to 43200");
     }
   }
 
@@ -267,28 +282,26 @@ public AccessToken refreshAccessToken() throws IOException {
         OAuth2Utils.validateString(responseData, "expireTime", "Expected to find an expireTime");
 
     DateFormat format = new SimpleDateFormat(RFC3339);
-    Date date;
     try {
-      date = format.parse(expireTime);
+      Date date = format.parse(expireTime);
+      return new AccessToken(accessToken, date);
     } catch (ParseException pe) {
       throw new IOException("Error parsing expireTime: " + pe.getMessage());
     }
-    return new AccessToken(accessToken, date);
   }
 
   /**
    * Returns an IdToken for the current Credential.
    *
-   * @param targetAudience the audience field for the issued ID Token
-   * @param options List of Credential specific options for for the token. For example, an IDToken
-   *     for a ImpersonatedCredentials can return the email address within the token claims if
+   * @param targetAudience the audience field for the issued ID token
+   * @param options credential specific options for for the token. For example, an ID token for an
+   *     ImpersonatedCredentials can return the email address within the token claims if
    *     "ImpersonatedCredentials.INCLUDE_EMAIL" is provided as a list option.
* Only one option value is supported: "ImpersonatedCredentials.INCLUDE_EMAIL" If no options - * are set, the default excludes the "includeEmail" attribute in the API request - * @return IdToken object which includes the raw id_token, expiration and audience. - * @throws IOException if the attempt to get an IdToken failed + * are set, the default excludes the "includeEmail" attribute in the API request. + * @return IdToken object which includes the raw id_token, expiration, and audience + * @throws IOException if the attempt to get an ID token failed */ - @Beta @Override public IdToken idTokenWithAudience(String targetAudience, List options) throws IOException { @@ -348,7 +361,7 @@ public static class Builder extends GoogleCredentials.Builder { private String targetPrincipal; private List delegates; private List scopes; - private int lifetime; + private int lifetime = DEFAULT_LIFETIME_IN_SECONDS; private HttpTransportFactory transportFactory; protected Builder() {} @@ -395,7 +408,7 @@ public List getScopes() { } public Builder setLifetime(int lifetime) { - this.lifetime = lifetime; + this.lifetime = lifetime == 0 ? DEFAULT_LIFETIME_IN_SECONDS : lifetime; return this; } diff --git a/oauth2_http/java/com/google/auth/oauth2/JwtProvider.java b/oauth2_http/java/com/google/auth/oauth2/JwtProvider.java index eadee9600..0e25d20e6 100644 --- a/oauth2_http/java/com/google/auth/oauth2/JwtProvider.java +++ b/oauth2_http/java/com/google/auth/oauth2/JwtProvider.java @@ -31,17 +31,13 @@ package com.google.auth.oauth2; -import com.google.common.annotations.Beta; - /** Interface for creating custom JWT tokens */ -@Beta public interface JwtProvider { /** * Returns a new JwtCredentials instance with modified claims. * - * @param newClaims new claims. Any unspecified claim fields will default to the the current - * values. + * @param newClaims new claims. Any unspecified claim fields default to the current values. * @return new credentials */ JwtCredentials jwtWithClaims(JwtClaims newClaims); diff --git a/oauth2_http/java/com/google/auth/oauth2/OAuth2Credentials.java b/oauth2_http/java/com/google/auth/oauth2/OAuth2Credentials.java index ab6c042da..f22d3c74e 100644 --- a/oauth2_http/java/com/google/auth/oauth2/OAuth2Credentials.java +++ b/oauth2_http/java/com/google/auth/oauth2/OAuth2Credentials.java @@ -37,7 +37,6 @@ import com.google.auth.http.AuthHttpConstants; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.MoreObjects; -import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableMap; import com.google.common.collect.Iterables; import java.io.IOException; @@ -131,7 +130,10 @@ public void getRequestMetadata( super.getRequestMetadata(uri, executor, callback); return; } - metadata = Preconditions.checkNotNull(requestMetadata, "cached requestMetadata"); + if (requestMetadata == null) { + throw new NullPointerException("cached requestMetadata"); + } + metadata = requestMetadata; } callback.onSuccess(metadata); } @@ -146,7 +148,10 @@ public Map> getRequestMetadata(URI uri) throws IOException if (shouldRefresh()) { refresh(); } - return Preconditions.checkNotNull(requestMetadata, "requestMetadata"); + if (requestMetadata == null) { + throw new NullPointerException("requestMetadata"); + } + return requestMetadata; } } @@ -156,9 +161,11 @@ public void refresh() throws IOException { synchronized (lock) { requestMetadata = null; temporaryAccess = null; - useAccessToken( - Preconditions.checkNotNull(refreshAccessToken(), "new access token"), - getAdditionalHeaders()); + AccessToken accessToken = refreshAccessToken(); + if (accessToken == null) { + throw new NullPointerException("new access token"); + } + useAccessToken(accessToken, getAdditionalHeaders()); if (changeListeners != null) { for (CredentialsChangedListener listener : changeListeners) { listener.onChanged(this); @@ -214,8 +221,10 @@ private boolean shouldRefresh() { *

Throws IllegalStateException if not overridden since direct use of OAuth2Credentials is only * for temporary or non-refreshing access tokens. * - * @return Refreshed access token. - * @throws IOException from derived implementations + * @return never + * @throws IllegalStateException always. OAuth2Credentials does not support refreshing the access + * token. An instance with a new access token or a derived type that supports refreshing + * should be used instead. */ public AccessToken refreshAccessToken() throws IOException { throw new IllegalStateException( @@ -230,7 +239,7 @@ public AccessToken refreshAccessToken() throws IOException { *

This is called when token content changes, such as when the access token is refreshed. This * is typically used by code caching the access token. * - * @param listener The listener to be added. + * @param listener the listener to be added */ public final void addChangeListener(CredentialsChangedListener listener) { synchronized (lock) { diff --git a/oauth2_http/java/com/google/auth/oauth2/OAuth2Utils.java b/oauth2_http/java/com/google/auth/oauth2/OAuth2Utils.java index d0e4fa15e..54e6bb941 100644 --- a/oauth2_http/java/com/google/auth/oauth2/OAuth2Utils.java +++ b/oauth2_http/java/com/google/auth/oauth2/OAuth2Utils.java @@ -37,7 +37,7 @@ 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.jackson2.JacksonFactory; +import com.google.api.client.json.gson.GsonFactory; import com.google.auth.http.AuthHttpConstants; import com.google.auth.http.HttpTransportFactory; import com.google.common.io.ByteStreams; @@ -49,7 +49,6 @@ import java.io.OutputStream; import java.math.BigDecimal; import java.net.URI; -import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.util.Collection; import java.util.Map; @@ -66,9 +65,7 @@ class OAuth2Utils { static final HttpTransportFactory HTTP_TRANSPORT_FACTORY = new DefaultHttpTransportFactory(); - static final JsonFactory JSON_FACTORY = JacksonFactory.getDefaultInstance(); - - static final Charset UTF_8 = Charset.forName("UTF-8"); + static final JsonFactory JSON_FACTORY = GsonFactory.getDefaultInstance(); private static String VALUE_NOT_FOUND_MESSAGE = "%sExpected value %s not found."; private static String VALUE_WRONG_TYPE_MESSAGE = "%sExpected %s value %s of wrong type."; diff --git a/oauth2_http/java/com/google/auth/oauth2/ServiceAccountCredentials.java b/oauth2_http/java/com/google/auth/oauth2/ServiceAccountCredentials.java index 974959129..e12f8d412 100644 --- a/oauth2_http/java/com/google/auth/oauth2/ServiceAccountCredentials.java +++ b/oauth2_http/java/com/google/auth/oauth2/ServiceAccountCredentials.java @@ -55,7 +55,6 @@ import com.google.api.client.util.SecurityUtils; import com.google.auth.ServiceAccountSigner; import com.google.auth.http.HttpTransportFactory; -import com.google.common.annotations.Beta; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.MoreObjects; import com.google.common.collect.ImmutableSet; @@ -66,6 +65,7 @@ import java.io.StringReader; import java.net.URI; import java.net.URISyntaxException; +import java.nio.charset.StandardCharsets; import java.security.GeneralSecurityException; import java.security.InvalidKeyException; import java.security.KeyFactory; @@ -92,6 +92,8 @@ public class ServiceAccountCredentials extends GoogleCredentials private static final long serialVersionUID = 7807543542681217978L; private static final String GRANT_TYPE = "urn:ietf:params:oauth:grant-type:jwt-bearer"; private static final String PARSE_ERROR_PREFIX = "Error parsing token refresh response. "; + private static final int TWELVE_HOURS_IN_SECONDS = 43200; + private static final int DEFAULT_LIFETIME_IN_SECONDS = 3600; private final String clientId; private final String clientEmail; @@ -103,6 +105,7 @@ public class ServiceAccountCredentials extends GoogleCredentials private final URI tokenServerUri; private final Collection scopes; private final String quotaProjectId; + private final int lifetime; private transient HttpTransportFactory transportFactory; @@ -122,6 +125,10 @@ public class ServiceAccountCredentials extends GoogleCredentials * authority to the service account. * @param projectId the project used for billing * @param quotaProjectId The project used for quota and billing purposes. May be null. + * @param lifetime number of seconds the access token should be valid for. The value should be at + * most 43200 (12 hours). If the token is used for calling a Google API, then the value should + * be at most 3600 (1 hour). If the given value is 0, then the default value 3600 will be used + * when creating the credentials. */ ServiceAccountCredentials( String clientId, @@ -133,7 +140,8 @@ public class ServiceAccountCredentials extends GoogleCredentials URI tokenServerUri, String serviceAccountUser, String projectId, - String quotaProjectId) { + String quotaProjectId, + int lifetime) { this.clientId = clientId; this.clientEmail = Preconditions.checkNotNull(clientEmail); this.privateKey = Preconditions.checkNotNull(privateKey); @@ -148,6 +156,10 @@ public class ServiceAccountCredentials extends GoogleCredentials this.serviceAccountUser = serviceAccountUser; this.projectId = projectId; this.quotaProjectId = quotaProjectId; + if (lifetime > TWELVE_HOURS_IN_SECONDS) { + throw new IllegalStateException("lifetime must be less than or equal to 43200"); + } + this.lifetime = lifetime; } /** @@ -324,7 +336,8 @@ static ServiceAccountCredentials fromPkcs8( tokenServerUri, serviceAccountUser, projectId, - quotaProject); + quotaProject, + DEFAULT_LIFETIME_IN_SECONDS); } /** Helper to convert from a PKCS#8 String to an RSA private key */ @@ -377,7 +390,7 @@ public static ServiceAccountCredentials fromStream( JsonFactory jsonFactory = OAuth2Utils.JSON_FACTORY; JsonObjectParser parser = new JsonObjectParser(jsonFactory); GenericJson fileContents = - parser.parseAndClose(credentialsStream, OAuth2Utils.UTF_8, GenericJson.class); + parser.parseAndClose(credentialsStream, StandardCharsets.UTF_8, GenericJson.class); String fileType = (String) fileContents.get("type"); if (fileType == null) { @@ -462,7 +475,6 @@ public boolean isRequired(HttpResponse response) { * @throws IOException if the attempt to get an IdToken failed * @return IdToken object which includes the raw id_token, expiration and audience */ - @Beta @Override public IdToken idTokenWithAudience(String targetAudience, List