1 /* 2 * Copyright 2016 Google LLC 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions are 6 * met: 7 * 8 * * Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * * Redistributions in binary form must reproduce the above 11 * copyright notice, this list of conditions and the following disclaimer 12 * in the documentation and/or other materials provided with the 13 * distribution. 14 * * Neither the name of Google LLC nor the names of its 15 * contributors may be used to endorse or promote products derived from 16 * this software without specific prior written permission. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 */ 30 package com.google.api.gax.core; 31 32 import com.google.api.core.BetaApi; 33 import com.google.auth.Credentials; 34 import com.google.auth.oauth2.GoogleCredentials; 35 import com.google.auth.oauth2.ServiceAccountCredentials; 36 import com.google.auth.oauth2.ServiceAccountJwtAccessCredentials; 37 import com.google.auto.value.AutoValue; 38 import com.google.common.annotations.VisibleForTesting; 39 import com.google.common.collect.ImmutableList; 40 import java.io.IOException; 41 import java.util.List; 42 import javax.annotation.Nullable; 43 44 /** 45 * GoogleCredentialsProvider acquires credentials using Application Default Credentials. 46 * 47 * <p>For more information on Application Default Credentials, see <a 48 * href="https://developers.google.com/identity/protocols/application-default-credentials"> 49 * https://developers.google.com/identity/protocols/application-default-credentials</a>. 50 */ 51 @AutoValue 52 public abstract class GoogleCredentialsProvider implements CredentialsProvider { 53 getScopesToApply()54 public abstract List<String> getScopesToApply(); 55 56 @BetaApi getJwtEnabledScopes()57 public abstract List<String> getJwtEnabledScopes(); 58 59 @BetaApi getUseJwtAccessWithScope()60 public abstract boolean getUseJwtAccessWithScope(); 61 62 @VisibleForTesting 63 @Nullable getOAuth2Credentials()64 abstract GoogleCredentials getOAuth2Credentials(); 65 66 @Override getCredentials()67 public Credentials getCredentials() throws IOException { 68 GoogleCredentials credentials = getOAuth2Credentials(); 69 if (credentials == null) { 70 credentials = GoogleCredentials.getApplicationDefault(); 71 } 72 73 // Check if the current scopes permit JWT token use 74 boolean hasJwtEnabledScope = false; 75 for (String scope : getJwtEnabledScopes()) { 76 if (getScopesToApply().contains(scope)) { 77 hasJwtEnabledScope = true; 78 break; 79 } 80 } 81 // Use JWT tokens when using a service account with an appropriate scope. 82 if (credentials instanceof ServiceAccountCredentials && hasJwtEnabledScope) { 83 ServiceAccountCredentials serviceAccount = (ServiceAccountCredentials) credentials; 84 85 return ServiceAccountJwtAccessCredentials.newBuilder() 86 .setClientEmail(serviceAccount.getClientEmail()) 87 .setClientId(serviceAccount.getClientId()) 88 .setPrivateKey(serviceAccount.getPrivateKey()) 89 .setPrivateKeyId(serviceAccount.getPrivateKeyId()) 90 .setQuotaProjectId(serviceAccount.getQuotaProjectId()) 91 .build(); 92 } 93 94 if (credentials.createScopedRequired()) { 95 credentials = credentials.createScoped(getScopesToApply()); 96 } 97 98 if (getUseJwtAccessWithScope() && credentials instanceof ServiceAccountCredentials) { 99 // See https://google.aip.dev/auth/4111 for self signed JWT. 100 ServiceAccountCredentials serviceAccount = (ServiceAccountCredentials) credentials; 101 return serviceAccount.createWithUseJwtAccessWithScope(true); 102 } 103 return credentials; 104 } 105 newBuilder()106 public static Builder newBuilder() { 107 return new AutoValue_GoogleCredentialsProvider.Builder() 108 .setJwtEnabledScopes(ImmutableList.<String>of()) 109 .setUseJwtAccessWithScope(false); 110 } 111 toBuilder()112 public abstract Builder toBuilder(); 113 114 @BetaApi 115 @AutoValue.Builder 116 public abstract static class Builder { 117 118 /** 119 * Sets the scopes to apply to the credentials that are acquired from Application Default 120 * Credentials, before the credentials are sent to the service. 121 */ setScopesToApply(List<String> val)122 public abstract Builder setScopesToApply(List<String> val); 123 124 /** The scopes previously provided. */ getScopesToApply()125 public abstract List<String> getScopesToApply(); 126 127 @VisibleForTesting setOAuth2Credentials(GoogleCredentials oauth2Credentials)128 abstract Builder setOAuth2Credentials(GoogleCredentials oauth2Credentials); 129 130 /** 131 * Sets the scopes that are compatible with JWT tokens. 132 * 133 * <p>JWT Tokens don't support scopes, they only support audiences. Audiences allow access to 134 * the entire service as opposed some subset (ie. access can't be restricted to use the scope 135 * {@code https://www.googleapis.com/auth/bigtable.data.readonly}). A service client can opt-in 136 * to using JWT tokens by specifying which scopes encompass the entire service. If any of those 137 * scopes are present when the client is using {@link ServiceAccountCredentials}, then JWT 138 * tokens will be used for authentication. 139 */ 140 @BetaApi setJwtEnabledScopes(List<String> val)141 public abstract Builder setJwtEnabledScopes(List<String> val); 142 143 /** The JWT enable scopes previously provided. */ 144 @BetaApi getJwtEnabledScopes()145 public abstract List<String> getJwtEnabledScopes(); 146 147 /** Whether self signed JWT with scopes should be used for service account credentials. */ 148 @BetaApi setUseJwtAccessWithScope(boolean val)149 public abstract Builder setUseJwtAccessWithScope(boolean val); 150 151 /** The UseJwtAccessWithScope value previously provided. */ 152 @BetaApi getUseJwtAccessWithScope()153 public abstract boolean getUseJwtAccessWithScope(); 154 build()155 public GoogleCredentialsProvider build() { 156 setScopesToApply(ImmutableList.copyOf(getScopesToApply())); 157 setJwtEnabledScopes(ImmutableList.copyOf(getJwtEnabledScopes())); 158 setUseJwtAccessWithScope(getUseJwtAccessWithScope()); 159 return autoBuild(); 160 } 161 autoBuild()162 abstract GoogleCredentialsProvider autoBuild(); 163 } 164 } 165