From 397694b482cef3c05e0ce7fc6e5843b6943dae86 Mon Sep 17 00:00:00 2001 From: Guillaume Blaqueire Date: Fri, 28 Aug 2020 16:55:06 +0200 Subject: [PATCH 1/9] Feat: add signed id token capability on UserCredentials class. idTokenWithAudience method not tested (I don't know how to do this to be useful) Depend on this update https://github.com/googleapis/google-http-java-client/pull/1100 --- .../google/auth/oauth2/UserCredentials.java | 112 ++++++++++++++++-- .../auth/oauth2/UserCredentialsTest.java | 46 ++++++- 2 files changed, 145 insertions(+), 13 deletions(-) diff --git a/oauth2_http/java/com/google/auth/oauth2/UserCredentials.java b/oauth2_http/java/com/google/auth/oauth2/UserCredentials.java index 5010a9ae6..47be683fe 100644 --- a/oauth2_http/java/com/google/auth/oauth2/UserCredentials.java +++ b/oauth2_http/java/com/google/auth/oauth2/UserCredentials.java @@ -35,34 +35,36 @@ import static com.google.auth.oauth2.OAuth2Utils.UTF_8; import static com.google.common.base.MoreObjects.firstNonNull; -import com.google.api.client.http.GenericUrl; -import com.google.api.client.http.HttpRequest; -import com.google.api.client.http.HttpRequestFactory; -import com.google.api.client.http.HttpResponse; -import com.google.api.client.http.UrlEncodedContent; +import com.google.api.client.http.*; 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.util.GenericData; import com.google.api.client.util.Preconditions; import com.google.auth.http.HttpTransportFactory; +import com.google.common.annotations.Beta; import com.google.common.base.MoreObjects; +import com.google.common.collect.ImmutableSet; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.io.ObjectInputStream; import java.net.URI; -import java.util.Date; -import java.util.List; -import java.util.Map; -import java.util.Objects; +import java.util.*; /** OAuth2 Credentials representing a user's identity and consent. */ -public class UserCredentials extends GoogleCredentials implements QuotaProjectIdProvider { +public class UserCredentials extends GoogleCredentials + implements QuotaProjectIdProvider, IdTokenProvider { private static final String GRANT_TYPE = "refresh_token"; private static final String PARSE_ERROR_PREFIX = "Error parsing token refresh response. "; private static final long serialVersionUID = -4800758775038679176L; + public static final String GOOGLE_CLIENT_ID = "32555940559.apps.googleusercontent.com"; + public static final String GOOGLE_CLIENT_SECRET = "ZmssLNjJy2998hD4CTg2ejr2"; + public static final Collection GOOGLE_DEFAULT_SCOPES = + ImmutableSet.of( + "https://www.googleapis.com/auth/userinfo.email", + "https://www.googleapis.com/auth/accounts.reauth"); private final String clientId; private final String clientSecret; @@ -70,6 +72,7 @@ public class UserCredentials extends GoogleCredentials implements QuotaProjectId private final URI tokenServerUri; private final String transportFactoryClassName; private final String quotaProjectId; + private final Collection scopes; private transient HttpTransportFactory transportFactory; @@ -89,6 +92,7 @@ private UserCredentials( String clientSecret, String refreshToken, AccessToken accessToken, + Collection scopes, HttpTransportFactory transportFactory, URI tokenServerUri, String quotaProjectId) { @@ -96,6 +100,13 @@ private UserCredentials( this.clientId = Preconditions.checkNotNull(clientId); this.clientSecret = Preconditions.checkNotNull(clientSecret); this.refreshToken = refreshToken; + // Merge the scope with the default and mandatory ones. + Collection mergedScopes = new ArrayList<>(); + mergedScopes.addAll(GOOGLE_DEFAULT_SCOPES); + if (scopes != null) { + mergedScopes.addAll(scopes); + } + this.scopes = ImmutableSet.copyOf(mergedScopes); this.transportFactory = firstNonNull( transportFactory, @@ -261,6 +272,7 @@ private InputStream getUserCredentialsStream() throws IOException { if (quotaProjectId != null) { json.put("quota_project", clientSecret); } + json.put("scopes", scopes); json.setFactory(JSON_FACTORY); String text = json.toPrettyString(); return new ByteArrayInputStream(text.getBytes(UTF_8)); @@ -290,6 +302,7 @@ public int hashCode() { clientSecret, refreshToken, tokenServerUri, + scopes, transportFactoryClassName, quotaProjectId); } @@ -304,6 +317,7 @@ public String toString() { .add("tokenServerUri", tokenServerUri) .add("transportFactoryClassName", transportFactoryClassName) .add("quotaProjectId", quotaProjectId) + .add("scopes", scopes) .toString(); } @@ -319,6 +333,7 @@ public boolean equals(Object obj) { && Objects.equals(this.refreshToken, other.refreshToken) && Objects.equals(this.tokenServerUri, other.tokenServerUri) && Objects.equals(this.transportFactoryClassName, other.transportFactoryClassName) + && Objects.equals(this.scopes, other.scopes) && Objects.equals(this.quotaProjectId, other.quotaProjectId); } @@ -340,6 +355,71 @@ public String getQuotaProjectId() { return quotaProjectId; } + /** + * Clone the UserCredential with the specified scopes. + * + *

Should be called before use for instances with empty scopes. + */ + @Override + /* public GoogleCredentials createScoped(Collection newScopes) { + + this.scopes = scopes; + return this; + }*/ + + public GoogleCredentials createScoped(Collection newScopes) { + return new UserCredentials( + clientId, + clientSecret, + refreshToken, + getAccessToken(), + newScopes, + transportFactory, + tokenServerUri, + quotaProjectId); + } + + /** + * Returns a Google ID Token from the user credential + * + * @param targetAudience currently unused for UserCredential. + * @param options list of Credential specific options for for the token. Currently unused for + * UserCredentials. + * @throws IOException if the attempt to get an IdToken failed + * @return IdToken object which includes the raw id_token and expiration + */ + @Beta + @Override + public IdToken idTokenWithAudience(String targetAudience, List

Should be called before use for instances with empty scopes. */ @Override - /* public GoogleCredentials createScoped(Collection newScopes) { - - this.scopes = scopes; - return this; - }*/ - public GoogleCredentials createScoped(Collection newScopes) { return new UserCredentials( clientId, From c94a0ce4287c4f375577521dec1e96d3316808d5 Mon Sep 17 00:00:00 2001 From: Guillaume Blaquiere Date: Tue, 29 Sep 2020 21:06:49 +0200 Subject: [PATCH 4/9] Remove useless scopes. --- .../google/auth/oauth2/UserCredentials.java | 59 +---------------- .../auth/oauth2/UserCredentialsTest.java | 66 +------------------ 2 files changed, 3 insertions(+), 122 deletions(-) diff --git a/oauth2_http/java/com/google/auth/oauth2/UserCredentials.java b/oauth2_http/java/com/google/auth/oauth2/UserCredentials.java index cd767d857..d4de6dc24 100644 --- a/oauth2_http/java/com/google/auth/oauth2/UserCredentials.java +++ b/oauth2_http/java/com/google/auth/oauth2/UserCredentials.java @@ -71,18 +71,12 @@ public class UserCredentials extends GoogleCredentials private static final long serialVersionUID = -4800758775038679176L; public static final String GOOGLE_CLIENT_ID = "32555940559.apps.googleusercontent.com"; public static final String GOOGLE_CLIENT_SECRET = "ZmssLNjJy2998hD4CTg2ejr2"; - public static final Collection GOOGLE_DEFAULT_SCOPES = - ImmutableSet.of( - "https://www.googleapis.com/auth/userinfo.email", - "https://www.googleapis.com/auth/accounts.reauth"); - private final String clientId; private final String clientSecret; private final String refreshToken; private final URI tokenServerUri; private final String transportFactoryClassName; private final String quotaProjectId; - private final Collection scopes; private transient HttpTransportFactory transportFactory; @@ -93,8 +87,6 @@ public class UserCredentials extends GoogleCredentials * @param clientSecret Client ID of the credential from the console. * @param refreshToken A refresh token resulting from a OAuth2 consent flow. * @param accessToken Initial or temporary access token. - * @param scopes Scope strings for the APIs to be called. May be null or an empty collection, - * which results in a credential that must have createScoped called before use. * @param transportFactory HTTP transport factory, creates the transport used to get access * tokens. * @param tokenServerUri URI of the end point that provides tokens @@ -104,7 +96,6 @@ private UserCredentials( String clientSecret, String refreshToken, AccessToken accessToken, - Collection scopes, HttpTransportFactory transportFactory, URI tokenServerUri, String quotaProjectId) { @@ -112,13 +103,6 @@ private UserCredentials( this.clientId = Preconditions.checkNotNull(clientId); this.clientSecret = Preconditions.checkNotNull(clientSecret); this.refreshToken = refreshToken; - // Merge the scope with the default and mandatory ones. - Collection mergedScopes = new ArrayList<>(); - mergedScopes.addAll(GOOGLE_DEFAULT_SCOPES); - if (scopes != null) { - mergedScopes.addAll(scopes); - } - this.scopes = ImmutableSet.copyOf(mergedScopes); this.transportFactory = firstNonNull( transportFactory, @@ -284,7 +268,6 @@ private InputStream getUserCredentialsStream() throws IOException { if (quotaProjectId != null) { json.put("quota_project", clientSecret); } - json.put("scopes", scopes); json.setFactory(JSON_FACTORY); String text = json.toPrettyString(); return new ByteArrayInputStream(text.getBytes(UTF_8)); @@ -314,7 +297,6 @@ public int hashCode() { clientSecret, refreshToken, tokenServerUri, - scopes, transportFactoryClassName, quotaProjectId); } @@ -329,7 +311,6 @@ public String toString() { .add("tokenServerUri", tokenServerUri) .add("transportFactoryClassName", transportFactoryClassName) .add("quotaProjectId", quotaProjectId) - .add("scopes", scopes) .toString(); } @@ -345,7 +326,6 @@ public boolean equals(Object obj) { && Objects.equals(this.refreshToken, other.refreshToken) && Objects.equals(this.tokenServerUri, other.tokenServerUri) && Objects.equals(this.transportFactoryClassName, other.transportFactoryClassName) - && Objects.equals(this.scopes, other.scopes) && Objects.equals(this.quotaProjectId, other.quotaProjectId); } @@ -367,24 +347,6 @@ public String getQuotaProjectId() { return quotaProjectId; } - /** - * Clone the UserCredential with the specified scopes. - * - *

Should be called before use for instances with empty scopes. - */ - @Override - public GoogleCredentials createScoped(Collection newScopes) { - return new UserCredentials( - clientId, - clientSecret, - refreshToken, - getAccessToken(), - newScopes, - transportFactory, - tokenServerUri, - quotaProjectId); - } - /** * Returns a Google ID Token from the user credential * @@ -405,15 +367,8 @@ public IdToken idTokenWithAudience(String targetAudience, List