I want to use a Google API from a server. For this, I a reading the following documentation https://developers.google.com/identity/protocols/oauth2/service-account
I create a service account and I link it to the translation API.
Following the document, I create a JWT
{
"iss":"bonnefacture@appspot.gserviceaccount.com",
"scope":"https://translate.googleapis.com/",
"aud":"https://oauth2.googleapis.com/token",
"exp":1665515089,
"iat":1665511969
}
And I sign it with the private key of the service account.
I do a POST https://oauth2.googleapis.com/token grant_type: urn:ietf:params:oauth:grant-type:jwt-bearer assertion: MY_JWT_TOKEN
And I don't get an "access_token" as it is written in the documentation but only a id_token.
{
"id_token": "eyJhbGciOiJSUzI1Ni...eg"
}
When I check the JWT with the https://www.googleapis.com/oauth2/v3/tokeninfo?id_token= URL
https://www.googleapis.com/oauth2/v3/tokeninfo?id_token= service and all seams ok.
No access_token. Where I am wrong ?
I would like to help solve one of the worst mysteries about the Google API.
Given that I'm mainly interested in those servers (machine translation, storage, etc.) and therefore do not imply an iteration with a user to collect consent.
Following various tutorials on the official documentation I installed gcloud on my computer and tried to use the translation api.
I created the service account as requested and generated the related json file with all the authentication information.
The Google example run fine.
#!/bin/bash
export GOOGLE_APPLICATION_CREDENTIALS="myfile.json"
curl -X POST \
-H "Authorization: Bearer "$(gcloud auth application-default print-access-token) \
-H "Content-Type: application/json; charset=utf-8" \
-d @request.json \
"https://translation.googleapis.com/language/translate/v2"
When i tried to redo the thing in java it was a nightmare. I could not find a system to generate the authentication token, although I have read everything readable on the google site.
Finally thanks to mwik I found the solution; the mystery was finding the right SCOPE to use in the authentication call.
private File credFile;
private static final Set<String> SCOPES = ImmutableSet.of("https://www.googleapis.com/auth/cloud-platform");
private static final Logger logger = Logger.getLogger(GoogleTranslate2.class.getName());
/**
* Esegue l'autorizzazione in base al file di credenziali.
* @return il token di accesso da utilizzare in successive chiamate
* @throws Exception
*/
public AccessToken autorizza()
throws Exception
{
// lo stream viene chiuso da fromStream
GoogleCredentials credentials = GoogleCredentials.fromStream(new FileInputStream(credFile))
.createScoped(SCOPES);
credentials.refreshIfExpired();
// The typical way to use a GoogleCredentials instance is to call its getRequestMetadata(),
// and include the metadata in your request. Since we are accessing the token directly via
// getAccessToken(), we must first call getRequestMetadata() to ensure the token is available
// (refreshed if necessary).
logger.fine("METADATA: " + credentials.getRequestMetadata());
return credentials.getAccessToken();
}
This is the mystery best hidden in google's hundreds of pages of documentation.
The call to
gcloud auth application-default print-access-token
produce the same result of
System.out.println(accessToken.getTokenValue());
Finally I can use the api I want without including tons of useless packages for each single api in my project.
If the correct SCOPE is not specified, the call to the OAuth2 server returns an id_token which is not the one needed to call the API, ie an access_token.
For SCOPE you can use a generic
https://www.googleapis.com/auth/cloud-platform
All other data needed for authentication is already present in the json file.
I hope my experience can help other developers as well.