Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

#492 id token from service account credential api #493

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
101 changes: 96 additions & 5 deletions oauth2_http/java/com/google/auth/oauth2/UserCredentials.java
Original file line number Diff line number Diff line change
Expand Up @@ -45,23 +45,29 @@
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.HttpCredentialsAdapter;
import com.google.auth.http.HttpTransportFactory;
import com.google.common.base.MoreObjects;
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.*;
import java.util.logging.Logger;
import com.google.api.client.http.javanet.NetHttpTransport;


/** 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 Logger LOGGER = Logger.getLogger(HttpCredentialsAdapter.class.getName());

private static final String GRANT_TYPE = "refresh_token";
private static final String PARSE_ERROR_PREFIX = "Error parsing token refresh response. ";
private static final String RESOURCE_MANAGER_API = "https://cloudresourcemanager.googleapis.com/v1/";
private static final String SERVICE_ACCOUNT_APPLICATION_CREDENTIALS = "SERVICE_ACCOUNT_APPLICATION_CREDENTIALS";
private static final String DEFAULT_COMPUTE_ENGINE_SERVICE_ACCOUNT_SUFFIX = "-compute@developer.gserviceaccount.com";
private static final long serialVersionUID = -4800758775038679176L;

private final String clientId;
Expand All @@ -70,9 +76,19 @@ public class UserCredentials extends GoogleCredentials implements QuotaProjectId
private final URI tokenServerUri;
private final String transportFactoryClassName;
private final String quotaProjectId;
private String serviceAccountCredentialEmail;

private transient HttpTransportFactory transportFactory;

//For test purpose
UserCredentials(){
clientId = null;
clientSecret = null;
refreshToken = null;
tokenServerUri = null;
transportFactoryClassName = null;
quotaProjectId = null;
}
/**
* Constructor with all parameters allowing custom transport and server URL.
*
Expand Down Expand Up @@ -340,6 +356,81 @@ public String getQuotaProjectId() {
return quotaProjectId;
}

/**
* Returns a Google ID Token from the Service Account Credentials API.
* Compute Engine default service account of the quotas project is used
* except if the environment variable 'SERVICE_ACCOUNT_APPLICATION_CREDENTIALS'
* is set. The user account must have the 'Service Account Token Creator' on the
* service account to be allowed to generate an id_token
*
* @param targetAudience the aud: field the IdToken should include.
* @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<Option> options) throws IOException {

if (targetAudience == null || targetAudience.equals("")) {
throw new IOException("TargetAudience can't be null or empty");
}

HttpRequestFactory requestFactory = new NetHttpTransport().createRequestFactory(new HttpCredentialsAdapter(this));

//First time, check if the instance attribute has been initialized or not
if (serviceAccountCredentialEmail == null){
serviceAccountCredentialEmail = getServiceAccountEmail(requestFactory);
}

return IamUtils.getIdToken(serviceAccountCredentialEmail,this, requestFactory.getTransport(),
targetAudience, true, Collections.EMPTY_MAP);
}

protected String getServiceAccountEmail(HttpRequestFactory requestFactory) throws IOException {
LOGGER.warning("ID Token generation with audience and based on user credential is not possible. \n" +
"A service account must be used. You can define it in the environment variable\n" +
SERVICE_ACCOUNT_APPLICATION_CREDENTIALS + "\nIf not set, default Compute Engine default " +
"service account will be used\nYou need to have the role 'Service Account Token Creator' " +
"on the service account.");

// Get the service account in the Environment Variables
String saEmail = getEnv(SERVICE_ACCOUNT_APPLICATION_CREDENTIALS);

// If missing, use the compute engine default service account by default
if (saEmail == null || saEmail.equals("")){
// If quotaProjectId is null, you can't determine the current project
if (quotaProjectId == null){
throw new IOException(
"QuotaProjectId can't be null to determine the default service account to use\n" +
"Use 'gcloud auth application-default set-quota-project' to set it");
}

// Inform the user that no defined service account is found. Use the Compute Engine Default service account
saEmail = getComputeEngineDefaultServiceAccountEmail(getQuotaProjectId(),requestFactory);
}
LOGGER.info("The service account with email '" + saEmail + "' is used");
return saEmail;
}

//For test purpose
String getEnv(String key){
return System.getenv(key);
}


protected String getComputeEngineDefaultServiceAccountEmail(String projectId, HttpRequestFactory requestFactory)
throws IOException {
String url = RESOURCE_MANAGER_API + "projects/" + projectId;

HttpRequest request = requestFactory.buildGetRequest(new GenericUrl(url));
request.setParser(new JsonObjectParser(JSON_FACTORY));
HttpResponse httpResponse = request.execute();
GenericData responseData = httpResponse.parseAs(GenericData.class);

return responseData.get("projectNumber").toString() + DEFAULT_COMPUTE_ENGINE_SERVICE_ACCOUNT_SUFFIX;
}

public static class Builder extends GoogleCredentials.Builder {

private String clientId;
Expand Down
Loading