sheets api error access denied exception desp Requested client not authorized - google-api

Hi I am using Google apis.. I have successfully able to access google drive and google calendar but when i try to access google spreadsheet i am getting following exception.
Exception in thread "main" com.google.gdata.util.AuthenticationException: Failed to refresh access token: 403 Forbidden
{
"error" : "access_denied",
"error_description" : "Requested client not authorized."
}
Caused by: com.google.api.client.auth.oauth2.TokenResponseException: 403 Forbidden
{
"error" : "access_denied",
"error_description" : "Requested client not authorized."
}
My code is as follows
private static Credential authorize() throws Exception {
HttpTransport httpTransport = GoogleNetHttpTransport.newTrustedTransport();
JsonFactory jsonFactory = JacksonFactory.getDefaultInstance();
String SERVICE_ACCOUNT_EMAIL = "xxx#developer.gserviceaccount.com";
List<String> SCOPES = Arrays.asList("https://spreadsheets.google.com/feeds/");
GoogleCredential credential = new GoogleCredential.Builder().setTransport(httpTransport)
.setJsonFactory(jsonFactory).setServiceAccountId(SERVICE_ACCOUNT_EMAIL).setServiceAccountScopes(SCOPES)
.setServiceAccountPrivateKeyFromP12File(
new java.io.File("xxx.p12"))
.setServiceAccountUser("xx#zzz.com")
.build();
return credential;
}
public static void main(String[] args) throws Exception {
Credential credential = authorize();
SpreadsheetService service = new SpreadsheetService("new data service ");
service.setProtocolVersion(SpreadsheetService.Versions.V3);
service.setOAuth2Credentials(credential);
// Define the URL to request. This should never change.
URL SPREADSHEET_FEED_URL = new URL(
"https://spreadsheets.google.com/feeds/spreadsheets/private/full");
// Make a request to the API and get all spreadsheets.
SpreadsheetFeed feed = service.getFeed(SPREADSHEET_FEED_URL, SpreadsheetFeed.class);
List<SpreadsheetEntry> spreadsheets = feed.getEntries();
// Iterate through all of the spreadsheets returned
for (SpreadsheetEntry spreadsheet : spreadsheets) {
// Print the title of this spreadsheet to the screen
System.out.println(spreadsheet.getTitle().getPlainText());
}
}
Thanks

I was only passing the spreadsheet scope, I have to pass both drive and sheet scope. Now I am able to read spreadsheets from drive.. Here is the corrected code.
String SERVICE_ACCOUNT_EMAIL = "xxxx#developer.gserviceaccount.com";
ArrayList<String> scopes = new ArrayList<String>();
scopes.add(0, DriveScopes.DRIVE);
scopes.add(1, "https://spreadsheets.google.com/feeds");
GoogleCredential credential = new GoogleCredential.Builder().setTransport(httpTransport)
.setJsonFactory(jsonFactory).setServiceAccountId(SERVICE_ACCOUNT_EMAIL).setServiceAccountScopes(scopes)
.setServiceAccountPrivateKeyFromP12File(
new java.io.File("xx.p12"))
.setServiceAccountUser("yyy#example.com")
.build();

Related

Error Response while getting jwt access token for google user with Google Credential Object

I am trying to get the jwt access tokens for each user of my gsuite domain using the GoogleCredential and JacksonFactory libraries.
Code sample -
GoogleCredential credential = new GoogleCredential.Builder().setTransport(httpTransport)
.setJsonFactory(JSON_FACTORY)
.setServiceAccountId(clientEmail)
.setServiceAccountScopes(scopes)
.setServiceAccountPrivateKeyFromP12File(privateKeyFile)
.setServiceAccountUser(userEmail)
.build();
credential.refreshToken();
String accessToken = credential.getAccessToken();
All fields - clientEmail, scopes, key and userEmail are neither null nor empty
For a few number of users I am not able to get the access token and am getting this error
com.google.api.client.repackaged.com.google.common.base.Preconditions.checkNotNull(Preconditions.java:191) com.google.api.client.util.Preconditions.checkNotNull(Preconditions.java:127) com.google.api.client.json.jackson2.JacksonFactory.createJsonParser(JacksonFactory.java:96) com.google.api.client.json.JsonObjectParser.parseAndClose(JsonObjectParser.java:85) com.google.api.client.json.JsonObjectParser.parseAndClose(JsonObjectParser.java:81) com.google.api.client.auth.oauth2.TokenResponseException.from(TokenResponseException.java:88) com.google.api.client.auth.oauth2.TokenRequest.executeUnparsed(TokenRequest.java:287) com.google.api.client.auth.oauth2.TokenRequest.execute(TokenRequest.java:307) com.google.api.client.googleapis.auth.oauth2.GoogleCredential.executeRefreshToken(GoogleCredential.java:269) com.google.api.client.auth.oauth2.Credential.refreshToken(Credential.java:489) com.hubble.hubbleEngine.policyTypes.OAuth.getJWTAccessToken(OAuth.java:815)
This is happening only the first time, I am trying to get the access tokens. When I try to get the access tokens again for all users, I am able to get the access tokens for the users which were throwing the error the first time.
I debugged a bit and saw that the error gets generated from the following function present in com.google.api.client.auth.oauth2.TokenRequest
public final HttpResponse executeUnparsed() throws IOException {
// must set clientAuthentication as last execute interceptor in case it needs to sign request
HttpRequestFactory requestFactory =
transport.createRequestFactory(new HttpRequestInitializer() {
public void initialize(HttpRequest request) throws IOException {
if (requestInitializer != null) {
requestInitializer.initialize(request);
}
final HttpExecuteInterceptor interceptor = request.getInterceptor();
request.setInterceptor(new HttpExecuteInterceptor() {
public void intercept(HttpRequest request) throws IOException {
if (interceptor != null) {
interceptor.intercept(request);
}
if (clientAuthentication != null) {
clientAuthentication.intercept(request);
}
}
});
}
});
// make request
HttpRequest request =
requestFactory.buildPostRequest(tokenServerUrl, new UrlEncodedContent(this));
request.setParser(new JsonObjectParser(jsonFactory));
request.setThrowExceptionOnExecuteError(false);
HttpResponse response = request.execute();
if (response.isSuccessStatusCode()) {
return response;
}
throw TokenResponseException.from(jsonFactory, response);
}
The request.execute() hits the "https://accounts.google.com/o/oauth2/token" to get the token but it is throwing some error response. Due to this it throws the TokenResponseException mentioned at last. Here, the response.getContent() is null due to which the whole null exception is occuring.
Is there a way to know, which kind of error response is thrown by the call. (>300 or <200)? Or why such a case is happening ?

google drive Oauth 2.0 for java web application

My project is a java spark project that I have hosted on heroku. A part of the project requires that I download a particular file from my google drive into the application. I managed to set up everything when my application was running locally but as soon as I deployed the app it stopped working. This was regardless of the fact that when the app was running locally I used an Oauth2 client ID of type other and when I deployed the application I created a new one of type web application.
Below is a snippet of the authentication and download code:
public class AutoCalls {
/** Application name. */
private static final String APPLICATION_NAME = "calls-made";
private static final java.io.File DATA_STORE_DIR = new java.io.File(System.getProperty("target/classes/auth"), ".credentials/calls-made");
private static FileDataStoreFactory DATA_STORE_FACTORY;
private static final JsonFactory JSON_FACTORY = JacksonFactory.getDefaultInstance();
private static HttpTransport HTTP_TRANSPORT;
private static final List<String> SCOPES = Arrays.asList(DriveScopes.DRIVE);
static {
try {
HTTP_TRANSPORT = GoogleNetHttpTransport.newTrustedTransport();
DATA_STORE_FACTORY = new FileDataStoreFactory(DATA_STORE_DIR);
} catch (GeneralSecurityException | IOException t) {
System.exit(1);
}
}
public static void startDownload() throws IOException, ParseException {
Drive serv = getDriveService();
// drive stuff deleted
}
public static Credential authorize() throws IOException {
InputStream in = new FileInputStream("target/classes/auth/client_secret_*****************************************.apps.googleusercontent.com.json");
GoogleClientSecrets clientSecrets = GoogleClientSecrets.load(JSON_FACTORY, new InputStreamReader(in));
GoogleAuthorizationCodeFlow flow = new GoogleAuthorizationCodeFlow.Builder(HTTP_TRANSPORT, JSON_FACTORY, clientSecrets, SCOPES)
.setDataStoreFactory(DATA_STORE_FACTORY)
.setAccessType("offline")
.build();
Credential credential = new AuthorizationCodeInstalledApp(flow, new LocalServerReceiver()).authorize("user");
System.out.println("Credentials saved to " + DATA_STORE_DIR.getAbsolutePath());
return credential;
}
public static Drive getDriveService() throws IOException {
Credential credential = authorize();
return new Drive.Builder(HTTP_TRANSPORT, JSON_FACTORY, credential)
.setApplicationName(APPLICATION_NAME)
.build();
}
}
Currently when I try to initiate the download I get a URL in the Heroku logs a follows:
2017-02-20T10:32:28.656820+00:00 app[web.1]: Please open the following address in your browser:
2017-02-20T10:32:28.656908+00:00 app[web.1]: https://accounts.google.com/o/oauth2/auth?access_type=offline&client_id=************-vvdi7u6bmp1vc90sdidtnuiftdi1t49c.apps.googleusercontent.com&redirect_uri=http://localhost:48085/Callback&response_type=code&scope=https://www.googleapis.com/auth/drive
When I try open the URL in a browser I get an error:
Error: redirect_uri_mismatch
I cannot find any good step by step tutorial on accessing google drive contents on a java web application so any help would be arreiciated.
Generally, if OAuth works on server-A but not on server-B, it's because the redirect URL hasn't been configured correctly on the API Console. There is no reason why you would need to use a different client-ID because a single client-ID can be configured with multiple URLs, eg your local host and your heroku server. Another possibility is the use of https.
There is a separate problem in your code where you iterate on lista.isEmpty(). You should be iterating on nextPageToken != null. See the answer to Google drive rest API, Download files from root folder only

while accessing profile data from google plus getting following exception my code is below

Exception in thread "main"
com.google.api.client.googleapis.json.GoogleJsonResponseException: 404
Not Found Not Found at
com.google.api.client.googleapis.json.GoogleJsonResponseException.from(GoogleJsonResponseException.java:145)
at
com.google.api.client.googleapis.services.json.AbstractGoogleJsonClientRequest.newExceptionOnError(AbstractGoogleJsonClientRequest.java:113)
at
com.google.api.client.googleapis.services.json.AbstractGoogleJsonClientRequest.newExceptionOnError(AbstractGoogleJsonClientRequest.java:40)
at
com.google.api.client.googleapis.services.AbstractGoogleClientRequest$1.interceptResponse(AbstractGoogleClientRequest.java:312)
at
com.google.api.client.http.HttpRequest.execute(HttpRequest.java:1045)
at
com.google.api.client.googleapis.services.AbstractGoogleClientRequest.executeUnparsed(AbstractGoogleClientRequest.java:410)
at
com.google.api.client.googleapis.services.AbstractGoogleClientRequest.executeUnparsed(AbstractGoogleClientRequest.java:343)
at
com.google.api.client.googleapis.services.AbstractGoogleClientRequest.execute(AbstractGoogleClientRequest.java:460)
at googleplusdemo2.MyClass.main(MyClass.java:107)
public class MyClass {
// List the scopes your app requires:
private static List<String> SCOPE = Arrays.asList(
"https://www.googleapis.com/auth/plus.me",
"https://www.googleapis.com/auth/plus.profiles.read",
"https://www.googleapis.com/auth/plus.circles.write",
"https://www.googleapis.com/auth/plus.stream.write",
"https://www.googleapis.com/auth/plus.stream.read");
// The following redirect URI causes Google to return a code to the user's
// browser that they then manually provide to your app to complete the
// OAuth flow.
private static final String REDIRECT_URI = "http://some url";
static String CLIENT_ID="myclient id";
static String CLIENT_SECRET="myclient secret";
public static void main(String[] args) throws Exception {
// TODO Auto-generated method stub
GoogleAuthorizationCodeFlow flow = new GoogleAuthorizationCodeFlow.Builder(
new NetHttpTransport(),
new JacksonFactory(),
CLIENT_ID, // This comes from your Developers Console project
CLIENT_SECRET, // This, as well
SCOPE)
.setApprovalPrompt("force")
.setAccessType("offline").build();
String url = flow.newAuthorizationUrl().setRedirectUri(REDIRECT_URI).build();
System.out.println("Please open the following URL in your browser then " +
"type the authorization code:");
System.out.println(" " + url);
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
String code = br.readLine();
// End of command line prompt for the authorization code.
GoogleTokenResponse tokenResponse = flow.newTokenRequest(code)
.setRedirectUri(REDIRECT_URI).execute();
GoogleCredential credential = new GoogleCredential.Builder()
.setTransport(new NetHttpTransport())
.setJsonFactory(new JacksonFactory())
.setClientSecrets(CLIENT_ID, CLIENT_SECRET)
.addRefreshListener(new CredentialRefreshListener() {
#Override
public void onTokenResponse(Credential credential, TokenResponse tokenResponse) {
// Handle success.
System.out.println("Credential was refreshed successfully.");
}
#Override
public void onTokenErrorResponse(Credential credential,
TokenErrorResponse tokenErrorResponse) {
// Handle error.
System.err.println("Credential was not refreshed successfully. "
+ "Redirect to error page or login screen.");
}
})
.build();
// Set authorized credentials.
credential.setFromTokenResponse(tokenResponse);
credential.refreshToken();
// Create a new authorized API client
PlusDomains plusDomains = new PlusDomains.Builder(new NetHttpTransport(), new JacksonFactory(), credential).setApplicationName("TESTMK3").build();
Person mePerson = plusDomains.people().get("me").execute();
mePerson.getGender();
System.out.println("ID:\t" + mePerson.getId());
System.out.println("Display Name:\t" + mePerson.getDisplayName());
System.out.println("Image URL:\t" + mePerson.getImage().getUrl());
System.out.println("Profile URL:\t" + mePerson.getUrl());
}
}

Accessing Google Drive API using service account

I'm trying to access Google Drive API using a service account. I had set up the service account to access Google Directory API and I assume I can use the same for this. Here is my code, it's pretty much self-explanatory.
private static List<File> retrieveAllFiles(Drive service) throws IOException {
List<File> result = new ArrayList<File>();
Files.List request = service.files().list();
do {
FileList files = request.execute();
result.addAll(files.getItems());
request.setPageToken(files.getNextPageToken());
} while (request.getPageToken() != null &&
request.getPageToken().length() > 0);
return result;
}
public static Drive getDriveService(){
GoogleDriectoryService GDservice = new GoogleDriectoryService();
java.io.File serviceAccountP12Certificate = GDservice.loadF("resources/APIProject-62d885cdf671.p12");
HttpTransport httpTransport = new NetHttpTransport();//?
JacksonFactory jsonFactory = new JacksonFactory();//?
GoogleCredential credential = new GoogleCredential.Builder()
.setTransport(httpTransport)
.setJsonFactory(jsonFactory)
.setServiceAccountId(serviceAccountEmailAddress)
.setServiceAccountScopes(Arrays.asList(DriveScopes.DRIVE))
.setServiceAccountUser(serviceAccountUser)
.setServiceAccountPrivateKeyFromP12File(serviceAccountP12Certificate)
.build();
Drive service = new Drive.Builder(httpTransport, jsonFactory, credential)
.setApplicationName("radiantlogic-vds").build();
return service;
}
public static void main(String[] args){
Drive service = getDriveService();
List<File> files = retrieveAllFiles(service);
System.out.println(files);
}
But, I'm getting "com.google.api.client.auth.oauth2.TokenResponseException: 401 Unauthorized" at request.execute() line. Any help is appreciated.

How do I control Token Response Expiry time for google API access

I have problem extending the standard one hour for validity of google access token.
One part of my code is getting authorization from the user, using the GoogleAuthorizationCodeFlow as per Google recommendation. This works fine and gives me a TokenResponse that I persist to be used in an other part of the application where the user is not connected.
As per Google documentation, I thought that the "offline" access type in the flow would enable the TokenResponse to be usable as longer as the user doesnt revoke it. But apparently when I use this TokenReponse just after the user authorization, it works fine but when I use it after more than one hour, I get an "invalid credentials" sent back by Google.
Here is the code which creates the TokenResponse once the user has authorized it :
private HttpTransport HTTP_TRANSPORT;
private JacksonFactory JSON_FACTORY = JacksonFactory.getDefaultInstance();
private static GoogleAuthorizationCodeFlow flow;
#PostConstruct
public void init() {
try {
HTTP_TRANSPORT = GoogleNetHttpTransport.newTrustedTransport();
} catch (GeneralSecurityException | IOException e) {
logger.info(String.format("Raised Exception while getting GoogleNetHttpTransport : %s", e.getMessage()));
e.printStackTrace();
}
flow = new GoogleAuthorizationCodeFlow.Builder(HTTP_TRANSPORT, JSON_FACTORY, APP_ID, APP_SECRET,
Collections.singleton(CalendarScopes.CALENDAR_READONLY)).setAccessType("offline").build();
}
#RequestMapping(value = Uris.GOOGLERD)
public ModelAndView googleCallBack(HttpServletRequest request, #RequestParam(value = "state", required = false) String state,
#RequestParam(value = "code", required = false) String code,
#RequestParam(value = "error", required = false) String error, Model model) {
DynSubscriber dynSubscriber = (DynSubscriber) request.getSession().getAttribute("dynSubscriber");
ModelAndView toReturn = new ModelAndView("confirmation");
toReturn.addObject("buttonLabel", "Accueil");
try {
AuthorizationCodeTokenRequest tokenRequest = flow.newTokenRequest(code);
TokenResponse tr = tokenRequest.setRedirectUri(request.getRequestURL().toString()).execute();
// Json Conversion of Token Response for future use
StringWriter jsonTrWriter = new StringWriter();
JsonGenerator generator = JSON_FACTORY.createJsonGenerator(jsonTrWriter);
generator.serialize(tr);
generator.flush();
generator.close();
//Persists google access info
dynSubOp.setSPConnexionInfo(dynSubscriber, jsonTrWriter.toString(), DynServiceProviderType.GOOGLECAL);
toReturn.addObject("message","Agenda Google autorisé");
} catch (IOException | DynServicesException e) {
logger.error(String.format("Exception raised in googleCallBack for subscriber %s : %s", dynSubscriber.buildFullName(), e.getMessage()),e);
toReturn.addObject("message", "Problème lors du processus d'autorisation google");
}
return toReturn;
}
}
And here is the offline code which uses this TokenReponse :
private com.google.api.services.calendar.Calendar calendarConnection;
public DynGoogleCalendarRetriever(String subid, String connectionInformation)
throws CalendarConnectionNotAuthorizedException {
TokenResponse tr;
try {
HTTP_TRANSPORT = GoogleNetHttpTransport.newTrustedTransport();
tr = JSON_FACTORY.fromString(connectionInformation, TokenResponse.class);
Credential c = new GoogleCredential().setFromTokenResponse(tr);
calendarConnection = new com.google.api.services.calendar.Calendar.Builder(HTTP_TRANSPORT, JSON_FACTORY, c)
.build();
} catch (IOException | GeneralSecurityException e) {
logger.error(String.format("Failure creating the credentials for subscriber id %s", subid), e);
throw new CalendarConnectionNotAuthorizedException(String.format(
"Failure creating the credentials for subscriber id %s", subid), e);
}
}
Looks like this was already answered in this other SO question.
To get the refresh token that enables what I want, I need to build the flow with a approval_prompt=force parameter (builder.setApprovalPrompt("force"))
As per the comment, this requires the offline access which is done in the flow initialization.
A complement however : the offline code in my question doesn't work as such although I copied and pasted it from google documentation (probably an older version). The Credential needs to use its Builder object.
Here is the fully functionnal offline code :
TokenResponse tr;
try {
HTTP_TRANSPORT = GoogleNetHttpTransport.newTrustedTransport();
tr = JSON_FACTORY.fromString(connectionInformation, TokenResponse.class);
Credential c = new GoogleCredential.Builder().setTransport(HTTP_TRANSPORT).setJsonFactory(JSON_FACTORY)
.setClientSecrets(APP_ID, APP_SECRET).build().setFromTokenResponse(tr);
calendarConnection = new com.google.api.services.calendar.Calendar.Builder(HTTP_TRANSPORT, JSON_FACTORY, c)
.build();
} catch (IOException | GeneralSecurityException e) {
logger.error(String.format("Failure creating the credentials for subscriber id %s", subid), e);
throw new CalendarConnectionNotAuthorizedException(String.format(
"Failure creating the credentials for subscriber id %s", subid), e);
}

Resources