I am using the service account model and Google's Java API to retrieve and modify users.
I am able to successfully create a GoogleCredential object using code similar to Google's example:
GoogleCredential googleCredential = new GoogleCredentialBuilder()
.setTransport(httpTransport)
.setJsonFactory(jsonFactory)
.setServiceAccountId(SERVICE_ACCOUNT_EMAIL)
.setServiceAccountUser(SERVICE_ACCOUNT_USER)
.setServiceAccountPrivateKeyFromP12File(P12_PRIVATE_KEY_FILE)
.setServiceAccountScopes(Collections.singleton(GLOBAL_USER_AND_ALIAS_SCOPE)
.build();
I see no mention in any examples that I have to explicitly create an access token, so I have been assuming that the above code takes care of that. Is that true?
After that, I successfully create an instance of Directory, then try to retrieve a specific user:
User user = new User();
user = directory.users().get(uid).execute();
That fails, throwing a NullPointerException.
When I inspect the GoogleCredential object right before the call to get the user object, it appears that it does not contain an access token:
accessToken = null
refreshToken = null
What am I missing?
How does one get the access token using the service account model?
Thanks in advance.
Where are you getting your accessToken? Try
credential.refreshToken();
accessToken = credential.getAccessToken();
Also, you should consider running your credentials in the Oauth2 Playground. If it works in the playground, then it's likely something wrong with your implementation.
Andy is correct. The examples for the Google Java API leave out this critical step. At runtime, the Google code throws a NullPointerException with no other details that would identify where it is occurring. Stepping through the debugger in Eclipse made it clear that the token was null in GoogleCredential.
Related
I'm attempting to read/write to Google Sheet (owned by me) via Google Sheets API v4. To get Credential object I'm using the following code:
private static Credential getCredentials(final NetHttpTransport HTTP_TRANSPORT) throws IOException {
// Load client secrets.
InputStream in = ResourceX.class.getResourceAsStream(CLIENT_SECRET_DIR);
GoogleClientSecrets clientSecrets = GoogleClientSecrets.load(JSON_FACTORY, new InputStreamReader(in));
System.out.println(clientSecrets.toPrettyString());
// When I print this ^^ it loads contents of secret key exactly as expected
// Build flow and trigger user authorization request.
Builder builder = new GoogleAuthorizationCodeFlow.Builder(HTTP_TRANSPORT, JSON_FACTORY, clientSecrets, SCOPES); // error occurs on this line
builder.setDataStoreFactory(new FileDataStoreFactory(new java.io.File(CREDENTIALS_FOLDER)));
GoogleAuthorizationCodeFlow flow = builder.setAccessType("offline").build();
return new AuthorizationCodeInstalledApp(flow, new LocalServerReceiver()).authorize("user");
}
I understand that IllegalArgumentException means that somehow the argument clientSecrets is illegal to be passed as parameter however when I print out the contents of json file (see print statement in code above) the contents seems to match the expected contents (I cannot print the contents on SO for security reasons but nothing was changed in the file since I downloaded it from Google).
I found a similar question here but there are no answers addressing the core question.
What possibly have I missed that may cause this exception?
Stacktrace:
Exception in thread "main" java.lang.IllegalArgumentException
at com.google.api.client.repackaged.com.google.common.base.Preconditions.checkArgument(Preconditions.java:111)
at com.google.api.client.util.Preconditions.checkArgument(Preconditions.java:37)
at com.google.api.client.googleapis.auth.oauth2.GoogleClientSecrets.getDetails(GoogleClientSecrets.java:82)
at com.google.api.client.googleapis.auth.oauth2.GoogleAuthorizationCodeFlow$Builder.<init>(GoogleAuthorizationCodeFlow.java:195)
at global_projects.GoogleSheetsAPI.getCredentials(GoogleSheetsAPI.java:100)
at global_projects.GoogleSheetsAPI.updateSheetByOauth2(GoogleSheetsAPI.java:116)
at amz.Main.main(Main.java:78)
Update:
I think I found the problem. clientSecrets expects a json file from OAuth 2.0 client ID not a Service account key/jsonfile (as described in this link). However, since I am accessing my own spreadsheet is there a way to access it using a service account key/json file or must I use OAuth which would require me to provide consent to edit my own spreadsheet?
There are 2 ways of accessing your Google sheets file using the Google Client API.
Create service account credentials for which you must give edit access in your particular sheet to the email which Google assigned for the account.
OR
Create Oauth2 client id credentials to use with generating a credential object. This method will prompt you, by way of return URL, to provide authorisation in browser. Once you approve, you can query for a refresh token that enables you to refresh your access token without needing the browser flow approval again.
You are using method (1) and you're getting the IllegalArgumenException error because you have not given access to the email assigned to the service account.
I'm currently working with Google's Games API. The client sends through a user's authorization code, alongside their GPlay ID.
I'm sending this off to validate with Google, with;
var tokenResponse =
new GoogleAuthorizationCodeTokenRequest(
new NetHttpTransport(),
JacksonFactory.getDefaultInstance(),
"https://www.googleapis.com/oauth2/v4/token",
client_id,
client_secret,
idTokenString,
"")
.execute()
Where the client_id and client_secret are retrieved from our client_secret as retrieved from Google, and the idTokenString is the authorization code as provided by the user logging in to the client (format: 4/xxxxx..).
After retrieving the tokenResponse, the following will return the access token without issue;
var accessToken = tokenResponse.getAccessToken()
However, the idToken as retrieved from:
var idToken = tokenResponse.getIdToken()
returns with null. As such, attempting to get the user's data to validate they're the legitimate owner of the account with;
var idToken = tokenResponse.parseIdToken()
will return a nullpointer exception.
From googling on the topic, some users seem to think that the parseIdToken method is no longer in use, and that only the accessToken can be used to retrieve such information.
However, any solutions I've found have all required use of the getIdToken, which is also returning with null.
Does anyone have any ideas on what I may be doing wrong here, or if there's another expected method for retrieving the user's details after login?
It might be because getIdToken() is in BETA.
As stated in the documentation - Google API Client Library for Java:
Features marked with #Beta at the class or method level are subject to change. They might be modified or removed in any major release. Do not use beta features if your code is a library itself (that is, if your code is used on the CLASSPATH of users outside your control).
You might want to use getIdToken from GoogleSignInAccount as a workaround.
You can check the release notes for any update.
Hope this helps.
I have an application in whick an user can login with google. Than the user during his experience into the application can choose to integrate also the GoogleCalendar service. In order to achieve this my actual system works like the follow:
-when the user log in with google the system store his access and refresh token and calls Google to retrive basic profile information. In order to do that I create a GoogleCalendar object using the following call:
GoogleTokenResponse response = flow.newTokenRequest(code).setRedirectUri(GOOGLE_CALLBACK_URI).execute();
GoogleCredential credential= new GoogleCredential.Builder().setJsonFactory(Constants.JSON_FACTORY)
.setTransport(Constants.HTTP_TRANSPORT).setClientSecrets(Constants.GOOGLE_CLIENT_ID, Constants.GOOGLE_CLIENT_SECRET).build();
credential.setAccessToken(response.getAccessToken());
credential.setRefreshToken(response.getRefreshToken());
-When the user choose to integrate the calendar service another Oauth iteration starts and the with a new code. I ask the GoogleCredential like before and access the service.
-When the systems need to perform some operations accessing Google Calendar I create a GoogleCredential object with the following call:
GoogleCredential c= new GoogleCredential.Builder().setJsonFactory(Constants.JSON_FACTORY)
.setTransport(Constants.HTTP_TRANSPORT).setClientSecrets(Constants.GOOGLE_CLIENT_ID, Constants.GOOGLE_CLIENT_SECRET).build()
.setAccessToken(user.getAccessToken()).setRefreshToken(user.getRefreshToken());
Ans then ask for the service. But in this last case I get an error reporting 403 code and "Insufficient Permission" message.
I have checked that the stored access and refresh token I set into the GoogleCredential are the same I receive the first time getting the GoogleCredential with the "code". I also search a lot online but no solution would work. I'm really blocked into this error. Thank you in advance for the help and sorry if I miss specifing something or if I make some mistake in asking the question.
If I understand the problem you're describing correctly, incremental authorization may be what you're looking for: https://developers.google.com/accounts/docs/OAuth2WebServer#incrementalAuth
Specifically, add include_granted_scopes=true to your authorization URLs which should cause the resulting tokens to include the sum of all so-far-granted authorizations.
I'm trying to fetch a user from Google Directory API with the following request:
Collection<String> SCOPES = Arrays.asList("https://www.googleapis.com/auth/admin.directory.user.readonly", "https://www.googleapis.com/auth/admin.directory.user");
URL url = getClass().getResource("privatekey.p12");
GoogleCredential credential = new GoogleCredential.Builder()
.setJsonFactory(request.getJsonFactory())
.setServiceAccountId("foobar.com")
.setServiceAccountScopes(SCOPES)
.setTransport(new NetHttpTransport())
.setServiceAccountUser("foo#bar.com")
.setServiceAccountPrivateKeyFromP12File(new File(url.getPath())).build();
Directory dir = new Directory.Builder(GoogleNetHttpTransport.newTrustedTransport(), request.getJsonFactory(), credential)
.setApplicationName(request.getApplicationName())
.build();
But I'm getting the following error from the API:
Caused by: com.google.api.client.auth.oauth2.TokenResponseException: 400 Bad Request
{
"error" : "access_denied",
"error_description" : "Requested scopes not allowed: https://www.googleapis.com/auth/admin.directory.user https://www.googleapis.com/auth/admin.directory.user.readonly"
}
I've checked the scopes are correct, anyone know what's the problem here?
Try using one scope or the other. If you only need read access, use the readonly scope. If you need read/write access to users, use the other.
Also, ensure that you've granted the service account's client_id access to these scopes in the Admin's control panel (admin.google.com) and that your ServiceAccountUser is a super admin in the domain.
Argh! I removed the old service account and created a new one. After that things started to roll. Guessing I had a wrong p12 file etc. Can finally breathe again, phew.
Directly from Google Developers Api for Java
GoogleCredential credential = new GoogleCredential.Builder().setTransport(HTTP_TRANSPORT)
.setJsonFactory(JSON_FACTORY)
.setServiceAccountId("[[INSERT SERVICE ACCOUNT EMAIL HERE]]")
.setServiceAccountScopes(PlusScopes.PLUS_ME)
.setServiceAccountPrivateKeyFromP12File(new File("key.p12"))
// .setServiceAccountUser("user#example.com")
.build();
// set up global Plus instance
plus = Plus.builder(HTTP_TRANSPORT, JSON_FACTORY)
.setApplicationName("Google-PlusServiceAccountSample/1.0")
.setHttpRequestInitializer(credential).build();
Now I wanted to know what are the different
ServiceAccountScopes
that we can use ??
Here, its using PlusScopes.PLUS_ME, some where it uses AnalyticsScopes.ANALYTICS_READONLY .
I really don't have any idea about these scopes.
Further, when i try to use *AnalyticsScopes.ANALYTICS_READONLY* in my analytics-cmline-sample project from here
It doesn't allow me to. Saying its accepting a List while I am providing a String.
Does anyone has any idea whats this ServiceAccountScopes is all about ?
The answer to your second question is that you should put the scope in a arraylist, like this:
GoogleAccountCredential.usingOAuth2(this,Arrays.asList(AnalyticsScopes.ANALYTICS_READONLY));