Google Drive API: 404 when accessing file someone else created - javascript

I'm trying to get the contents of a file located a shared drive in which I am a content manager. The scope I'm using is https://www.googleapis.com/auth/drive.file.
I get 404 errors when I try to access files that are created with my app (which I know is required for the drive.file scope), but by someone else. Everything works as it should when I try to access files created by me.
Here is my code:
function getJsonContent(fileId, accessToken) {
let url = "https://www.googleapis.com/drive/v3/files/" + fileId + "?alt=media&supportsAllDrives=true";
console.log("Sending JSON read request.");
return fetch(url, {
'headers': {'Authorization': "Bearer " + accessToken}
})
.then(response => {
if (response.ok) {
console.log("JSON read successful.");
return response.json();
}
else {
throw "JSON read unsuccessful.";
}
});
}
Here is my authorization code:
function handleAuth(onAccept, onDenial) {
let onSignInUpdate = function(isSignedIn) {
if (isSignedIn) {
onAccept();
} else {
onDenial();
}
}
gapi.load('client:auth2', function() {
gapi.client.init({
apiKey: API_KEY,
clientId: CLIENT_ID,
discoveryDocs: DISCOVERY_DOCS,
scope: SCOPES
})
.then(function() {
gapi.auth2.getAuthInstance().isSignedIn.listen(onSignInUpdate);
onSignInUpdate(gapi.auth2.getAuthInstance().isSignedIn.get()); //Initial update
})
.catch(err => {
console.error(err);
});
});
}
//In another module, specific to a webpage:
function onAccept() {
accessToken = gapi.auth.getToken().access_token;
getJsonContent(<ACTUALFILEID>, accessToken)
.catch(err => {
console.error(err);
});
}
function onDenial() {
console.log("Permission rejected; redirecting to index.html.");
location.replace("index.html");
}
window.onload = function() {
handleAuth(onAccept, onDenial);
};
This is the file creation code:
function makeJsonFile(parentId, fileName, accessToken) {
let url = "https://www.googleapis.com/upload/drive/v3/files?uploadType=multipart&fields=id&keepRevisionForever=true&supportsAllDrives=true";
console.log("Sending JSON creation request.");
let file = new Blob([JSON.stringify([])], {'type': "application/json"}); //The object is an empty array initially
let metadata = {
'name': fileName,
'mimeType': "application/json",
'parents': [parentId],
}
let form = new FormData();
form.append("metadata", new Blob([JSON.stringify(metadata)], {type: "application/json"}));
form.append("file", file);
return fetch(url, { //Returns a promise that resolves with the id of the created file
'method': "POST",
'headers': {
'Authorization': "Bearer " + accessToken
},
'body': form
})
.then(response => {
if (response.ok) {
console.log("JSON created.")
return response.json();
}
else {
throw "JSON creation unsuccessful.";
}
})
.then(val => val.id);
}

Issue:
https://www.googleapis.com/auth/drive.file only gives you access to files that YOU have opened or created with the corresponding app. See this:
View and manage Google Drive files and folders that you have opened or created with this app.
In this case, it is irrelevant that the file is created on a shared drive. I'm thinking you will have to use a wider scope like https://www.googleapis.com/auth/drive, unfortunately.
Feature request:
Also, considering your situation, I think you might be interested in this Feature Request, regarding the possibility to restrict access to a specific folder. I think that could be very useful for you if it got implemented. I'd suggest you to star that issue, both to keep track of its development and to help prioritize its implemenation:
Drive Restrict access to folder when authorizing applications
Reference:
OAuth 2.0 Scopes for Google APIs > Drive API, v3
Authorizing requests with OAuth 2.0

Related

Use Google Drive APIv3 with JavaScript only

I am creating a part of a website where the user can design a certain picture. When they are done I want the picture to be sent to a company Google Drive. I do not have access to the backend of that website, therefore I need to make all this work just in JavaScript.
I set up the API key and OAuth in Google Developer Console. However, I can't find a good solution to make the authentication work. The gapi.auth.getToken() always returns 404. My code looks like this:
function initClient() {
gapi.client
.init({
apiKey: API_KEY,
clientId: CLIENT_ID,
scope: "https://www.googleapis.com/auth/drive",
discoveryDocs: [
"'https://www.googleapis.com/discovery/v1/apis/drive/v3/rest'",
],
})
.then(function () {
GoogleAuth = gapi.auth2.getAuthInstance();
GoogleAuth.isSignedIn.listen(updateSigninStatus);
});
}
function sendToDrive() {
gapi.load("client:auth2", initClient);
let file = canvas.toDataURL("image/png"); // file
var metadata = {
name: file.name,
mimeType: file.type,
parents: [FOLDER_ID],
};
var form = new FormData();
form.append(
"metadata",
new Blob([JSON.stringify(metadata)], { type: "application/json" })
);
form.append("file", file);
fetch(
"https://www.googleapis.com/upload/drive/v3/files?uploadType=multipart",
{
method: "POST",
headers: new Headers({
Authorization: "Bearer " + gapi.auth.getToken().access_token, // Returns 404
}),
body: form,
}
)
.then((res) => {
return res.json();
})
.then(function (val) {
console.log(val);
});
}
Not sure where to look now - the OAuth and API Key configuration seems right, the code returns 404 with no explanation ("gapi.client.error: Pa"), and I couldn't find this kind of use case in the docs (backend side is always used there).
Thanks in advance for any help!

401 Error - Call REST API secured with AZURE Active Directory (JavaScript Client)

Hi All,
I got a scenario in which i am supposed to call a REST api that is secured by a AZURE ACTIVE DIRECTORY. Most of the code runs fine and i am able to get the token using myMSALObj.acquireTokenSilent function too.
401 ERROR COMES WHEN I SEND AJAX CALL USING THAT TOKEN, YOU CAN SEE FOLLOWING CODE
User is there in Active Directory for which i get proper token, i dont know why my ajax call fails when i send that token to rest-api, please help
<script type="text/javascript">
const msalConfig = {
auth: {
clientId: "94edf294-08ae-489f-8621-c6d98009afa8",
authority: "https://login.microsoftonline.com/c483b3c4-21a6-4c93-95ea-7436cf318a68",
redirectUri: "https://localhost:44334/",
},
cache: {
cacheLocation: "sessionStorage", // This configures where your cache will be stored
storeAuthStateInCookie: false, // Set this to "true" if you are having issues on IE11 or Edge
}
};
const myMSALObj = new Msal.UserAgentApplication(msalConfig);
function CallApi() {
// Add scopes for the id token to be used at Microsoft identity platform endpoints.
const loginRequest = {
scopes: ["User.Read"],
};
myMSALObj.loginPopup(loginRequest)
.then((loginResponse) => {
const accessTokenRequest = {
scopes: ["api://8c2b0253-f9e8-442c-bccf-b4a8bbe73b59/access_as_user"]
};
myMSALObj.acquireTokenSilent(accessTokenRequest).then((accessTokenResponse) => {
var accessToken = accessTokenResponse.accessToken;
var apiEndpoint = "https://localhost:44387/api/hello";
var bearer = "Bearer " + accessToken;
console.log("accessToken = ", accessToken);
$.ajax({
url: apiEndpoint,
type: "GET",
beforeSend: function (xhr) { xhr.setRequestHeader("Authorization", bearer) }
}).done(function (response) {
alert("SUCCESS");
console.log("response = ", JSON.stringify(response));
}).fail(function (err) {
console.error("Error Occurred");
console.log("error = ", JSON.stringify(err));
});
})
}).catch(function (error) {
console.log(error);
});
}
</script>
Screenshot of a NEW API Code
Screenshot of JWT.ms (Access Token)
New Screenshot of JWT Token
You should not set the client ID in the appsettings.json file to the client id of your api app. It should be the client id of your client app. According to the jwt analysis diagram you provided, I think it should be: 94edf294- 08ae-489f-8621-c6xxxxxxx.
My bad, Following call was missing in my startup.cs file
app.UseAuthentication();
thanks for your help guys
Specially - #Carl Zhao, #Purushothaman #Rass Masood

http 401 error when providing an access token the outlook api

I am trying to create a folder for a user, and I have been unsuccessful with api call attempts. My code is able to receive the correct access token, so I believe the be bug would be in createFolderTestFunction below.
async function redirectToDashboard() {
console.log("redirect to dashboard");
// var response = await requestTokenSilent();
var response;
if (!response || !response.status == 200) {
response = await requestTokenPopup();
}
if (response.accessToken) {
console.log(response);
createFolderTest(response.accessToken);
// location.href = hostname;
} else {
console.log("Unable to acquire token");
}
}
function createFolderTest(accessToken) {
var options = {
method: "POST",
headers: {
Authorization: accessToken,
"Content-Type": "application/json"
},
mode: "cors",
body: JSON.stringify({
displayName: "#COOLMONDAY"
})
};
var graphEndpoint = "https://outlook.office.com/api/v2.0/me/Inbox/";
fetch(graphEndpoint, options)
.then(resp => {
console.log(resp);
})
.catch(err => {
console.log(err);
});
}
A recommendation would be to get this working in Graph Explorer first. As this eliminates any issues with language being used and access token permissions.
https://developer.microsoft.com/en-us/graph/graph-explorer/preview
The Microsoft Graph endpoint is actually https://graph.microsoft.com/ , you can use the outlook url but moving forward Graph is where we invest in documentation, sdks and tooling.
As per the documentation https://learn.microsoft.com/en-us/graph/api/user-post-mailfolders?view=graph-rest-1.0&tabs=http
You should be using, you're missing 'mailfolders'
POST /me/mailFolders
You could also use our JavaScript SDK which makes mistakes like these a little easier with intellisense and strongly typed objects.
const options = {
authProvider,
};
const client = Client.init(options);
const mailFolder = {
displayName: "displayName-value"
};
let res = await client.api('/me/mailFolders')
.post(mailFolder);
https://learn.microsoft.com/en-us/graph/api/user-post-mailfolders?view=graph-rest-1.0&tabs=javascript

Fetch googlesheet data using access token?

I want to fetch googlesheet data. Through my code I am able to get the access token but now I don't know how can I fetch all the records in a googlesheet using that particular access token in javascript.
I have given the scope in manifest as spreadsheet.readonly.
I have my token in a particular variable and also googlesheeetId in another variable now what headers and what url do I need to paas so that I can get the googlesheet data.
Please help with some reference or links or sample code so that I can get a glimpse of what process do I need to follow to do this.
Here is the code
var gSheetId;
chrome.identity.getAuthToken({ 'interactive': true }, function (token) {
if (chrome.runtime.lastError) {
console.log(chrome.runtime.lastError);
return;
}
console.log(token);
//getting googlesheetId from user
gSheetId = prompt("Please provide your googlesheet Id");
if (!gSheetId) {
alert("Operation Cannot be Performed! Please refresh");
}
else {
//read googlesheetdata
var headers = {
'Authorization':'Bearer '+token
}
//here I need to read the googlesheet data using the token
chrome.debugger.onEvent.addListener(onEvent);
}
});
Thanks in advance!
Solution
Brought to you by #StayAtOrbit , owner of the oiginal question. (this is a community wiki answer)
Useful resources for this issue:
Google sheets API documentation for JavaScript
Sending access token to an API
Making authentificated requests with a token
var gSheetId;
chrome.identity.getAuthToken({ 'interactive': true }, function (token) {
if (chrome.runtime.lastError) {
console.log(chrome.runtime.lastError);
return;
}
console.log(token);
//getting googlesheetId from user
gSheetId = prompt("Please provide your googlesheet Id");
if (!gSheetId) {
alert("Operation Cannot be Performed! Please refresh");
}
else { //read googlesheetdata
var obj = {
method: 'GET',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
'Host': 'sheets.googleapis.com',
'Authorization': 'Bearer '+token
}
}
fetch('https://sheets.googleapis.com/v4/spreadsheets/<sheetID>/values/<range>', obj)
.then(function (res) {
return console.log(res.json());
}).then(function (resJson) {
return console.log(resJson);
})
//here I need to read the googlesheet data using the token
chrome.debugger.onEvent.addListener(onEvent);
}
});

Unauthorized when fetching Tasks with Microsoft graph

I want to fetch my tasks within Javascript and possibly add new ones, but let's focus on fetching a task.
I made an app and use the msal.js file to get a token. I get prompted to allow the app to read/write from my account, the popup closes and I've obtained a token!
So far so good, but when I try to fetch my tasks the API responds with "unauthorized". When I check the headers I can see I sent along "bearer [token]" however.
I'm completely clueless on how to get my tasks by now since I did get a proper token and I've followed the guided setup to make sure I send along the token.
In my app (which I created on https://apps.dev.microsoft.com) I've set all Task related permissions and User.read for good measure. As for the platform I've set "Web".
Is there something I'm missing or mis-configuring?
My init method:
const self = this
this.userAgentApplication = new Msal.UserAgentApplication(this.clientID, null, function (errorDes, token, error, tokenType) {
// this callback is called after loginRedirect OR acquireTokenRedirect (not used for loginPopup/aquireTokenPopup)
})
this.userAgentApplication.loginPopup(['Tasks.readwrite']).then(function (token) {
let user = self.userAgentApplication.getUser()
if (user) {
self.token = token
localStorage.setItem('token', token)
self.getTasks()
}
}, function (error) {
console.log(error)
})
My getTasks method:
const bearer = 'Bearer ' + this.token
let headers = new Headers()
headers.append('Authorization', bearer)
let options = {
method: 'GET',
headers: headers
}
// Note that fetch API is not available in all browsers
fetch('https://outlook.office.com/api/v2.0/me/tasks', options).then(function (response) {
let contentType = response.headers.get('content-type')
if (response.status === 200 && contentType && contentType.indexOf('application/json') !== -1) {
response.json().then(function (data) {
console.log(data)
})
.catch(function (error) {
console.log(error)
})
} else {
response.json().then(function (data) {
console.log(data)
})
.catch(function (error) {
console.log(error)
})
}
})
.catch(function (error) {
console.log(error)
})
Your token is scoped for Graph, not Outlook. Tasks.readwrite will default to the Microsoft Graph and won't work against the Outlook endpoint.
Change this bit:
this.userAgentApplication.loginPopup(['Tasks.readwrite'])
To:
this.userAgentApplication.loginPopup(['https://outlook.office.com/Tasks.readwrite'])
You are trying to use Microsoft Graph, so the request should look like
GET https://graph.microsoft.com/beta/users/{id|userPrincipalName}/outlook/tasks
It's documented here:https://developer.microsoft.com/en-us/graph/docs/api-reference/beta/api/outlookuser_list_tasks
I believe you got a Microsoft Graph token but you're trying to use it on the Outlook REST endpoint, which would not work.

Categories