s-KaiNet / spsave

Save files in SharePoint using node.js easily
MIT License
86 stars 22 forks source link

Response code 403 (Forbidden) #71

Closed yarosz closed 3 years ago

yarosz commented 3 years ago

Hi there,

I'm using Sharepoint Online. I'm successfully using sp-download with username password credentials as advised here.

I'm running node.js on Netlify Functions, which are based on AWS Lambdas. This is part of a larger integration where I am downloading a template that's later passed on to an external API that handles esignature, which currently works. The other piece is uploading the signed document back to sharepoint, which is not working and the reason for submitting the current issue.

const { SHAREPOINT_USER, SHAREPOINT_PW } = process.env;
const fs = require('fs')
var spsave = require("spsave").spsave;
const Download = require('sp-download').Download;

const statusCode = 200;
const headers = {
  "Access-Control-Allow-Origin" : "*",
}

const authContext = {
  username: SHAREPOINT_USER,
  password: SHAREPOINT_PW,
  online: true
}
const download = new Download(authContext);
let filePathToDownload = 'https://carbon.sharepoint.com/sites/carbon-IMS/Shared%20Documents/IMS/DocumentTemplates/HelloSignDocs/Form-QRC.pdf';
let saveToPath = '/tmp';

let coreOptions = {
  siteUrl: 'https://carbon.sharepoint.com/sites/carbon-IMS/',
  checkin: true,
  checkinType: 1
};

let fileOptions = {
  folder: 'test',
  fileName: 'file.pdf',
  fileContent: fs.readFileSync('/tmp/Form-QRC.pdf')
};

exports.handler = async (event, context) => {
  try {
    console.log('--- Netlify Endpoint: sharePointUpload ---')
    // console.log({event})

    const savedToPath = await download.downloadFile(filePathToDownload, saveToPath)

    console.log(`${filePathToDownload} has been downloaded to ${savedToPath}`);

    const stats = await fs.statSync('/tmp/Form-QRC.pdf');
    console.log('isFile:', stats.isFile()) //true

    const saveResponse = await spsave(coreOptions, authContext, fileOptions)

    console.log('saveResponse:', saveResponse);

    return {
      statusCode: 200,
      headers,
      body: JSON.stringify({saveResponse})
    }
  } catch (error) {
    return {
      statusCode: 500,
      headers,
      body: JSON.stringify({error: error.message})
    }
  }
}

If when not running the spsave() function, this code works without issue.

When I run the spsave() my code returns:

Request from ::1: GET /.netlify/functions/sharePointUpload
--- Netlify Endpoint: sharePointUpload ---
Downloading: https://saacarbon.sharepoint.com/sites/carbon-IMS/Shared%20Documents/IMS/DocumentTemplates/HelloSignDocs/Form-QRC.pdf
https://carbon.sharepoint.com/sites/carbon-IMS/Shared%20Documents/IMS/DocumentTemplates/HelloSignDocs/Form-QRC.pdf has been downloaded to /tmp/Form-QRC.pdf
isFile: true
[20:03:49] spsave: Error occured:
[20:03:49] spsave: Response code 403 (Forbidden)
[20:03:49] spsave: {"error":{"code":"-2147024891, System.UnauthorizedAccessException","message":{"lang":"en-US","value":"Access denied. You do not have permission to perform this action or access this resource."}}}

[20:03:49] spsave: Stack trace:

[20:03:49] spsave: HTTPError: Response code 403 (Forbidden)
    at EventEmitter.<anonymous> (/Users/nick/Documents/GitHub/Netlify/node_modules/sp-request/node_modules/got/dist/source/as-promise.js:118:31)
    at processTicksAndRejections (internal/process/task_queues.js:97:5)
Response with status 500 in 6489 ms.

I've asked our Sharepoint admin to review the permissions for the account for both and read and write permissions. I don't think MFA is on for this account as It's not required when I login to the browser with the same credentials.

I attempted to send the optional online boolean through but no change.

I reviewed a couple of other closed issues for clues as to how to solve this but haven't come across anything. Any help is much appreciated.

koltyakov commented 3 years ago

Hi @yarosz,

Thanks for using the libraries.

Usually 403 means you really have no permissions for an action.

With SPO it's also might be custom scripts disabled (by default) on Site Collection level. More info

Conditional access might effect behaviour as well, theoretically.

Are you able to upload a file to a location in the UI using this user creds?

yarosz commented 3 years ago

Thank you!

I'm requesting that our Sharepoint Admin look into enabling custom scripts for the credentials I'm using and sent the support docs referenced in the issue you referenced. I'm also asking them to review if there is any conditional access limiting things. Yes, I am able to upload a file to a location using these user creds.

I'll update this issue after attempting the above suggestions.

yarosz commented 3 years ago

Our sharepoint admin followed the steps here to enable custom scripts. Even after this update, I'm getting the same error.

We don't believe that conditional access is in place. To confirm, I am able to upload a file to the location via the UI using these user credentials.

Additionally, I followed the suggestion of @s-KaiNet here to install sp-request and check if it returns web information, which it does. I'm unsure if any of the response is sensitive, but I'm happy to anonymize it and share it here if it's helpful.

What else can I try?

yarosz commented 3 years ago

We just tried changing credentials from the developer account to the owner's account which is an admin with all privileges - so that we could eliminate the possibility of per-user permission settings that might be blocking things. I suspect at this point since we can get an sp-request response, and we can download, there must be some other site-level permission setting that is preventing our access.

I'm considering pivoting away from this particular library for this particular task and trying to interact with the Microsoft REST API directly to see if I get the same error.

yarosz commented 3 years ago

I worked with our Sharepoint Administrator to try and make some changes to the permissions settings, and fortunately one of the settings adjusted lifted the 403 error, but unfortunately, I couldn't find out from him which settings were changed.

yarosz commented 3 years ago

While I'm here, if the authors or community can chime in - I'm wondering if there's anything in the response that would allow me to get a shareable link to the document that would not require logging in. Currently, when I combine the siteURL with the serverRelativeURL it prompts the user to enter credentials, but I want the document to appear immediately.

koltyakov commented 3 years ago

Hi @yarosz you can try getsharelink API.