Google Fit location datatype yields 403 Permission Denied - google-fit

In my app I am requesting data from a Google Fit dataset using the "aggregate" function. This works fine for heart rate, speed, distance, etc., but this stopped working (but has been working before) for location data.
This is the request template I am using:
POST https://www.googleapis.com/fitness/v1/users/me/dataset:aggregate
{
"aggregateBy": [
{
"dataTypeName": "com.google.location.sample"
}
],
"endTimeMillis": xxxx,
"startTimeMillis": xxxx
}
For dataTypeName=="com.google.location.sample" the request returns:
403
- Show headers -
{
"error": {
"errors": [
{
"domain": "global",
"reason": "forbidden",
"message": "No permission to read data for this private data source."
}
],
"code": 403,
"message": "No permission to read data for this private data source."
}
}
Why is location data private? Has there been a change on the Google
API side? Is there a different way to request location data?
These are the scopes my app is authorized for:
"https://www.googleapis.com/auth/fitness.activity.read",
"https://www.googleapis.com/auth/fitness.body.read",
"https://www.googleapis.com/auth/fitness.location.read",
"https://www.googleapis.com/auth/fitness.nutrition.read"
Note that the list includes "fitness.location.read".
Thanks for any advice.

I also try to get data from Google Fit but for "steps" and also sometimes getting the response with status 403
REQUEST:
POST https://www.googleapis.com/fitness/v1/users/me/dataset:aggregate
$headers = [
'Authorization' => 'Bearer ' . $access_token,
'Content-Type' => 'application/json',
];
$body = [
"aggregateBy" => [
[
"dataTypeName" => "com.google.step_count.delta",
"dataSourceId" =>
"derived:com.google.step_count.delta:com.google.android.gms:estimated_steps"
],
[
"dataTypeName" => "com.google.distance.delta",
"dataSourceId" =>
"derived:com.google.distance.delta:com.google.android.gms:merge_distance_delta"
]
],
"bucketByTime" => ["durationMillis" => xxxx],
"startTimeMillis" => xxxx,
"endTimeMillis" => xxxx
];
RESPONSE:
"error": {
"errors": [
{
"domain": "global",
"reason": "forbidden",
"message": "datasource not found (truncated...)
POST https://www.googleapis.com/fitness/v1/users/me/dataset:aggregate` resulted in a `403 Forbidden` response:
DECISION:
All descriptions for error 403 you can see here:
https://developers.google.com/analytics/devguides/reporting/core/v3/errors
For resolving you can try to do this algorithm: https://developers.google.com/analytics/devguides/reporting/core/v3/errors#backoff
or your users should have sufficient permissions (403 "insufficientPermissions")

Related

How can I get my Google Groups via API? I am NOT admin

I want to get all my groups but I am not an admin
I know how to fetch groups if I am admin. But if not - I don't understand. Also I can see its via website
My steps:
I do auth with Google and with scope
[
"https://www.googleapis.com/auth/userinfo.email",
"https://www.googleapis.com/auth/groups",
]
I fetch groups by GET. Url: "https://admin.googleapis.com/admin/directory/v1/groups?domain=konstructly.com"
I get the next problem:
{
"error": {
"code": 403,
"message": "Not Authorized to access this resource/api",
"errors": [
{
"message": "Not Authorized to access this resource/api",
"domain": "global",
"reason": "forbidden"
}
]
}
}
My question
How can I get my Google Groups via Google API?
I tried:
Auth with scope
[
"https://www.googleapis.com/auth/userinfo.email",
"https://www.googleapis.com/auth/groups",
]
GET "https://admin.googleapis.com/admin/directory/v1/groups?domain=konstructly.com"
Expectation:
List of my groups
Actual result:
{
"error": {
"code": 403,
"message": "Not Authorized to access this resource/api",
"errors": [
{
"message": "Not Authorized to access this resource/api",
"domain": "global",
"reason": "forbidden"
}
]
}
}
Instead of using the Google Workspace Admin SDK, use the Groups Service from Google Apps Script, more specifically GroupsApp.getGroups()

Google Drive API - Files stored in a GSuite Shared Drive (only) get a 404 error

My application has scopes drive.file, drive.readonly and drive.metadata.readonly.
Using https://github.com/googleapis/google-api-php-client v2.2.2
It works fine fetching files when authenticated as a Google Drive user, but only when those files are owned by another user (or the same user).
Files stored on a Shared Drive (G Suite Business feature) and shared with the user result in a 404 error:
"error": {
"errors": [
{
"domain": "global",
"reason": "notFound",
"message": "File not found: 1gasqkgWcabla8sksT5FUtZGzlfIwGbc_aI4g2gl9bla.",
"locationType": "parameter",
"location": "fileId"
}
],
"code": 404,
"message": "File not found: 1gasqkgWcabla8sksT5FUtZGzlfIwGbc_aI4g2gl9bla."
}
I have verified that the file in question is indeed readable by that user via Drive/Docs etc.
I have been into the API Console and checked the "Shared Drives support" under Drive UI integration - this made no difference.
Neither did it help to add the wider 'drive' scope.
After testing I found the same issue.
When I tried to get a file from a Shared Drive with the following HTTP Request
GET /drive/v3/files/<ID-file> HTTP/1.1
Host: www.googleapis.com
Content-length: 0
Authorization: Bearer <access Token>
I got the same error as you did:
{
"error": {
"code": 404,
"message": "File not found: 1DIM-vS4058e0X5eutNmOqSr3z0rA1Nqh.",
"errors": [
{
"locationType": "parameter",
"domain": "global",
"message": "File not found: 1DIM-vS4058e0X5eutNmOqSr3z0rA1Nqh.",
"reason": "notFound",
"location": "fileId"
}
]
}
}
But upon reading the documentation it's clear that you need to include the supportsAllDrives paramater to the HTTP request.
So now adding supportsAllDrives=true my request is the following:
GET /drive/v3/files/<File ID>?supportsAllDrives=true HTTP/1.1
Host: www.googleapis.com
Content-length: 0
Authorization: Bearer <access Token>>
And then I got to retrieve the file in the response:
{
"mimeType": "image/jpeg",
"kind": "drive#file",
"name": "file.jpg",
"driveId": "<Drive Id>",
"teamDriveId": "<Team Drive ID>",
"id": "<File ID>"
}

User can upload but can't delete file on shared drive

I'm using the REST API to create [1], list [2] and delete [3] files.
An user with content manager privileges can create a file to a shared drive.
This user can also list the drive contents.
However, when trying to delete the same file with:
DELETE /drive/v3/files/<fileid>?supportsAllDrives=true
a 403 Forbidden error is returned with a body like this:
{
"error": {
"errors": [
{
"domain": "global",
"reason": "insufficientFilePermissions",
"message": "The user does not have sufficient permissions for this file."
}
],
"code": 403,
"message": "The user does not have sufficient permissions for this file."
}
}
This can be reproduced with Google API Explorer.
Edit:
About get [4] returns
GET https://www.googleapis.com/drive/v3/about?supportsAllDrives=true
{
"user": {
"kind": "drive#user",
"displayName": "Jessy Hurley",
"me": true,
"permissionId": "03805955809493222960",
"emailAddress": "jessyhurley#ondazultecnologia.com"
}
}
Permissions get [5] returns
GET https://www.googleapis.com/drive/v3/files/1PWntiqRbg3ZhPdqkNBAv7_NpevGrHsO1/permissions/03805955809493222960?supportsAllDrives=true
{
"kind": "drive#permission",
"id": "03805955809493222960",
"type": "user",
"role": "fileOrganizer"
}
[1] https://developers.google.com/drive/api/v3/reference/files/create
[2] https://developers.google.com/drive/api/v3/reference/files/list
[3] https://developers.google.com/drive/api/v3/reference/files/delete
[4] https://developers.google.com/drive/api/v3/reference/about/get
[5] https://developers.google.com/drive/api/v3/reference/permissions/get
From [6] a content manager user can't permanently delete files.
So I change the call to move to trash using [7]:
PATCH https://www.googleapis.com/drive/v3/files/1PWntiqRbg3ZhPdqkNBAv7_NpevGrHsO1?supportsAllDrives=true
{
"trashed": true
}
[6] https://support.google.com/a/answer/7337554?hl=en
[7] https://developers.google.com/drive/api/v3/reference/files/update

Send a timstamp in JSON via Postman

I'm fairly new to working with REST APIs and Postman and I'm looking for a way to include a timestamp as a string in a POST request body via Postman.
According to the Postman docs, there is a dynamic variable {{$timestamp}} which can be used in the request body. However, when I try to use it, I get a 400 back from the endpoint I am POSTing to with the message "malformed data."
Here are a couple of variations I have tried:
variation 1
[
{
"from_number": "+123456789",
"messages": [
{
"text": "Message at" {{$timestamp}},
"to_number": "+123456789"
}
]
}
]
variation 2
[
{
"from_number": "+123456789",
"messages": [
{
"text": "Message at {{$timestamp}}",
"to_number": "+123456789"
}
]
}
]
variation 3
[
{
"from_number": "+123456789",
"messages": [
{
"text": "Message at " + {{$timestamp}},
"to_number": "+123456789"
}
]
}
]
All of these have returned the same error
{
"errorMessage": "malformed data",
"error": true
}
Thanks #NimS and #JAAulde: Varation 2 is the correct answer. The problem must have been a typo.

Using Chrome auth to access gmail api inside of a Chrome Extension

Building a Chrome Extension for Gmail, trying to use Chrome Auth to gain access to gmail api per this StackOverflow post and the Gmail API docs, among others. I successfully receive the token with chrome.identity.getAuthToken({ 'interactive': true }, function(token) {} however when I plug the token into the request url I get the following 401 error response (code follows)
error response
{
"error": {
"errors": [
{
"domain": "global",
"reason": "required",
"message": "Login Required",
"locationType": "header",
"location": "Authorization"
}
],
"code": 401,
"message": "Login Required"
}
}
My code:
background.js
chrome.tabs.onUpdated.addListener( function (tabId, changeInfo, tab) {
if (changeInfo.status == 'complete') {
chrome.identity.getAuthToken({ 'interactive': true }, function(token) {
thisToken = token
chrome.runtime.onMessage.addListener(
function(request,sender,sendResponse){
var gapiRequestUrlAndToken = "https://www.googleapis.com/gmail/v1/users/mail%40gmail.com/threads?key={" + thisToken + "}"
var makeGetRequest = function (gapiRequestURL)
{
var xmlHttp = new XMLHttpRequest();
xmlHttp.open( "GET", gapiRequestURL, false );
xmlHttp.send( null );
return xmlHttp.responseText;
}
makeGetRequest(gapiRequestUrlAndToken);
}
);
});
}
})
manifest.json
{
"manifest_version": 2,
"key": "<key>",
"name": "exampleName",
"description": "Description",
"version": "0.0.1.7",
"default locale": "en",
"icons": { "128": "imgs/pledge_pin.png"},
"content_scripts" : [
{
"matches": ["*://mail.google.com/mail/*"],
"js": ["js/jquery.js", "js/compose.js", "bower_components/jqnotifybar/jquery.notifyBar.js"],
"css": ["css/stylesheet.css", "bower_components/jqnotifybar/css/jquery.notifyBar.css"]
}
],
"background": {
"scripts": ["scripts/background.js"]
},
"permissions": [
"identity"
],
"oauth2": {
"client_id": "<client id>",
"scopes": ["https://www.googleapis.com/auth/gmail.modify"]
}
}
I suspect it has something to do with the fact that I'm trying to use Chrome Auth for Gmail api, but other posts I've read have lead me to believe this is a viable option.
In case my code didn't give it away, I'm a newbie, so any help is most welcome, and I greatly appreciate your time.
key is for generic application secrets. For user specific tokens you need to use access_token. And token should not be wrapped in {}. If mail%40gmail.com is the actual value you are using in the URL, it won't work. It either needs to be the email address of the authenticated user or me.
So change:
"https://www.googleapis.com/gmail/v1/users/mail%40gmail.com/threads?key={" + thisToken + "}"
To this:
"https://www.googleapis.com/gmail/v1/users/me/threads?access_token=" + thisToken