Google Cloud Storage JSON API Service account 403 Forbidden - java

I am trying to access Google Cloud Storage buckets objects with Java JSON API.
We are uses service account authentication. We also check our permission of service account ID as "Editor".
When we tried to list out bucket Object we got following error.
403 Forbidden
{
"code" : 403,
"errors" : [ {
"domain" : "global",
"message" : "Forbidden",
"reason" : "forbidden"
} ],
"message" : "Forbidden"
}
Authentication Code
HttpTransport transport = GoogleNetHttpTransport.newTrustedTransport();
JsonFactory jsonFactory = new JacksonFactory();
GoogleCredential credential = null;
InputStream credentialsStream = null;
try {
credentialsStream = new FileInputStream("<Location of Service Account JSON File>");
credential =GoogleCredential.fromStream(credentialsStream, transport, jsonFactory);
} catch (IOException e) {
} finally {
if (credentialsStream != null) {
credentialsStream.close();
}
}
if (credential.createScopedRequired()) {
Collection<String> scopes = StorageScopes.all();
credential = credential.createScoped(scopes);
}
return new Storage.Builder(transport, jsonFactory, credential)
.setApplicationName("DemoApplication")
.build();
Bucket Object Code
Storage client = StorageFactory.getService();
Storage.Objects.List listRequest = client.objects().list(bucketName);
List<StorageObject> results = new ArrayList<StorageObject>();
Objects objects;
// Iterate through each page of results, and add them to our results list.
do {
objects = listRequest.execute();
// Add the items in this page of results to the list we'll return.
results.addAll(objects.getItems());
// Get the next page, in the next iteration of this loop.
listRequest.setPageToken(objects.getNextPageToken());
} while (null != objects.getNextPageToken());
Please help me out for this issue.

Related

create a Credential with access_token/refresh_token/expires_in parameters without the flow/installApp stuff

The quick start sample project is loading the *.json credential file, do the authorization flow, save the tokens in another file, and then use the token to create a credential. My goal is to do them in separate steps:
store the tokens in a DB after the exchange the authorization code for tokens
retrieve the tokens from DB
check the credential expiry and refresh token if needed and save it to DB again
create a credential object (without doing the flow/authorizationInstalledApp stuff)
So I tried the following to create the credential with the token related pramameters:
TokenResponse tokens = new TokenResponse();
String token = "xxxxxxxxxxxxxxx$$$$$$$$$";
tokens.setAccessToken(token);
tokens.setTokenType("bearer");
tokens.setExpiresInSeconds(3500L);
Credential credential = new
Credential(BearerToken.authorizationHeaderAccessMethod()).setFromTokenResponse(tokens);
The credential was created but the API call failed with:
401 Unauthorized { "code" : 401, "errors" : [ {
"domain" : "global",
"location" : "Authorization",
"locationType" : "header",
"message" : "Invalid Credentials",
"reason" : "authError" } ], "message" : "Invalid Credentials" }
I also tried another suggestion in the java doc:
public static Credential createCredentialWithRefreshToken(
HttpTransport transport, JsonFactory jsonFactory, TokenResponse tokenResponse) {
return new Credential.Builder(BearerToken.authorizationHeaderAccessMethod()).setTransport(
transport)
.setJsonFactory(jsonFactory)
.setTokenServerUrl(
new GenericUrl("https://server.example.com/token"))
.setClientAuthentication(new BasicAuthentication("s6BhdRkqt3", "7Fjfp0ZBr1KtDRbnfVdmIw"))
.build()
.setFromTokenResponse(tokenResponse);
}
That also did not work for me. So my questions are:
can we just create a very simple credential object with access_token, refresh_token, expires?
why do we have to hook up with the GoogleAuthorizationCodeFlow?
what's the minimum required parameters for a valid credential object based on Java client library (OAuth 2.0).
Thanks a lot for any ideas/input.

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.

AuthError On YouTube Search List Execute

I am exploring YouTube API and creating a java application which can search with authorized users token. I do Authorization as follows.
private static Credential authorize(String storeDirectory) throws Exception {
// load client secrets
YT.DATA_STORE_DIR = new java.io.File(".",".store/"+storeDirectory);
YT.dataStoreFactory = new FileDataStoreFactory(YT.DATA_STORE_DIR);
GoogleClientSecrets clientSecrets = GoogleClientSecrets.load(YT.JSON_FACTORY,
new InputStreamReader(new ByteArrayInputStream(YT.CLIENT_SECRET_JSON.getBytes(StandardCharsets.UTF_8))));
if (clientSecrets.getDetails().getClientId().startsWith("Enter")
|| clientSecrets.getDetails().getClientSecret().startsWith("Enter ")) {
System.out.println(
"Enter Client ID and Secret from https://code.google.com/apis/console/?api=plus "
+ "into client string.");
System.exit(1);
}
String[] permissoins = { PlusScopes.PLUS_LOGIN, PlusScopes.PLUS_ME, PlusScopes.USERINFO_EMAIL, PlusScopes.USERINFO_PROFILE, "https://www.googleapis.com/auth/youtube" };
// set up authorization code flow
GoogleAuthorizationCodeFlow flow = new GoogleAuthorizationCodeFlow.Builder(
YT.httpTransport, YT.JSON_FACTORY, clientSecrets,
new ArrayList(Arrays.asList(permissoins))).setDataStoreFactory(
YT.dataStoreFactory).build();
// authorize
return new AuthorizationCodeInstalledApp(flow, new LocalServerReceiver()).authorize("user");
}
I am trying to search a single keyword as bellow
Credential credential = authorize("username");
YouTube.Search.List search = youtube.search().list("id,snippet");
// I have set OauthToken here.
search.setOauthToken(credential.getAccessToken());
search.setMaxResults(50L);
search.setQ("Search keyword");
search.setType("video");
search.setFields("items(id/kind,id/videoId,snippet/title,snippet/thumbnails/default/url)");
// It get exception below line.
SearchListResponse searchResponse = search.execute();
List<SearchResult> searchResultList = searchResponse.getItems();
I am getting exception details as below.
ex = (com.google.api.client.googleapis.json.GoogleJsonResponseException) com.google.api.client.googleapis.json.GoogleJsonResponseException: 401 Unauthorized
{
"code" : 401,
"errors" : [ {
"domain" : "global",
"location" : "Authorization",
"locationType" : "header",
"message" : "Invalid Credentials",
"reason" : "authError"
} ],
"message" : "Invalid Credentials"
}
I am wondering i am missing to set any api key or I do something else wrong.
Do i need to use
search.setKey("YOUTUBE_DEVELOPER_KEY");
I am also confused what to use as YOUTUBE_DEVELOPER_KEY ?

Google endpoint: Access auth api from native client

I try to access a google endpoint service from a native application with OAuth 2.0. I managed to authenticate with GoogleAuthorizationCodeFlow and the JavaFX webview (as browser).
After a successfull authentication I try to access the api method but the User object is always null and the question is why?
Code for api method call:
GoogleAuthorizationCodeFlow flow = getGoogleAuthorizationCodeFlow();
Credential credential = flow.loadCredential(USER_ID);
Helloworld.Builder builder = new Helloworld.Builder(new NetHttpTransport(),
new JacksonFactory(), credential);
Helloworld service = builder.build();
Helloworld.Greetings.Authed protectedApiMethod = service.
greetings().authed();
HelloGreeting execute = protectedApiMethod.execute();
System.out.println("Response " + execute.getMessage());
Code for creating the flow object:
private static GoogleAuthorizationCodeFlow getGoogleAuthorizationCodeFlow() {
return new GoogleAuthorizationCodeFlow(new NetHttpTransport(),
new JacksonFactory(), INSTALLED_ID, CLIENT_SECRET, Arrays.asList(SCOPE_EMAIL));
}
Code where I try to authenticate:
GoogleAuthorizationCodeFlow flow = getGoogleAuthorizationCodeFlow();
GoogleAuthorizationCodeTokenRequest tokenRequest = flow.newTokenRequest(code);
tokenRequest.setRedirectUri(REDIRECT_URL);
try {
GoogleTokenResponse execute = tokenRequest.execute();
flow.createAndStoreCredential(execute, USER_ID);
Platform.exit();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
Declaration of the Api method:
#ApiMethod(name = "greetings.authed",
path = "greeting/authed",
clientIds = {Constants.WEB_CLIENT_ID, Constants.INSTALLED_ID,
Constants.API_EXPLORER_CLIENT_ID})
public HelloGreeting authedGreeting(User user) {
if (user != null) {
HelloGreeting response = new HelloGreeting("hello " + user.getEmail());
return response;
} else {
HelloGreeting response = new HelloGreeting("no user object was specified");
return response;
}
}
The only response I get is "no user object was specified". Since I can call the method without any error I guess I'm authenticated correctly.
From the docs: https://developers.google.com/appengine/docs/java/endpoints/getstarted/backend/auth
If the request coming in from the client has a valid auth token or is
in the list of authorized clientIDs, the backend framework supplies a
valid User to the parameter. If the incoming request does not have a
valid auth token or if the client is not on the clientIDs whitelist,
the framework sets User to null
So, you have to manully catch the case, where a null user is supplied by the infrastructure. So to answer the above question: The request is invalid. And the mistake in the code is, that the CodeFlow object is recreated for the actual request but since no CredentialStore is set, the token is lost and cannot be resend.

Cannot get data from Google Analytics (401 Unauthorized)

I try to implement basic data feed example and all I get is just:
com.google.api.client.googleapis.json.GoogleJsonResponseException: 401 Unauthorized.
But when I use http://code.google.com/apis/analytics/docs/gdata/gdataExplorer.html
It works fine with my Google Analytics account.
It seems I've done everything according to OAuth2 instructions: created client id and client secret, enabled Analytcs API.
Also I've tried to get my profiles and accounts with Analytics Management API but I get the same error. What do I do wrong?
Here is my code (of course I run this code with actual CLIENT_ID, CLIENT_SECRET and TABLE_ID):
public class AnalyticsTest {
private static final String CLIENT_ID = "MY_CLIENT_ID";
private static final String CLIENT_SECRET = "MY_CLIENT_SECRET";
private static final String REDIRECT_URL = "urn:ietf:wg:oauth:2.0:oob";
private static final String APPLICATION_NAME = "test";
private static final String SCOPE = "https://www.googleapis.com/auth/analytics";
// TRIED AS WELL private static final String SCOPE = "https://www.google.com/analytics/feeds";
private static final String TABLE_ID = "MY_TABLE_ID";
public static void main(String[] args) throws IOException {
NetHttpTransport netHttpTransport = new NetHttpTransport();
JacksonFactory jacksonFactory = new JacksonFactory();
// Generate the URL to send the user to grant access.
String authorizationUrl = new GoogleAuthorizationRequestUrl(CLIENT_ID, REDIRECT_URL, SCOPE).build();
// Direct user to the authorization URI.
System.out.println("Go to the following link in your browser:");
System.out.println(authorizationUrl);
// Get authorization code from user.
BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
System.out.println("What is the authorization code?");
String authorizationCode = null;
authorizationCode = in.readLine();
// Use the authorization code to get an access token and a refresh
// token.
AccessTokenResponse response = null;
try {
response = new GoogleAccessTokenRequest.GoogleAuthorizationCodeGrant(netHttpTransport, jacksonFactory, CLIENT_ID, CLIENT_SECRET, authorizationCode, REDIRECT_URL).execute();
} catch (IOException ioe) {
ioe.printStackTrace();
}
// Use the access and refresh tokens to get a new
// GoogleAccessProtectedResource.
GoogleAccessProtectedResource googleAccessProtectedResource = new GoogleAccessProtectedResource(response.accessToken, netHttpTransport, jacksonFactory, CLIENT_ID, CLIENT_SECRET, response.refreshToken);
Analytics analytics = Analytics
.builder(netHttpTransport, jacksonFactory)
.setHttpRequestInitializer(googleAccessProtectedResource)
.setApplicationName(APPLICATION_NAME).build();
//System.out.println(analytics.management().accounts().list().execute());
Get apiQuery = analytics.data().ga().get(TABLE_ID, // "ga:" + Profile
// Id.
"2011-09-01", // Start date.
"2011-12-23", // End date.
"ga:visits"); // Metrics.
try {
GaData gaData = apiQuery.execute();
// Success. Do something cool!
} catch (GoogleJsonResponseException e) {
// Catch API specific errors.
e.printStackTrace();
} catch (IOException e) {
// Catch general parsing errors.
e.printStackTrace();
}
}
Here is the stack trace:
com.google.api.client.googleapis.json.GoogleJsonResponseException: 401 Unauthorized
{
"code" : 401,
"errors" : [ {
"domain" : "global",
"location" : "Authorization",
"locationType" : "header",
"message" : "Invalid Credentials",
"reason" : "authError"
} ],
"message" : "Invalid Credentials"
}
at com.google.api.client.googleapis.json.GoogleJsonResponseException.from(GoogleJsonResponseException.java:138)
at com.google.api.client.googleapis.services.GoogleClient.execute(GoogleClient.java:123)
at com.google.api.client.http.json.JsonHttpRequest.executeUnparsed(JsonHttpRequest.java:67)
at com.google.api.services.analytics.Analytics$Data$Ga$Get.execute(Analytics.java:1335)
at voc.AnalyticsTest.main(AnalyticsTest.java:76)
Try changing your scope from this:
private static final String SCOPE = "https://www.googleapis.com/auth/analytics";
to this:
private static final String SCOPE = "https://www.googleapis.com/auth/analytics.readonly";

Categories