First, let me explain what I am trying to do, as this is a two part question.
I am building a JAX-RS service that internally authenticates with a Google account via
As for the 401, it seems some oddity with Google's API. I find the first time I run my app in a while, and then at seemingly random intervals hours spread apart, I always get a 401. On the second or third round it actually logs in.
The behavior is even accounted for in Google's Task API sample code here.
Notice this code:
void onRequestCompleted() {
received401 = false;
}
void handleGoogleException(IOException e) {
if (e instanceof GoogleJsonResponseException) {
GoogleJsonResponseException exception = (GoogleJsonResponseException) e;
if (exception.getStatusCode() == 401 && !received401) {
received401 = true;
accountManager.invalidateAuthToken(credential.getAccessToken());
credential.setAccessToken(null);
SharedPreferences.Editor editor2 = settings.edit();
editor2.remove(PREF_AUTH_TOKEN);
editor2.commit();
gotAccount();
return;
}
}
Log.e(TAG, e.getMessage(), e);
}
In Google's own code sample they have made provision for a first time 401. It might be some special security issue that needs to be handled in code.
Did you request permission for the proper scopes? You should always include https://www.googleapis.com/auth/userinfo.profile
I think you will need to specify the user that you are trying to impersonate.
The way you are using ServiceAccount right now is only valid to access data related to your application or to your service account itself. Since you wish to access Google Calendar data you must be a Google Apps domain administrator with privileges to access any of your domain user's data - let me know if I am not correct. Google Calendar data belong to users so you need to specify which user you are trying to impersonate.
To do this using Java, it seems that you need to do use GoogleCredential.Builder#setServiceAccountUser(String)
Please try:
GoogleCredential credential = new GoogleCredential.Builder().setTransport(HTTP_TRANSPORT)
.setJsonFactory(JSON_FACTORY)
.setServiceAccountId("284XXXXXXXX@developer.gserviceaccount.com")
.setServiceAccountScopes(CalendarScopes.CALENDAR)
.setServiceAccountPrivateKeyFromP12File(new File("/path/to/privatekey.p12"))
.setServiceAccountUser("user@domain.com")
.build();
Also, once you have created a Service Account key in your API project (from the APIs Console), you will have to add this project to the list of authorized third party app in the cPanel. More information about this can be found here. The "Client Id" you need to use is the one bound to the Service Account key and looks like <APP_ID>-<OTHER_KEY>.apps.googleusercontent.com
Such behaviour may occur if you did not add api user in admin panel (in your case Documents panel I guess), for instance in google analytics you have to add new user where email address is the same which you got from api (provided by the Google API Service Account, after creating private key). In analytics it looks like this:

I am an Android Mobile App Developer. I am making an application using Google Calendar API (OAuth 2.0). I also faced this same error. But now, I have resolved the error. I am writing the scenario and the solution for it.
The url of getting the Calendar Event for the particular calendar (Mobile App API):
https://www.googleapis.com/calendar/v3/calendars/en.indian%23holiday%40group.v.calendar.google.com/events?access_token=ya29.AHES6ZQyP3iDZM8PF-7ZqZE8oKmWAgUX55sPGPTjGbM5A
In this url, we have to add the calendarId. The format of the url is;
https://www.googleapis.com/calendar/v3/calendars/<calendarId>/events
I was not encoding the calendarId before adding to the url. I encoded the calendarId and then made the Http request. All went fine then.
final String accessToken=dataStore.getAccessToken();
List<NameValuePair> params = new LinkedList<NameValuePair>();
params.add(new BasicNameValuePair("access_token", accessToken));
String paramString = URLEncodedUtils.format(params, "utf-8");
final String url = String.format(AuthConstants.CALENDAR_EVENTS_URI,URLEncoder.encode(calendarId))+"?"+ paramString;
Now the calendar events are returned in the JSON.
OK, so I have been playing around with this and have got past the 401 error, but I am now hitting a 403. However, I will detail the code I have written that at least answers the 401 problem.
I did not change any of the service account settings or re-generate any of the keys etc, that is all the same.
What has change is some of the code, and I upgraded to v3r20lv1.12.0-beta of the Calendar API.
The code below, as detailed above in the OP, remains the same, i.e.:
private static final HttpTransport HTTP_TRANSPORT = new NetHttpTransport();
private static final JsonFactory JSON_FACTORY = new JacksonFactory();
.
.
.
GoogleCredential credential = new GoogleCredential.Builder().setTransport(HTTP_TRANSPORT)
.setJsonFactory(JSON_FACTORY)
.setServiceAccountId("284XXXXXXXX@developer.gserviceaccount.com")
.setServiceAccountScopes(CalendarScopes.CALENDAR)
.setServiceAccountPrivateKeyFromP12File(new File("D:/3cd8XXXXXXXXXXXXXXXXXXXXXXXXXXXXXd635e-privatekey.p12"))
.build();
The code to get a handle for the Calendar has changed, but this was forced by an API change:
Calendar calendar = new Calendar(HTTP_TRANSPORT, JSON_FACTORY, credential);
At this point, I can iterate through my calendar and see the events, by running the following code (this prints an event summary):
Events events = calendar.events().list(CALENDAR_ID).execute();
for (Event event : events.getItems())
{
System.out.println(event.getSummary());
}
But, if I try and update an event, I get a 403 Forbidden error. I will raise a separate thread for this, and will link the two threads together.
Thanks to all for your help!
EDIT: here is the new thread.