GCP service accounts are authentication and authorization units that enable you to communicate with Google APIs without the need for end users. Authentication includes knowledge of who a client is and authorization is what it can do.
In an application, service accounts are typically used when in need of access to app specific data, exluding the users but includes servers.
For example, you want to write an application that reads and writes files using Google Cloud Storage. Also, you want your files in GCS to be accessible only by your application, rather than a public access. Create a service account with the necessary roles on the GCS, add the key file of this service account to your server, and you'll be able to do read/write operations through the servers (without the need for the user). Also you would have lost the ache of hiding API keys in the client-side.
{
"headers": {
"normalizedNames": {},
"lazyUpdate": null
},
"status": 401,
"statusText": "Unauthorized",
"url": "urlToService",
"ok": false,
"name": "HttpErrorResponse",
"message": "Http failure response for urlToService: 401 Unauthorized",
"error": {
"error": {
"errors": [
{
"domain": "global",
"reason": "required",
"message": "Anonymous caller does not have storage.objects.create access to bucket bucketName.",
"locationType": "header",
"location": "Authorization"
}
],
"code": 401,
"message": "Anonymous caller does not have storage.objects.create access to bucket bucketName."
}
}
}
User-Managed Service Accounts
User-defined service accounts, in addition to service accounts that Google automatically defines. Generally, these types of service accounts are used in environments that security is not an issue (for example, in a local or corporate environment) or on cloud servers different from Google's.
[SERVICE_ACCOUNT_NAME]@[PROJECT_ID].iam.gserviceaccount.com
Using the Google Cloud Platform Console, it's possible to create a service account in just a few steps.
- Using the menu, navigate to the service accounts page.
- Click on Create Service Account button.
- On the dialog box
- Enter a descriptive name for the service account.
- Choose the roles you want that service account to execute jobs with.
- To have a key file, click on Furnish a new private key checkbox.
- Choose a file format (p12 or json) for your private key file.
- Click on the create button.
- Save the key file, you'll be using it to authorize the Google API calls.
It is possible to process the key file using the Google Api Client libraries. In the following examples java language is used with Google API Java Client library.
Credential credential = GoogleCredential.fromStream(keyFileInputStream).createScoped(Collections.singleton("https://www.googleapis.com/auth/devstorage.read_write"), httpTransport, jsonFactory);
storage = new Storage.Builder(httpTransport, jsonFactory, credential).build();
The same can be done using a key file in p12 format and the GoogleCredential.Builder method.
credential = new GoogleCredential.Builder()
.setTransport(httpTransport)
.setJsonFactory(jsonFactory)
.setServiceAccountId(serviceAccountId)
.setServiceAccountPrivateKeyFromP12File(p12File)
.setServiceAccountScopes(scopes)
.addRefreshListener(refreshListener)
.build();
storage = new Storage.Builder(httpTransport, jsonFactory, credential).build();
The setServiceAccountUser method can be used when you want to impersonate a user. This method can be used to access a system where a common data set is kept and an account is required.
credential = new GoogleCredential.Builder()
.setTransport(httpTransport)
.setJsonFactory(jsonFactory)
.setServiceAccountId(serviceAccountId)
.setServiceAccountPrivateKeyFromP12File(p12File)
.setServiceAccountScopes(scopes)
.setServiceAccountUser("[email protected]")
.build();
Google-Managed Service Accounts
Service accounts created automatically by Google and assigned to projects. Each of these accounts represents different Google services, and each has access to a certain level of Google Cloud Platform projects.
If your application is running on Compute Engine, Kubernetes Engine, App Engine flexible environment, or Cloud Functions you don't need to create an service account explicity. You can just use the assigned default compute engine service account.
[PROJECT_NUMBER][email protected]
GoogleCredential credential = GoogleCredential.getApplicationDefault().createScoped(gcsScopes);
storage = new Storage.Builder(httpTransport, jsonFactory, credential).build();
If you are working on Google App Engine standard environment, it is possible to access the credentials information using the App Engine App Identity API.
AppIdentityService appIdentity = AppIdentityServiceFactory.getAppIdentityService();
AppIdentityService.GetAccessTokenResult accessToken = appIdentity.getAccessToken(scopes);
String accessToken = accessToken.getAccessToken();
Application Default Credentials
The default credential that is fetched by GoogleCredential.GetApplicationDefault() first checks whether the GOOGLE_APPLICATION_CREDENTIALS environment variable is set. If it is, it uses the file referenced by its value. If not, it tries to fetch the compute engine default service account on Compute Engine, Kubernetes Engine, App Engine flexible environment, or Cloud Functions systems. If identity information is not available in both cases, error occurs.
To Sum Up,
- 1. If GOOGLE_APPLICATION_CREDENTIALS environment variable is present, the value it points to is used as the key file.
- 2. If system provides a default compute engine service account, that is used.
- 3. If there is no identity information found in both cases, error occurs.
If you are working in Google App Engine and want to use a manually created user-managed service account instead of the default App Engine service account, you need to define the key file path in appengine-web.xml along with specifying it as a resource as you should in all the files you want to access through your code. Otherwise, App Engine won't be able to find the file.
java.io.IOException: Error reading credential file from environment variable GOOGLE_APPLICATION_CREDENTIALS,
value 'private_key.json': File does not exist. at
com.google.api.client.googleapis.auth.oauth2.DefaultCredentialProvider.runningUsingEnvironmentVariable(DefaultCredentialProvider.java:199) at
com.google.api.client.googleapis.auth.oauth2.DefaultCredentialProvider.detectEnvironment(DefaultCredentialProvider.java:166) at
com.google.api.client.googleapis.auth.oauth2.DefaultCredentialProvider.getDefaultCredentialUnsynchronized(DefaultCredentialProvider.java:110) at
com.google.api.client.googleapis.auth.oauth2.DefaultCredentialProvider.getDefaultCredential(DefaultCredentialProvider.java:91) at
com.google.api.client.googleapis.auth.oauth2.GoogleCredential.getApplicationDefault(GoogleCredential.java:213)
<env-var name="GOOGLE_APPLICATION_CREDENTIALS" value="private_key.json" />
<resource-files>
<include path="/**.json" />
</resource-files>