I am trying to download files from Sharepoint using the Sharepoint REST API from a web app; my code needs to eventually work with Sharepoint Subscription Edition (and maybe even Sharepoint 2019), but for dev purposes I have created a test Sharepoint Online account and uploaded files there. I seem to have been able to authenticate ok using msal-browser, but I can't seem to get any information about files or folders in the Sharepoint document library; my GetFolderByServerRelativeUrl('Shared Documents') calls return 403 Forbidden. Interestingly, a call to https://xxxx.sharepoint.com/_api/web/lists
does seem to work ok, though I'm not sure how I might get from the lists to the proper path/url to the document(s) I'm interested in. I suspect I might not be using the right scope(s) for the Sharepoint REST API that I'm trying to use. I don't believe I can use the Graph API because of my need to be backwards compatible with Subscription Edition and maybe even 2019.
Here are the API Permissions I have on the app registration (I'm not sure which of these, if any, might actually be needed, given that the code I have that seems to be closest to working doesn't actually requests any of these in the 'scopes'):
- Microsoft Graph
- SharePoint
- AllSites.Read
- MyFiles.Read
Here's how I get the access token:
async _getAccessToken(): Promise<string> {
const msalConfig = {
auth: {
clientId: "<myClientId>",
authority: "https://login.microsoftonline.com/<myAuthority>",
redirectUri: "https://myMachine",
},
cache: {
cacheLocation: "sessionStorage",
storeAuthStateInCookie: false
}
};
const msalInstance = new PublicClientApplication(msalConfig);
await msalInstance.initialize();
const loginRequest = {
scopes: ["https://xxxx.sharepoint.com/.default"
/* "Files.Read.All", "MyFiles.Read"*/
],
skipCache: false, // This skips the cache if set to true
};
try {
const authResult = await msalInstance.loginPopup(loginRequest);
console.log("authResult: ", authResult);
if (authResult == null) {
console.error("Did not get an access token.")
this._selectAccount(msalInstance);
return "";
// const ssoSilentResponse = await msalInstance.ssoSilent(silentRequest);
// return ssoSilentResponse.accessToken;
} else {
console.log(`Returning access token ${authResult.accessToken}`);
return authResult.accessToken;
}
} catch (error) {
console.log(error);
// if (error instanceof InteractionRequiredAuthError) {
// signIn();
// }
throw error;
}
}
This then works with the returned access token:
let response = await esriRequest(`https://xxxx.sharepoint.com/_api/web/lists`, {
method: "GET",
headers: {
Authorization: `Bearer ${accesssToken}`,
Accept: "application/json;odata=verbose "
}
});
This results in a 403 Forbidden error:
let response = await esriRequest(`https://xxxx.sharepoint.com/_api/web/GetFolderByServerRelativeUrl('Shared Documents')`, {
method: "GET",
headers: {
Authorization: `Bearer ${accesssToken}`,
Accept: "application/octet-stream;odata=verbose"
}
});
This results in a 400 Bad Request:
let response = await esriRequest(`https://xxxx.sharepoint.com/_api/web/GetFileByServerRelativeUrl('Shared Documents/myFile.pdf')/$value`, {
method: "GET",
headers: {
Authorization: `Bearer ${accesssToken}`,
Accept: "application/octet-stream;odata=verbose"
}
});
I do realize that I'm not referencing a Sharepoint site in the form of /sites/xyz/ as I often see. I think this should theoretically be OK as I'm using my admin account when I'm doing this, so I think this should all still theoretically work...however, I did create another test Sharepoint site "xyz" under this tenant to see if there would be any difference. In that case, I am setting this as the scope: https://xxxx.sharepoint.com/sites/xyz/.default
when logging in, and I get this error: invalid_resource: AADSTS500011: The resource principal named https://xxxx.sharepoint.com/sites/xyz was not found in the tenant named MSFT. This can happen if the application has not been installed by the administrator of the tenant or consented to by any user in the tenant. You might have sent your authentication request to the wrong tenant.
I would be incredibly grateful for any pointers!!