Permissions - Google Sheets API - java

I'm getting this error when Im trying to write data to my spreadsheet in Google sheets.
{"code": 403,"details": [{"#type": "type.googleapis.com/google.rpc.ErrorInfo","reason": "ACCESS_TOKEN_SCOPE_INSUFFICIENT"}],"errors": [{"domain": "global","message": "Insufficient Permission","reason": "insufficientPermissions"}],"message": "Request had insufficient authentication scopes.","status": "PERMISSION_DENIED"}
The Java code:
public static void updateData(String sheetName, String cellLocation, String newValue) throws Exception {
final NetHttpTransport HTTP_TRANSPORT = GoogleNetHttpTransport.newTrustedTransport();
Sheets service = new Sheets.Builder(HTTP_TRANSPORT, JSON_FACTORY, getCredentials(HTTP_TRANSPORT))
.setApplicationName(APPLICATION_NAME)
.build();
ValueRange body = new ValueRange()
.setValues(Arrays.asList(Arrays.asList(newValue)));
UpdateValuesResponse result =
service.spreadsheets().values().update(spreadsheetId, cellLocation, body)
.setValueInputOption("RAW")
.execute();
System.out.printf("%d cells updated.", result.getUpdatedCells());
}
Thank You!
I have tried to make my spreadsheet public.

Your program uses spreadsheets().values().update() which requires OAuth scopes.
This is the reason why the "Request had insufficient authentication scopes" error occurs. Refer to https://developers.google.com/sheets/api/reference/rest/v4/spreadsheets.values/update?hl=en for more details.
This link should help you in authorizing the application.
https://developers.google.com/sheets/api/quickstart/java?hl=en

Related

Insufficient Permission: Request had insufficient authentication scopes. Google Drive

i tries to add member to my google sheet using java code, but it doesn't work.
I use OAuthor 2.
Can anyone help me with this problem? Thanks very much.
My code:
public static void main(String... args) throws IOException, GeneralSecurityException {
// Build a new authorized API client service.
private static final JsonFactory JSON_FACTORY = JacksonFactory.getDefaultInstance();
final NetHttpTransport HTTP_TRANSPORT = GoogleNetHttpTransport.newTrustedTransport();
final String spreadsheetId = "10gLncj6bGmm-UcXP1vztsbv23CD85GNG95zjDgZ8HBA";
JsonBatchCallback<Permission> callback = new JsonBatchCallback<Permission>() {
#Override
public void onFailure(GoogleJsonError e,
HttpHeaders responseHeaders)
throws IOException {
// Handle error
System.err.println(e.getMessage());
}
#Override
public void onSuccess(Permission permission,
HttpHeaders responseHeaders)
throws IOException {
System.out.println("Permission ID: " + permission.getId());
}
};
Drive driveService = new Drive.Builder(HTTP_TRANSPORT, JSON_FACTORY, getCredentials(HTTP_TRANSPORT))
.setApplicationName(APPLICATION_NAME)
.build();
BatchRequest batch = driveService.batch();
Permission userPermission = new Permission()
.setType("user")
.setRole("reader")
.setEmailAddress("trilo10101990#gmail.com");
driveService.permissions().create(spreadsheetId, userPermission)
.setFields("id")
.queue(batch, callback);
batch.execute();
}
Out put :
Insufficient Permission: Request had insufficient authentication scopes.
Insufficient Permission: Request had insufficient authentication scopes.
Means that the user you have authenticated the application with has not granted your application enough scopes to access the data you are trying to access.
You appear to be using Permissions.create this method requires that the user be authorized with one of the following scopes
You didn't post all of your authorization code You probably have something like this.
GoogleAuthorizationCodeFlow flow = new GoogleAuthorizationCodeFlow
.Builder(httpTransport, JSON_FACTORY, clientSecrets,
DriveScopes.all()).setDataStoreFactory(dataStoreFactory)
.build();
The trick is to look for which scope you are requesting in the case of the code above it is requesting full access to google drive. You need to check your scope and ensure that you have requested full access.
After that you need to remove the users consent to your application and force it to request access of the user again. The user needs to see the consent screen in order to verify their consent.

How to implement Exponential Backoff for Google Gmail API?

I am currently using Gmail API to send emails on user's behalf. The Mails are sent one by one and the average size of recipients is 500.
I frequently see {
"code" : 500,
"errors" : [ {
"domain" : "global",
"message" : "Backend Error",
"reason" : "backendError"
} ],
"message" : "Backend Error"
}
as well as some occurrences of
{
"code" : 429,
"errors" : [ {
"domain" : "usageLimits",
"message" : "Rate Limit Exceeded",
"reason" : "rateLimitExceeded"
} ],
"message" : "Rate Limit Exceeded"
}
Google has suggested implementing Exponential backoff strategy to resolve these errors. I have implemented below solution, but it doesn't seem to work and is not helping with these errors.Here is my implementation;
public GoogleCredential createCredentialWithRefreshToken(String accessToken, String refreshToken)
{
GoogleCredential credential = new GoogleCredential.Builder().setTransport(new NetHttpTransport())
.setJsonFactory(new JacksonFactory())
.setClientSecrets(Constants.GOOGLE_CLIENT_ID, Constants.GOOGLE_CLIENT_SECRET)
.setRequestInitializer(setHttpTimeout())
.build();
credential.setAccessToken(accessToken).setRefreshToken(refreshToken);
return credential;
}
public HttpRequestInitializer setHttpTimeout() {
return new HttpRequestInitializer() {
#Override
public void initialize(HttpRequest httpRequest) throws IOException {
httpRequest.setUnsuccessfulResponseHandler(new HttpBackOffUnsuccessfulResponseHandler(backOff()));
httpRequest.setConnectTimeout(3 * 60000); // 3 minutes connect timeout
httpRequest.setReadTimeout(3 * 60000); // 3 minutes read timeout
}
private final ExponentialBackOff.Builder BACK_OFF = new ExponentialBackOff.Builder().setInitialIntervalMillis(500);
private BackOff backOff() {
return BACK_OFF.build();
}
};
}
public static Gmail getGmailServiceForGoogleAccount(GoogleAccount googleAcct){
Gmail gmailService = null;
GoogleCredential credential = new Utils().createCredentialWithRefreshToken(googleAcct.getAccess_token(),googleAcct.getRefresh_token());
gmailService = new Gmail.Builder(new NetHttpTransport(),
new JacksonFactory(), credential)
.setApplicationName("test")
.build();
return gmailService;
}
What is wrong with this implementation? Am i implementing the custom HttpRequestInitializer correctly.
Where could i set the log statements to find out if a request is being retried as per Exponential policy?
Please suggest
I see this is an old question, but will leave my answer here in case anyone finds it useful.
The problem with the code is that it is calling .setRequestInitializer() on the GoogleCredential.Builder, which sets the initializer for token requests and not the service API requests.
See the documentation here
Sets the HTTP request initializer for refresh token requests to the token server or null for none.
Instead the initializer should be configured on the Google service client and you can chain it with the Credential response handler to preserve its functionality too.
Something like this should work for the provided example:
public static HttpRequestInitializer requestInitializer(Credential credential) {
return new HttpRequestInitializer() {
#Override
public void initialize(HttpRequest httpRequest) throws IOException {
httpRequest.setConnectTimeout(3 * 60000); // 3 minutes connect timeout
httpRequest.setReadTimeout(3 * 60000); // 3 minutes read timeout
// chain response handler with the handler from the credential
// that handles retries for authentication errors
HttpUnsuccessfulResponseHandler responseHandler =
new HttpBackOffUnsuccessfulResponseHandler(backOff());
httpRequest.setUnsuccessfulResponseHandler((req, res, retry) ->
credential.handleResponse(req, res, retry)
|| responseHandler.handleResponse(req, res, retry));
}
private final ExponentialBackOff.Builder BACK_OFF = new ExponentialBackOff.Builder().setInitialIntervalMillis(500);
private BackOff backOff() {
return BACK_OFF.build();
}
};
}
public static Gmail getGmailServiceForGoogleAccount(GoogleAccount googleAcct){
GoogleCredential credential = new Utils().createCredentialWithRefreshToken(googleAcct.getAccess_token(),googleAcct.getRefresh_token());
return new Gmail.Builder(new NetHttpTransport(), new JacksonFactory(), requestInitializer(credential))
.setApplicationName("test")
.build();
}
Check the Exponential Backoff for Java implementation:
ExponentialBackOff backoff = new ExponentialBackOff.Builder()
.setInitialIntervalMillis(500)
.setMaxElapsedTimeMillis(900000)
.setMaxIntervalMillis(6000)
.setMultiplier(1.5)
.setRandomizationFactor(0.5)
.build();
request.setUnsuccessfulResponseHandler(new HttpBackOffUnsuccessfulResponseHandler(backoff));
Check this SO post for additional reference.

Google Reseller API getting GoogleJsonResponseException: 403 Forbidden

I'm working on a project that uses the Google Apps Reseller API (Found here).
I'm running into a 403 Forbidden Exception.
Code (most of it origins from the Google Codelab Example Here:
try {
try {
Reseller service = GoogleResellerApiUtil.getResellerService();
Customer customerRecord = service.customers().get("acme.com").execute(); //crashes here
// "acme.com" is also used in the example from Google
System.out.println(customerRecord.toString());
} catch (GoogleJsonResponseException e) {
e.printStackTrace();
}
And this is the class I use to connect to the API.
I've provided a p12 file and it uses the service account, when calling the API it is impersonating one of the super admins, so it should be allowed to make all the calls.
At the moment I'm only using the read-only scope.
public class GoogleResellerApiUtil {
/** HTTP_TRANSPORT */
private static final HttpTransport HTTP_TRANSPORT = new NetHttpTransport();
/** JSON Factory*/
private static final JsonFactory JSON_FACTORY = new JacksonFactory();
/** Service Account Email */
public static final String SERVICE_ACCOUNT_EMAIL = "****#appspot.gserviceaccount.com";
/** P12 File Location */
public static final String PRIVATE_KEY_FILE = "WEB-INF/key.p12";
/** Reseller Admin Account to impersonate */
public static final String RESELLER_ADMIN = "**.**#**.com";
/** Scopes */
public static final List<String> SCOPES = Arrays.asList(ResellerScopes.APPS_ORDER_READONLY);
/** Application name. */
private static final String APPLICATION_NAME = "**-subscription-portal";
/** Logger */
private final static Logger LOGGER =
Logger.getLogger(GoogleResellerApiUtil.class.getName());
public static GoogleCredential getCredentials() throws IOException {
GoogleCredential credentials = null;
try {
credentials = new GoogleCredential.Builder()
.setTransport(HTTP_TRANSPORT)
.setJsonFactory(JSON_FACTORY)
.setServiceAccountId(SERVICE_ACCOUNT_EMAIL)
.setServiceAccountScopes(SCOPES)
.setServiceAccountUser(RESELLER_ADMIN)
.setServiceAccountPrivateKeyFromP12File(new File(PRIVATE_KEY_FILE))
.build();
} catch (GeneralSecurityException e) {
e.printStackTrace();
}
System.out.println("credential has been build, returning credential "); //this gets printed, so I think the credentials are valid?
return credentials;
}
/**
* Build and return an authorized Reseller client service.
* #return an authorized Reseller client service
* #throws IOException
*/
public static Reseller getResellerService() throws Exception {
Credential credential = getCredentials();
return new Reseller.Builder(
HTTP_TRANSPORT, JSON_FACTORY, credential)
.setApplicationName(APPLICATION_NAME)
.build();
}
}
But I get the following error message when making the call:
com.google.api.client.googleapis.json.GoogleJsonResponseException: 403 OK
{
"code" : 403,
"errors" : [ {
"domain" : "global",
"message" : "Forbidden",
"reason" : "forbidden"
} ],
"message" : "Forbidden"
}
at com.google.api.client.googleapis.json.GoogleJsonResponseException.from(GoogleJsonResponseException.java:146)
etc. etc. etc.
It is noted in Reseller API: Manage Subscriptions that
Note: If the customerAuthToken is not valid or has expired, the API response returns a 403 "Forbidden" error.
To solve the issue, please make sure that requests must be authorized by an authenticated user who has access to the data. As also noted in Reseller API: Authorizing
Note: The user granting permission for the Reseller API must be a domain super administrator.
In addition to that, it was suggested in Token expiration that you write your code to anticipate the possibility that a granted token might no longer work. A token might stop working for one of these reasons:
The user has revoked access.
The token has not been used for six months.
The user changed passwords and the token contains Gmail scopes.
The user account has exceeded a certain number of token requests.
Hope that helps!

Access Not Configured for Android Calendar API

I'm trying to access the google calendar api using its java/android API (I followed the example from: http://samples.google-api-java-client.googlecode.com/hg/calendar-android-sample/instructions.html).
What I do is the following:
private static final List<String> SCOPES = Arrays.asList(CalendarScopes.CALENDAR,
CalendarScopes.CALENDAR_READONLY);
private HttpTransport httpTransport = AndroidHttp.newCompatibleTransport();
private JsonFactory jsonFactory = GsonFactory.getDefaultInstance();
private GoogleAccountCredential credential;
#Override
protected void onCreate(Bundle savedInstanceState) {
credential = GoogleAccountCredential.usingOAuth2(this, SCOPES);
user = (User) getIntent().getExtras().getSerializable(User.KEY);
credential.setSelectedAccountName(user.getEmail());
// user.getEmail() is the value I previously retrieved from the selected
// android.accounts.Account.name
Calendar cal = new Calendar.Builder(httpTransport, jsonFactory, credential)
.setApplicationName("TestApp/1.0")
.build();
}
When I later call want to retrieve the calendarlist by doing this:
String pageToken = null;
do {
CalendarList calendarList = cal.calendarList()
.list()
.setPageToken(pageToken)
.execute();
calendars.addAll(calendarList.getItems());
pageToken = calendarList.getNextPageToken();
} while (pageToken != null);
I get an error:
{
"code": 403,
"errors": [
{
"domain": "usageLimits",
"message": "Access Not Configured. The API is not enabled for your project, or there is a per-IP or per-Referer restriction configured on your API key and the request does not match these restrictions. Please use the Google Developers Console to update your configuration.",
"reason": "accessNotConfigured",
"extendedHelp": "https://console.developers.google.com"
}
],
"message": "Access Not Configured. The API is not enabled for your project, or there is a per-IP or per-Referer restriction configured on your API key and the request does not match these restrictions. Please use the Google Developers Console to update your configuration."
}
Can anyone help me to solve this problem? Thanks!
I found the error. I didn't use the full package name for the API-Key. After I made sure that the package name used in the API-Key is equal to the package name defined in the AndroidManifest.xml it worked.

(JAVA) Google API analytics - unable to obtain new "access token" using "refresh token"

I want to obtain a new "access token" based on the "refresh token" saved in database.
Here is the code I wrote:
GoogleCredential.Builder credentialBuilder = new GoogleCredential.Builder()
.setTransport(HTTP_TRANSPORT).setJsonFactory(JSON_FACTORY)
.setClientSecrets(CLIENT_ID, CLIENT_SECRET);
credentialBuilder.addRefreshListener(new MyCredentialRefreshListener());
credential = credentialBuilder.build();
credential.setRefreshToken("saved_refresh_token_from_database");
try {
credential.refreshToken();
} catch (IOException e) {
e.printStackTrace();
}
class MyCredentialRefreshListener implements CredentialRefreshListener {
public void onTokenResponse(Credential cr, TokenResponse tr) {
System.out.println("Credential was refreshed successfully.");
}
public void onTokenErrorResponse(Credential cr, TokenErrorResponse tr) {
System.out.println(tr);
}
}
I get this message:
com.google.api.client.auth.oauth2.TokenResponseException: 400 Bad
Request { "error" : "invalid_grant" }
I use the same CLIENT_ID, CLIENT_SECRET and "refresh token" in a php script and there I managed to get the new "access token" using "refresh token" when "access token" expired.
I wrote the code based on javadoc.google-oauth-java-client.
Any person here knows how to modify the code to obtain the new access token ?
Thanks in advance.
UPDATE: the problem was that I was saving in the database the refresh_token without doing a json_decode on it and it contained a "\" which is considered escaped character in JSON.
It looks like you may have found some out of date documentation. The JavaDoc you link is for version 1.8 of the client library. The current version is 1.12.
The client library authors recommend that you use GoogleAuthorizationCodeFlow to manage your OAuth credentials. It takes care of refreshes automatically. If you follow this path, the code will look something like this:
// Create the flow
AuthorizationCodeFlow authorizationCodeFlow = new GoogleAuthorizationCodeFlow.Builder(
new UrlFetchTransport(), new JacksonFactory(), CLIENT_ID, CLIENT_SECRET,
Collections.singleton(OAUTH_SCOPES))
.setAccessType("offline")
.setCredentialStore(new AppEngineCredentialStore())
.build();
// User Id: e.g. from session
Credential credential = authorizationCodeFlow.loadCredential(USER_ID);
// Make your API call. This example uses the Google+ API
// If the access token has expired, it will automatically refresh
Plus plus = new Plus(new UrlFetchTransport(), new JacksonFactory(), credential)
.activities().list("me", "public").execute();
if you get http status 400, and message "invalid_grant".
I think you should check your instance of HTTP_TRANSPORT, JSON_FACTORY, CLIENT_ID, CLIENT_SECRET.

Categories