diff --git a/cab-token-generator/java/com/google/auth/credentialaccessboundary/ClientSideCredentialAccessBoundaryFactory.java b/cab-token-generator/java/com/google/auth/credentialaccessboundary/ClientSideCredentialAccessBoundaryFactory.java new file mode 100644 index 000000000..26d2479d6 --- /dev/null +++ b/cab-token-generator/java/com/google/auth/credentialaccessboundary/ClientSideCredentialAccessBoundaryFactory.java @@ -0,0 +1,209 @@ +/* + * Copyright 2024, Google LLC + * + * 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 LLC 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.credentialaccessboundary; + +import static com.google.auth.oauth2.OAuth2Credentials.getFromServiceLoader; +import static com.google.auth.oauth2.OAuth2Utils.TOKEN_EXCHANGE_URL_FORMAT; +import static com.google.common.base.Preconditions.checkNotNull; + +import com.google.auth.Credentials; +import com.google.auth.http.HttpTransportFactory; +import com.google.auth.oauth2.AccessToken; +import com.google.auth.oauth2.CredentialAccessBoundary; +import com.google.auth.oauth2.GoogleCredentials; +import com.google.auth.oauth2.OAuth2Utils; +import com.google.auth.oauth2.StsRequestHandler; +import com.google.auth.oauth2.StsTokenExchangeRequest; +import com.google.auth.oauth2.StsTokenExchangeResponse; +import com.google.common.base.Strings; +import com.google.errorprone.annotations.CanIgnoreReturnValue; +import java.io.IOException; + +public final class ClientSideCredentialAccessBoundaryFactory { + private final GoogleCredentials sourceCredential; + private final transient HttpTransportFactory transportFactory; + private final String tokenExchangeEndpoint; + private String accessBoundarySessionKey; + private AccessToken intermediateAccessToken; + + private ClientSideCredentialAccessBoundaryFactory(Builder builder) { + this.transportFactory = builder.transportFactory; + this.sourceCredential = builder.sourceCredential; + this.tokenExchangeEndpoint = builder.tokenExchangeEndpoint; + } + + /** + * Refreshes the source credential and exchanges it for an intermediary access token using the STS + * endpoint. + * + *
If the source credential is expired, it will be refreshed. A token exchange request is then + * made to the STS endpoint. The resulting intermediary access token and access boundary session + * key are stored. The intermediary access token's expiration time is determined as follows: + * + *
This class handles the process of exchanging one type of token for another using the Security + * Token Service (STS). It constructs and sends the token exchange request to the STS endpoint and + * parses the response to create an {@link StsTokenExchangeResponse} object. + * + *
Use the {@link #newBuilder(String, StsTokenExchangeRequest, HttpRequestFactory)} method to + * create a new builder for constructing an instance of this class. + */ +public final class StsRequestHandler { private static final String TOKEN_EXCHANGE_GRANT_TYPE = "urn:ietf:params:oauth:grant-type:token-exchange"; private static final String PARSE_ERROR_PREFIX = "Error parsing token response."; @@ -85,6 +95,14 @@ private StsRequestHandler( this.internalOptions = internalOptions; } + /** + * Returns a new builder for creating an instance of {@link StsRequestHandler}. + * + * @param tokenExchangeEndpoint The STS token exchange endpoint. + * @param stsTokenExchangeRequest The STS token exchange request. + * @param httpRequestFactory The HTTP request factory to use for sending the request. + * @return A new builder instance. + */ public static Builder newBuilder( String tokenExchangeEndpoint, StsTokenExchangeRequest stsTokenExchangeRequest, @@ -175,6 +193,11 @@ private StsTokenExchangeResponse buildResponse(GenericData responseData) throws String scope = OAuth2Utils.validateString(responseData, "scope", PARSE_ERROR_PREFIX); builder.setScopes(Arrays.asList(scope.trim().split("\\s+"))); } + if (responseData.containsKey("access_boundary_session_key")) { + builder.setAccessBoundarySessionKey( + OAuth2Utils.validateString( + responseData, "access_boundary_session_key", PARSE_ERROR_PREFIX)); + } return builder.build(); } diff --git a/oauth2_http/java/com/google/auth/oauth2/StsTokenExchangeRequest.java b/oauth2_http/java/com/google/auth/oauth2/StsTokenExchangeRequest.java index a231fe383..f0ce390ed 100644 --- a/oauth2_http/java/com/google/auth/oauth2/StsTokenExchangeRequest.java +++ b/oauth2_http/java/com/google/auth/oauth2/StsTokenExchangeRequest.java @@ -38,10 +38,18 @@ import javax.annotation.Nullable; /** - * Defines an OAuth 2.0 token exchange request. Based on - * https://tools.ietf.org/html/rfc8693#section-2.1. + * Represents an OAuth 2.0 token exchange request, as defined in RFC 8693, Section 2.1. + * + *
This class encapsulates the parameters necessary for making a token exchange request to Google + * Security Token Service (STS). It includes the subject token, subject token type, optional + * parameters like acting party, scopes, resource, audience, requested token type, and internal + * options. + * + *
Instances of this class are immutable. Use the {@link #newBuilder(String, String)} method to + * create a new builder. */ -final class StsTokenExchangeRequest { +public final class StsTokenExchangeRequest { private static final String GRANT_TYPE = "urn:ietf:params:oauth:grant-type:token-exchange"; private final String subjectToken; @@ -73,6 +81,15 @@ private StsTokenExchangeRequest( this.internalOptions = internalOptions; } + /** + * Returns a new {@link StsTokenExchangeRequest.Builder} instance. + * + * @param subjectToken The token being exchanged. This represents the credentials being used to + * authorize the token exchange request. + * @param subjectTokenType The type of the {@code subjectToken}. For example, {@link + * OAuth2Utils#TOKEN_TYPE_ACCESS_TOKEN}. + * @return A new builder for creating {@code StsTokenExchangeRequest} instances. + */ public static Builder newBuilder(String subjectToken, String subjectTokenType) { return new Builder(subjectToken, subjectTokenType); } diff --git a/oauth2_http/java/com/google/auth/oauth2/StsTokenExchangeResponse.java b/oauth2_http/java/com/google/auth/oauth2/StsTokenExchangeResponse.java index 90a94e16d..62275778a 100644 --- a/oauth2_http/java/com/google/auth/oauth2/StsTokenExchangeResponse.java +++ b/oauth2_http/java/com/google/auth/oauth2/StsTokenExchangeResponse.java @@ -40,10 +40,18 @@ import javax.annotation.Nullable; /** - * Defines an OAuth 2.0 token exchange successful response. Based on - * https://tools.ietf.org/html/rfc8693#section-2.2.1. + * Represents a successful OAuth 2.0 token exchange response from the Google Security Token Service + * (STS), as defined in RFC 8693, + * Section 2.2.1. + * + *
This class provides access to the exchanged access token, issued token type, token type, + * expiration time, refresh token (optional), scopes (optional), and the access boundary session key + * (optional). + * + *
Instances are immutable. Use {@link #newBuilder(String, String, String)} to create an
+ * instance.
*/
-final class StsTokenExchangeResponse {
+public final class StsTokenExchangeResponse {
private final AccessToken accessToken;
private final String issuedTokenType;
private final String tokenType;
@@ -51,6 +59,7 @@ final class StsTokenExchangeResponse {
@Nullable private final Long expiresInSeconds;
@Nullable private final String refreshToken;
@Nullable private final List> scopeSequence = new ArrayDeque<>();
private final Queue