I'm deploying a Java API that does user impersonation via Domain-widge Delegation to access the calendar of a user. For this I have created a service account, done the delegation and given it the right permissions and access to the users calendar.
While developing locally I've been using a downloaded key for the service account in the JSON format, and pointed to it with the GOOGLE_APPLICATION_CREDENTIALS environment variable. In my code, I create the credentials like this using the Java client library:
@Bean
@Qualifier("userCredentials")
public GoogleCredentials impersonateCalendarOwner() throws IOException {
final List<String> scopes = Collections.singletonList(CalendarScopes.CALENDAR);
return ((ServiceAccountCredentials) GoogleCredentials.getApplicationDefault())
.toBuilder()
.setServiceAccountUser(GOOGLE_CALENDARS_OWNER)
.build()
.createScoped(scopes);
}
This works fine locally, but when running in Cloud Run I get:
nested exception is java.lang.ClassCastException:
class com.google.auth.oauth2.ComputeEngineCredentials cannot be cast to
class com.google.auth.oauth2.ServiceAccountCredentials
After many hours of debugging I think I finally understand how getApplicationDefault
works. I think what happens locally is this:
And in Cloud Run it happens like this:
So now my questions remain:
My current idea is to use the ComputeEngineCredentials I get, to fetch the service account JSON key file from Secret Manager at service start up and then pass that file to ServiceAccountCredentials.fromStream but it feels like an extra step since the ComputeEngineCredentials I would be using are already the credentials of the same service account I would be fetching the key for.
The credentials provided by the instance metadata (Metadata server) does not include the service account private key which is required to sign requests.
Therefore the service account impersonation code that you are trying to use will not work. You will need to use an actual service account JSON that contains the private key.
My recommendation is to setup the default service account with a role to access Secret Manager. Store a service account JSON key file as data in Secret Manager. On your program startup fetch the service account JSON content and proceed with your impersonation code.