I am using "Google Application Default Credentials" in my scala application to talk to the google sheets api. However, I am getting a "PERMISSION_DENIED" error message from the api response.
Here is a snippet of my code:
private val client: Sheets = {
val serviceAccountScopes = List(SheetsScopes.SPREADSHEETS_READONLY)
// https://developers.google.com/identity/protocols/application-default-credentials
val credential: GoogleCredential = GoogleCredential
.getApplicationDefault()
.createScoped(serviceAccountScopes.asJava)
new Sheets.Builder(httpTransport, jsonFactory, credential)
.setApplicationName(applicationName)
.build()
}
val response = client.spreadsheets().values().get(spreadSheetId, "Total Cost!A2:C2").execute()
val values = response.getValues
Here is the error message:
com.google.api.client.googleapis.json.GoogleJsonResponseException: 403
Forbidden { "code" : 403, "errors" : [ {
"domain" : "global",
"message" : "Request had insufficient authentication scopes.",
"reason" : "forbidden" } ], "message" : "Request had insufficient authentication scopes.", "status" : "PERMISSION_DENIED"
}
I did enabled google sheet api in the google developer console, and the sheet I am trying to read is under my google drive.
The only thing I am not sure about is the google sheet api doc https://developers.google.com/sheets/guides/authorizing#OAuth2Authorizing does not mention anything about if the api supports "Google Application Default Credentials" or not.
Anything I might miss or did wrong here?
Related
I need to implement the following scenario:
A user signs in with his or her Google credentials.
Thereafter my application checks whether or not this user is using Google
Apps.
If he or she is, my application reads all active users in the same Google
Apps domain (com.google.api.client.googleapis.auth.oauth2.GoogleIdToken.Payload#getHostedDomain).
It is similar to "Find your friends" feature on Facebook. In this case I want to
find all people, who use the same Google Apps domain.
Is it possible to do (both technically and with respect to their terms of use)?
Update 1 (28.08.2017 20:00 MSK):
I've tried to get a list of all users using an access token (gIdToken.payload.accessTokenHash in the code fragment below):
internal open fun createJsonFactory(): JsonFactory = JacksonFactory.getDefaultInstance()
internal open fun createTransport(): HttpTransport = GoogleNetHttpTransport.newTrustedTransport()
internal open fun createVerifier(httpTransport: HttpTransport, jsonFactory: JsonFactory): GoogleIdTokenVerifier {
return GoogleIdTokenVerifier.Builder(httpTransport, jsonFactory)
.setAudience(listOf(clientId))
.build()
}
val idToken = req.queryParams("idtoken")
val httpTransport = createTransport()
val jsonFactory = createJsonFactory()
val verifier = createVerifier(httpTransport, jsonFactory)
val gIdToken = verifier.verify(idToken)
if ((gIdToken != null) && (gIdToken.payload != null)) {
val credential = GoogleCredential().setAccessToken(gIdToken.payload.accessTokenHash)
val directory = Directory.Builder(httpTransport, jsonFactory, credential)
.setApplicationName("Google Auth Test")
.build()
try {
directory.users().list().setCustomer(gIdToken.payload.subject).execute()
}
catch (ex:Throwable) {
ex.printStackTrace()
}
}
When I run directory.users().list().setCustomer(gIdToken.payload.subject).execute(), I get the following error:
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:146)
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:321)
I'm using following Google libraries:
<dependency>
<groupId>google-api-services</groupId>
<artifactId>admin-directory_v1</artifactId>
<version>1.22.0</version>
</dependency>
<dependency>
<groupId>com.google.gdata</groupId>
<artifactId>core</artifactId>
<version>1.47.1</version>
</dependency>
How can I fix the error above?
Update 2 (29.08.2017 11:39 MSK): Tried to use Contacts API
Attempt 1: Contacts API feed
import com.google.gdata.data.extensions.ContactFeed
fun readUsers2(gIdToken:GoogleIdToken, httpTransport: HttpTransport, jsonFactory:JsonFactory) {
val credential = GoogleCredential().setAccessToken(gIdToken.payload.accessTokenHash)
val service = ContactsService("Google Auth Test")
service.setOAuth2Credentials(credential)
val feedUrl = URL("https://www.googleapis.com/auth/contacts.readonly")
val resultFeed: ContactFeed = service.getFeed(feedUrl, ContactFeed::class.java)
}
Result: Exception "Unrecognized content type: text/plain" in
com.google.gdata.client.Service#parseResponseData(com.google.gdata.data.ParseSource, com.google.gdata.wireformats.input.InputProperties, java.lang.Class<E>), see
throw statement below.
private <E> E parseResponseData(ParseSource source, InputProperties inputProperties, Class<E> resultType) throws IOException, ServiceException {
Preconditions.checkNotNull("resultType", resultType);
AltFormat inputFormat = null;
String alt = inputProperties.getQueryParameter("alt");
if (alt != null) {
inputFormat = this.altRegistry.lookupName(alt);
}
if (inputFormat == null) {
inputFormat = this.altRegistry.lookupType(inputProperties.getContentType());
if (inputFormat == null) {
throw new ParseException("Unrecognized content type:" + inputProperties.getContentType());
}
}
Attempt 2: Contacts API query
fun readUsers3(gIdToken:GoogleIdToken, httpTransport: HttpTransport, jsonFactory:JsonFactory) {
val credential = GoogleCredential().setAccessToken(gIdToken.payload.accessTokenHash)
val service = ContactsService("Google Auth Test")
service.setOAuth2Credentials(credential)
val feedUrl = URL("https://www.googleapis.com/auth/contacts.readonly")
val query = Query(feedUrl)
val resultFeed = service.query(query, ContactFeed::class.java)
}
Result: Same exception as in attempt 2.
Update 3 (29.08.2017 13:04 MSK):
I tried to get a list of users using the API Explorer.
I got the 403 error. Details are given below.
Request:
GET https://www.googleapis.com/admin/directory/v1/users?domain={MYDOMAIN}&fields=users(emails%2Cid%2Cname)&key={YOUR_API_KEY}
Response:
403
- Show headers -
{
"error": {
"errors": [
{
"domain": "global",
"reason": "forbidden",
"message": "Not Authorized to access this resource/api"
}
],
"code": 403,
"message": "Not Authorized to access this resource/api"
}
}
Update 4 (30.08.2017 13:19 MSK):
Finally, I managed to get rid of authentication and authorization errors (in a
test installation of a G suite app).
However, now the following calls return an empty list of users (without errors).
val directory = Directory.Builder(
NetHttpTransport(),
JacksonFactory.getDefaultInstance(),
credential)
.setApplicationName("Google Auth Test")
.build()
val users = directory.users().list()
You need to get sufficient permissions from the user to fetch the list of all users in the domain. Moreover, it will only work if logged in user is super admin of the domain.
You can get list of permissions here.
This question is related to Google API/get directory contacts
I'm posting an answer here since this one is more google friendly:
The Admin Directory Users:list resource allows company directory listing even without admin privileges.
Just make sure you set the viewType to domain_public and you're good to go:
https://www.googleapis.com/admin/directory/v1/users?customer=my_customer&viewType=domain_public
A scope of https://www.googleapis.com/auth/admin.directory.user.readonly should be enough.
Check this documentation to retrieve all users in the same domain, use the following GET request and include the authorization described in Authorize requests. For readability, this example uses line returns:
GET https://www.googleapis.com/admin/directory/v1/users
?domain=primary domain name&pageToken=token for next results page
&maxResults=max number of results per page
&orderBy=email, givenName, or familyName
&sortOrder=ascending or descending
&query=email, givenName, or familyName:the query's value*
For the request and response properties, you may want to use the Users: list method wherein it retrieves a paginated list of either deleted users or all users in a domain. This request requires authorization with at least one of the following scopes (read more about authentication and authorization).
https://www.googleapis.com/auth/admin.directory.user.readonly
https://www.googleapis.com/auth/admin.directory.user
I'm trying to read insights of my Twitter account with Twitter Insights API and when I request to that given end point I get and error called :
{
"errors": [
{
"code": "UNAUTHORIZED_ACCESS",
"message": "This request is not properly authenticated"
}
],
"request": {
"params": {}
}
}
This is the end point I tried :
https://ads-api.twitter.com/2/insights/accounts/:account_id/available_audiences
I used the given credentials like Consumer Key, Secret and Token, Token secret.
Can anyone explain me why I'm getting this error ? And what I should do ?
Here is a screenshot
Header:
You have to check box "Add params to header"
Everything else should be ok. Just check Consumer Key, Consumer Secret,... data
I would like to know if it is possible to call a Google API that requires auth such as the Google Calendar API using the Apache HttpClient, and no Google code or libraries. (and need the code to do it)
This is the code I have so far, its gets an auth error,
What do you use for the user/password?
HttpPost request = new HttpPost("https://www.googleapis.com/calendar/v3/users/me/calendarList/primary?key=mykey");
DefaultHttpClient client = new DefaultHttpClient();
client.getCredentialsProvider().setCredentials(
new AuthScope(AuthScope.ANY),
new UsernamePasswordCredentials(user, password));
HttpResponse response = client.execute(request);
Error:
"errors": [
{
"domain": "global",
"reason": "required",
"message": "Login Required",
"locationType": "header",
"location": "Authorization"
}
],
"code": 401,
"message": "Login Required"
You don't use Login and password you need to be authenticated once you are you will have an access token then you can call something like this.
https://www.googleapis.com/calendar/v3/users/me/calendarList/primary?access_token={tokenFromAuth}
You can authenticate to Google with any language that is capable of a HTTP POST and a HTTP GET. Here is my walk though of the Auth flow to Google http://www.daimto.com/google-3-legged-oauth2-flow/ you will need to create a Oauth2 credentials on Google Developer console to start. Then its just a matter of asking the user for permission to access their data and requesting the access.
I tried the calendar-appengine-sample on google calendar api java sample and when I used the client_secrets.json for running in localhost it always give me back my calendar list, but when I use the client_secrets.json to be run on google app engine, it give me following error message :
ERROR: 401
{
"code" : 401,
"errors" : [ {
"domain" : "global",
"location" : "Authorization",
"locationType" : "header",
"message" : "Invalid Credentials",
"reason" : "authError"
} ],
"message" : "Invalid Credentials"
}
Before it work with the same client_secrets.json, but suddenly it doesn't work. Would anyone be so kindly tell me how to fix this problem?
thanks.
I had asked this some time back (assuming your using version 3 of the API), and here is a potential answer.
If you get a 403 error, which I did after fixing this then try this.
I am using Big query sample code to work with big query. I am getting the following error while reading dataset list using the big query api.
The code is
Bigquery bigquery = Bigquery.builder(httpTransport, jsonFactory)
.setHttpRequestInitializer(requestInitializer)
.setJsonHttpRequestInitializer(new JsonHttpRequestInitializer() {
public void initialize(JsonHttpRequest request) {
BigqueryRequest bigqueryRequest = (BigqueryRequest) request;
bigqueryRequest.setPrettyPrint(true);
}
}).build();
Datasets.List datasetRequest = bigquery.datasets().list(PROJECT_ID);
DatasetList datasetList = datasetRequest.execute();
if (datasetList.getDatasets() != null) {
java.util.List datasets = datasetList.getDatasets();
for (Object dataset : datasets) {
System.out.format("%s\n", ((com.google.api.services.bigquery.model.DatasetList.Datasets)dataset).getDatasetReference().getDatasetId());
}
}
The exception is
Exception in thread "main" com.google.api.client.googleapis.json.GoogleJsonResponseException: 401 Unauthorized
{
"code" : 401,
"errors" : [ {
"domain" : "global",
"location" : "Authorization",
"locationType" : "header",
"message" : "User is not a trusted tester",
"reason" : "authError"
} ],
"message" : "User is not a trusted tester"
}
at com.google.api.client.googleapis.json.GoogleJsonResponseException.from(GoogleJsonResponseException.java:159)
at com.google.api.client.googleapis.json.GoogleJsonResponseException.execute(GoogleJsonResponseException.java:187)
at com.google.api.client.googleapis.services.GoogleClient.executeUnparsed(GoogleClient.java:115)
at com.google.api.client.http.json.JsonHttpRequest.executeUnparsed(JsonHttpRequest.java:112)
at com.google.api.services.bigquery.Bigquery$Datasets$List.execute(Bigquery.java:964)
at ShortSample.main(ShortSample.java:74
)
I don't see this as an authentication issue as I could use the same code to connect to Google Plus account via Google plus api. I also observed that api examples are stale.
Any Suggestions to fix it.
I suspect you're using an older version of the BigQuery Java client library that is based on a prerelease version of the API (v2beta1). If that's the case, try upgrading to the latest version of the client library here:
http://mavenrepo.google-api-java-client.googlecode.com/hg/com/google/apis/google-api-services-bigquery/v2-rev5-1.5.0-beta/
We'll make sure the links on the API client library page are updated, too!
I have encountered a similar problem and jcondit's solution works well for me (updating the jars). And for the authentication code, you may also take a look at here.