From cb3866ede3494c43f10b30262b2a4ee4e9c7646e Mon Sep 17 00:00:00 2001 From: Timur Sadykov Date: Wed, 5 May 2021 17:14:05 -0700 Subject: [PATCH 01/13] new adapter for idtoken --- .gitignore | 3 + .../auth/http/HttpUserCredentialsAdapter.java | 81 +++++++++++++++++++ .../google/auth/oauth2/UserCredentials.java | 20 +++++ 3 files changed, 104 insertions(+) create mode 100644 oauth2_http/java/com/google/auth/http/HttpUserCredentialsAdapter.java diff --git a/.gitignore b/.gitignore index b637b4b46..fe226042f 100644 --- a/.gitignore +++ b/.gitignore @@ -9,3 +9,6 @@ target/ # Intellij *.iml .idea/ + +# VS Code +.vscode/ \ No newline at end of file diff --git a/oauth2_http/java/com/google/auth/http/HttpUserCredentialsAdapter.java b/oauth2_http/java/com/google/auth/http/HttpUserCredentialsAdapter.java new file mode 100644 index 000000000..22e7500b2 --- /dev/null +++ b/oauth2_http/java/com/google/auth/http/HttpUserCredentialsAdapter.java @@ -0,0 +1,81 @@ +/* + * Copyright 2015, Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.google.auth.http; + +import com.google.api.client.http.HttpHeaders; +import com.google.api.client.http.HttpRequest; +import com.google.auth.Credentials; +import java.io.IOException; +import java.net.URI; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +/** A wrapper for using Credentials with the Google API Client Libraries for Java with Http. */ +public class HttpUserCredentialsAdapter extends HttpCredentialsAdapter { + + /** @param credentials Credentials instance to adapt for HTTP */ + public HttpUserCredentialsAdapter(Credentials credentials) { + super(credentials); + } + + /** + * {@inheritDoc} + * + *

Initialize the HTTP request prior to execution. + * + * @param request HTTP request + */ + @Override + public void initialize(HttpRequest request) throws IOException { + request.setUnsuccessfulResponseHandler(this); + + if (!credentials.hasRequestMetadata()) { + return; + } + HttpHeaders requestHeaders = request.getHeaders(); + URI uri = null; + if (request.getUrl() != null) { + uri = request.getUrl().toURI(); + } + Map> credentialHeaders = credentials.getRequestMetadata(uri); + if (credentialHeaders == null) { + return; + } + for (Map.Entry> entry : credentialHeaders.entrySet()) { + String headerName = entry.getKey(); + List requestValues = new ArrayList<>(); + requestValues.addAll(entry.getValue()); + requestHeaders.put(headerName, requestValues); + } + } +} \ No newline at end of file diff --git a/oauth2_http/java/com/google/auth/oauth2/UserCredentials.java b/oauth2_http/java/com/google/auth/oauth2/UserCredentials.java index b8a05bcd1..8745a9e90 100644 --- a/oauth2_http/java/com/google/auth/oauth2/UserCredentials.java +++ b/oauth2_http/java/com/google/auth/oauth2/UserCredentials.java @@ -44,6 +44,7 @@ import com.google.api.client.json.JsonObjectParser; import com.google.api.client.util.GenericData; import com.google.api.client.util.Preconditions; +import com.google.api.client.util.StringUtils; import com.google.auth.http.HttpTransportFactory; import com.google.common.base.MoreObjects; import java.io.ByteArrayInputStream; @@ -71,6 +72,8 @@ public class UserCredentials extends GoogleCredentials implements QuotaProjectId private final String transportFactoryClassName; private final String quotaProjectId; + private IdToken idToken; + private transient HttpTransportFactory transportFactory; /** @@ -202,6 +205,14 @@ public AccessToken refreshAccessToken() throws IOException { request.setParser(new JsonObjectParser(JSON_FACTORY)); HttpResponse response = request.execute(); GenericData responseData = response.parseAs(GenericData.class); + + String idTokenKey = "id_token"; + if (responseData.containsKey(idTokenKey)) { + String idTokenString = + OAuth2Utils.validateString(responseData, idTokenKey, PARSE_ERROR_PREFIX); + idToken = IdToken.create(idTokenString); + } + String accessToken = OAuth2Utils.validateString(responseData, "access_token", PARSE_ERROR_PREFIX); int expiresInSeconds = @@ -237,6 +248,15 @@ public final String getRefreshToken() { return refreshToken; } + /** + * Returns the id token resulting from a OAuth2 consent flow. + * + * @return id token + */ + public final IdToken getIdToken() { + return idToken; + } + /** * Returns the instance of InputStream containing the following user credentials in JSON format: - * RefreshToken - ClientId - ClientSecret - ServerTokenUri From 9def95a56c6463bb44b7eb3fd5b907c588fa3385 Mon Sep 17 00:00:00 2001 From: stim Date: Thu, 6 May 2021 01:55:29 -0700 Subject: [PATCH 02/13] poc of the IdtokenProvider implementation for UserCredentials --- .../auth/oauth2/IdTokenCredentials.java | 10 +++ .../google/auth/oauth2/UserCredentials.java | 77 ++++++++++++------- 2 files changed, 59 insertions(+), 28 deletions(-) diff --git a/oauth2_http/java/com/google/auth/oauth2/IdTokenCredentials.java b/oauth2_http/java/com/google/auth/oauth2/IdTokenCredentials.java index 5629d4e81..eea8e3e77 100644 --- a/oauth2_http/java/com/google/auth/oauth2/IdTokenCredentials.java +++ b/oauth2_http/java/com/google/auth/oauth2/IdTokenCredentials.java @@ -110,6 +110,16 @@ public class IdTokenCredentials extends OAuth2Credentials { private IdTokenCredentials(Builder builder) { this.idTokenProvider = Preconditions.checkNotNull(builder.getIdTokenProvider()); + + // if UserCredential is used as id token provider - we need to refresh access token + if (this.idTokenProvider instanceof UserCredentials) { + try { + this.refresh(); + } catch (IOException ignored) { + // ignore because all we need here is to reset the metadata + } + + } this.targetAudience = Preconditions.checkNotNull(builder.getTargetAudience()); this.options = builder.getOptions(); } diff --git a/oauth2_http/java/com/google/auth/oauth2/UserCredentials.java b/oauth2_http/java/com/google/auth/oauth2/UserCredentials.java index 8745a9e90..654bb9abd 100644 --- a/oauth2_http/java/com/google/auth/oauth2/UserCredentials.java +++ b/oauth2_http/java/com/google/auth/oauth2/UserCredentials.java @@ -44,7 +44,6 @@ import com.google.api.client.json.JsonObjectParser; import com.google.api.client.util.GenericData; import com.google.api.client.util.Preconditions; -import com.google.api.client.util.StringUtils; import com.google.auth.http.HttpTransportFactory; import com.google.common.base.MoreObjects; import java.io.ByteArrayInputStream; @@ -59,7 +58,7 @@ import java.util.Objects; /** 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. "; @@ -72,8 +71,6 @@ public class UserCredentials extends GoogleCredentials implements QuotaProjectId private final String transportFactoryClassName; private final String quotaProjectId; - private IdToken idToken; - private transient HttpTransportFactory transportFactory; /** @@ -189,30 +186,7 @@ public static UserCredentials fromStream( /** Refreshes the OAuth2 access token by getting a new access token from the refresh token */ @Override public AccessToken refreshAccessToken() throws IOException { - if (refreshToken == null) { - throw new IllegalStateException( - "UserCredentials instance cannot refresh because there is no" + " refresh token."); - } - GenericData tokenRequest = new GenericData(); - tokenRequest.set("client_id", clientId); - tokenRequest.set("client_secret", clientSecret); - tokenRequest.set("refresh_token", refreshToken); - tokenRequest.set("grant_type", GRANT_TYPE); - UrlEncodedContent content = new UrlEncodedContent(tokenRequest); - - HttpRequestFactory requestFactory = transportFactory.create().createRequestFactory(); - HttpRequest request = requestFactory.buildPostRequest(new GenericUrl(tokenServerUri), content); - request.setParser(new JsonObjectParser(JSON_FACTORY)); - HttpResponse response = request.execute(); - GenericData responseData = response.parseAs(GenericData.class); - - String idTokenKey = "id_token"; - if (responseData.containsKey(idTokenKey)) { - String idTokenString = - OAuth2Utils.validateString(responseData, idTokenKey, PARSE_ERROR_PREFIX); - idToken = IdToken.create(idTokenString); - } - + GenericData responseData = doRefreshAccessToken(); String accessToken = OAuth2Utils.validateString(responseData, "access_token", PARSE_ERROR_PREFIX); int expiresInSeconds = @@ -221,6 +195,29 @@ public AccessToken refreshAccessToken() throws IOException { return new AccessToken(accessToken, new Date(expiresAtMilliseconds)); } + /** + * Returns a Google ID Token from the refresh token response. + * + * @param targetAudience the aud: field the IdToken should include. Currently unused for + * UserCredentials. + * @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, expiration and audience + */ + @Override + public IdToken idTokenWithAudience(String targetAudience, List

Initialize the HTTP request prior to execution. - * - * @param request HTTP request - */ - @Override - public void initialize(HttpRequest request) throws IOException { - request.setUnsuccessfulResponseHandler(this); - - if (!credentials.hasRequestMetadata()) { - return; - } - HttpHeaders requestHeaders = request.getHeaders(); - URI uri = null; - if (request.getUrl() != null) { - uri = request.getUrl().toURI(); - } - Map> credentialHeaders = credentials.getRequestMetadata(uri); - if (credentialHeaders == null) { - return; - } - for (Map.Entry> entry : credentialHeaders.entrySet()) { - String headerName = entry.getKey(); - List requestValues = new ArrayList<>(); - requestValues.addAll(entry.getValue()); - requestHeaders.put(headerName, requestValues); - } - } -} \ No newline at end of file From 74b0994bb2523e900afc37ebe8f4f75c0168710b Mon Sep 17 00:00:00 2001 From: stim Date: Mon, 10 May 2021 23:49:47 -0700 Subject: [PATCH 04/13] fix build --- .../java/com/google/auth/oauth2/UserCredentials.java | 9 --------- 1 file changed, 9 deletions(-) diff --git a/oauth2_http/java/com/google/auth/oauth2/UserCredentials.java b/oauth2_http/java/com/google/auth/oauth2/UserCredentials.java index 654bb9abd..96df9d2b5 100644 --- a/oauth2_http/java/com/google/auth/oauth2/UserCredentials.java +++ b/oauth2_http/java/com/google/auth/oauth2/UserCredentials.java @@ -245,15 +245,6 @@ public final String getRefreshToken() { return refreshToken; } - /** - * Returns the id token resulting from a OAuth2 consent flow. - * - * @return id token - */ - public final IdToken getIdToken() { - return idToken; - } - /** * Does refresh access token request * From 195e37fb90dac7b7510f498604caae76b3781b7c Mon Sep 17 00:00:00 2001 From: Timur Sadykov Date: Tue, 11 May 2021 00:50:36 -0700 Subject: [PATCH 05/13] linter fixes --- .../auth/oauth2/IdTokenCredentials.java | 3 +-- .../google/auth/oauth2/UserCredentials.java | 26 +++++++------------ 2 files changed, 11 insertions(+), 18 deletions(-) diff --git a/oauth2_http/java/com/google/auth/oauth2/IdTokenCredentials.java b/oauth2_http/java/com/google/auth/oauth2/IdTokenCredentials.java index eea8e3e77..1364d956b 100644 --- a/oauth2_http/java/com/google/auth/oauth2/IdTokenCredentials.java +++ b/oauth2_http/java/com/google/auth/oauth2/IdTokenCredentials.java @@ -113,12 +113,11 @@ private IdTokenCredentials(Builder builder) { // if UserCredential is used as id token provider - we need to refresh access token if (this.idTokenProvider instanceof UserCredentials) { - try { + try { this.refresh(); } catch (IOException ignored) { // ignore because all we need here is to reset the metadata } - } this.targetAudience = Preconditions.checkNotNull(builder.getTargetAudience()); this.options = builder.getOptions(); diff --git a/oauth2_http/java/com/google/auth/oauth2/UserCredentials.java b/oauth2_http/java/com/google/auth/oauth2/UserCredentials.java index 654bb9abd..27bb59a53 100644 --- a/oauth2_http/java/com/google/auth/oauth2/UserCredentials.java +++ b/oauth2_http/java/com/google/auth/oauth2/UserCredentials.java @@ -58,7 +58,8 @@ import java.util.Objects; /** OAuth2 Credentials representing a user's identity and consent. */ -public class UserCredentials extends GoogleCredentials implements QuotaProjectIdProvider, IdTokenProvider { +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. "; @@ -206,16 +207,18 @@ public AccessToken refreshAccessToken() throws IOException { * @return IdToken object which includes the raw id_token, expiration and audience */ @Override - public IdToken idTokenWithAudience(String targetAudience, List