Using Acumatica REST API with Microsoft Flow - acumatica

Has anyone tried using the Acumatica REST APIs with Microsoft Flow?
I tried it a few weeks ago using Sergey's instructions (click here) and it worked fine.
But now I'm getting the following message on the second step.
{
"message": "You are not logged in."
}
The first step is where I authenticate (just like in Sergey's instructions), then I pass the headers from the first step to the second step (just like in Sergey's instructions), but somehow the second step doesn't think that I'm logged in.
Any ideas?
Here are some screenshots (the only thing I changed for the screenshot is the password):
https://i.imgur.com/uD3CpCa.png
https://i.imgur.com/CZ6XLsW.png
https://i.imgur.com/8XB1LT2.png
Here are the Headers Outputs from the first call (HTTP):
X-Handled-By
Acumatica-PX.Export/AuthenticationManagerModule
Set-Cookie
ASP.NET_SessionId=0koh1ysshqsfmzr0srib2s5z; path=/; HttpOnly,UserBranch=16; path=/,Locale=Culture=en-US&TimeZone=GMTM0800A; expires=Sat, 18-Nov-2017 17:51:27 GMT; path=/,.ASPXAUTH=880C71F4E1A76C36E7E468337C01BC9E6E4C898E1977BAAAF2A35F7217B1D9132794A9547071508A35A2D4B9132DC4B55E86DD6E9C9B8D46CAECE39D74CC9B44BDD47E7C0D836D22D8F4EEFAF8142A9987418B8003EFF5B340DF735E7F8F36EDE5D25300D887E4DADEB0A80B707D87F6B0D32437; path=/; HttpOnly
Server
Microsoft-IIS/7.5
X-Powered-By
ASP.NET
Date
Wed, 15 Nov 2017 17:51:27 GMT
Content-Length
0
Here are the Headers Inputs from the second call (HTTP 2):
{
"X-Handled-By": "Acumatica-PX.Export/AuthenticationManagerModule",
"Set-Cookie": "ASP.NET_SessionId=0koh1ysshqsfmzr0srib2s5z; path=/; HttpOnly,UserBranch=16; path=/,Locale=Culture=en-US&TimeZone=GMTM0800A; expires=Sat, 18-Nov-2017 17:51:27 GMT; path=/,.ASPXAUTH=880C71F4E1A76C36E7E468337C01BC9E6E4C898E1977BAAAF2A35F7217B1D9132794A9547071508A35A2D4B9132DC4B55E86DD6E9C9B8D46CAECE39D74CC9B44BDD47E7C0D836D22D8F4EEFAF8142A9987418B8003EFF5B340DF735E7F8F36EDE5D25300D887E4DADEB0A80B707D87F6B0D32437; path=/; HttpOnly",
"Server": "Microsoft-IIS/7.5",
"X-Powered-By": "ASP.NET",
"Date": "Wed, 15 Nov 2017 17:51:27 GMT",
"Content-Length": "0"
}
Here are the Headers Outputs from the second call (HTTP 2):
X-Handled-By
Acumatica-PX.Export/AuthenticationManagerModule
Date
Wed, 15 Nov 2017 17:51:27 GMT
Set-Cookie
ASP.NET_SessionId=dklemj2usv2zveyc3acxnhm5; path=/; HttpOnly
Server
Microsoft-IIS/7.5
X-Powered-By
ASP.NET
Content-Length
36
Content-Type
application/json; charset=utf-8

I had one previously and the difference that I see between your and mine is that your login post doesn't have any headers.
Mine had :
Accept application/json
Content-Type application/json
Here is a screenshot of my working flow : https://imgur.com/a/trgBa
EDIT :
After playing with this a bit more I have found a way off making this work. though it is using OAuth authentication instead of cookie based authentication.
Here is how :
I first created a connected application on the screen of the same name in acumatica
Of the following type : Ressource Owner Password Credentials
I also created added a shared secret while taking note of its value (it is only showed once).
I created the first http call in flow using the following information
URL : https://"Acumatica ERP instance URL"/identity/connect/token
Header : Content type / application/x-www-form-urlencoded
Body : grant_type=password&client_id=Value1&client_secret=Value2&username=Value3&password=Value4&scope=api
Value1 : ID of the connected application created in step 1
Value2 :Value of the shared secret saved from step 1. If lost just remove and add another secret. just be careful no one else is using that secret
Value3 : Username of existing account
Value4 : Password of account specified in value 3
Create the second HTTP call that will do the web service request
URL : https://"Acumatica ERP instance URL"/entity/Default/17.200.001/Case
Header : Authorization / bearer substring(substring(string(body('HTTP_2')),indexOf(string(body('HTTP_2')),':'),34),2,32)
Body : a normal REST API body for the requested operation
The Substring is only separated by a space in the header
Here are image representing the 2 HTTP calls

Related

DocuSign - Unable to convert document

We’re getting an error – “unable to convert document” for one of our clients on our multi-tenant server. I’ve had a rummage and it looks like that error is generated when you’re sending a file with an unexpected extension meaning that DocuSign doesn’t know how to convert it to a PDF (https://stackoverflow.com/questions/53771197/docusign-random-unable-to-convert-document-error). What I’m failing to understand is how it can be working for some – it works for me on our multi-tenant server – but not others. Is there more to this than meets the eye or am I missing something?
Headers : X-RateLimit-Reset: 1573833600
X-RateLimit-Limit: 1000
X-RateLimit-Remaining: 991
X-DocuSign-TraceToken: #####
Strict-Transport-Security: max-age=31536000; includeSubDomains
Cache-Control: no-cache
Date: Fri, 15 Nov 2019 15:20:40 GMT
Response stream : {
"errorCode": "UNABLE_TO_CONVERT_DOCUMENT",
"message": "System was unable to convert this document to a PDF. Unable to convert Document(2019.11.15_NDA - MyDocument) to a PDF. Error: UserId:##### IPAddress:##### Source:ApiRESTv2:FileType 15_nda - my document is ineligible for conversion."
}
Check that you are setting the fileExtension attribute to pdf in the document object in your Envelopes::create call.
If you don't set it, DocuSign does some guessing, but setting the attribute explicitly is the way to go.

Azure blob GET request authorization header "x-ms-date" field issue

I am trying to fetch a html page which is placed in Azure blob storage using postman. The default blob storage access has been set to private, so i have to send "Shared Key", "x-ms-version" and "x-ms-date" in the header section to Authorize.
Here is the screen shot of request in Postman.
When i click on send button i am getting an error stating "The date header in the request is incorrect".
Any ideas to solve the issue?
Update-Corrected Date Format
I corrected the "x-ms-date" format, now it throwing an error stating Authentication Info is not in correct format
Here is the Authorization section of postman
Thanks for the help.
Please review the documentation.
You need to specify two headers for correct request: Authorization and x-ms-date headers.
The correct format for x-ms-date header is Fri, 26 Jun 2015 23:39:12 GMT.
It seems your Authorization header is invalid.
Try to regenerate your SAS key and test your request again.
As I understand correctly then you have only 15 minutes for requests.
From documentation:
The storage services ensure that a request is no older than 15 minutes by the time it reaches the service. This guards against certain security attacks, including replay attacks. When this check fails, the server returns response code 403 (Forbidden).
The format is Fri, 26 Jun 2015 23:39:12 GMT. In Python, this can be obtained via
import datetime
date = datetime.datetime.utcnow().strftime('%a, %d %b %Y %H:%M:%S GMT')
assuming locale.en_US.
x-ms-date header must be specified in the following format: Fri, 26 Jun 2015 23:39:12 GMT.
Please try your request again with this format.
The format that you should have is:
Thu Apr 7 16:55:44 CET 2022

Fiddler: The input authorization token can't serve the request. Please check that the expected payload is built as per the protocol,

I want to query DocumentDB in Fiddler
So as mentioned in this link, i am using POST method as follows
URL: https://documebtdbaccount:443/dbs/ToDoList/colls/Items/docs
User-Agent: Fiddler
Host: documentdbaccount:443
Content-Length: 0
x-ms-date: Tue, 10 Jan 2017 06:15:00 GMT
x-ms-version: 2016-07-11
authorization: type=master&ver=1.0&sig=masterkey
Content-Type: application/query+json
Accept: application/json
x-ms-documentdb-isquery: True
I am getting below error, not sure what it is about
message=The input authorization token can't serve the request. Please
check that the expected payload is built as per the protocol, and
check the key being used. Server used the following payload to sign:
'post docs dbs/ToDoList/colls/Items tue, 10 jan 2017 05:43:20 gmt
'
ActivityId: a0df52a6-1629-4437-8d15-c82eb02df278
Check your object ID for any prohibited characters.
As per this answer:
The following characters are not supported for the Id property of document: '/', '\\', '?', '#'.
Also, Id cannot end with an empty space.
I've had an extra white space character at the very end of the payload object ID.
It introduces a mismatch between the POST URL (which doesn't has this trailing space) and the actual payload content (which still has it). Thus this error appeared. Removing the trailing space fixed the issue.
Check your authorization key that you are passing in.

Gmail REST api batch support for getting messages

We need to switch from google client library to Gmail REST api in our project, and I've encountered a problem with batch() because it is not available in REST api - you can only get list of ids of messages and then get each message one by one using it's id.
If we use that gmail library everything seems to be clear. We create a batch object and then queue each GET request inside of it. We don't have to care how it's implemented inside.
At the moment I'm trying to do some POC and I'm testing these suggestions https://developers.google.com/gmail/api/guides/batch
with Postman but with no luck..
I'm getting 400 bad request.
How should a proper request body look like in Postman (or other application)?
The next step will be implementing multipart request in Java and sending POST using RestTemplate but I need to present some POC in Postman first.
I'm setting it like on this screenshot ->
Postman
What am I doing wrong?:)
You are close. Here is a working example:
Request
POST https://www.googleapis.com/batch
Content-Type: multipart/mixed; boundary="foo_bar"
Authorization: Bearer {ACCESS_TOKEN}
--foo_bar
Content-Type: application/http
GET /gmail/v1/users/me/messages/152d10540c21bd07
--foo_bar
Content-Type: application/http
GET /gmail/v1/users/me/messages/152d1050d666d7ad
--foo_bar--
Response
--batch_7Xp52oGIwpA_AAEAc7ERnGU
Content-Type: application/http
HTTP/1.1 200 OK
ETag: "A-DdBGA6g-wV4rIZCu5Hcm3JQpY/w2hzEg9kqXFH7AEJ-oSc-y10HNQ"
Content-Type: application/json; charset=UTF-8
Date: Thu, 11 Feb 2016 16:02:06 GMT
Expires: Thu, 11 Feb 2016 16:02:06 GMT
Cache-Control: private, max-age=0
Content-Length: 2809
{
"id": "152d10540c21bd07",
"threadId": "152d1050d666d7ad",
"labelIds": [
"SENT",
"INBOX",
"IMPORTANT"
],
"snippet": "Likewise buddy.", ...
}
--batch_7Xp52oGIwpA_AAEAc7ERnGU
Content-Type: application/http
HTTP/1.1 200 OK
ETag: "A-DdBGA6g-wV4rIZCu5Hcm3JQpY/7v2nqQFBDmEHVvEQoboiwSidilE"
Content-Type: application/json; charset=UTF-8
Date: Thu, 11 Feb 2016 16:02:06 GMT
Expires: Thu, 11 Feb 2016 16:02:06 GMT
Cache-Control: private, max-age=0
Content-Length: 1752
{
"id": "152d1050d666d7ad",
"threadId": "152d1050d666d7ad",
"labelIds": [
"SENT",
"INBOX",
"IMPORTANT"
],
"snippet": "Nice to meet you.", ...
}
--batch_7Xp52oGIwpA_AAEAc7ERnGU--
You don't have to specify the host in each part of the batch, and giving the access token in the Authorization header is enough. You don't have to specify the Content-Length yourself, and don't forget to wrap you boundary string with ".
Then you just have to parse the JSON of each part and you are done.
You need to include gmail/v1 on the POST URL and on each request.
Don't forget about the " around your boundary on the Content-Type header.
See original batch gmail requests documentation: https://developers.google.com/gmail/api/guides/batch
The following worked for me:
POST /batch/gmail/v1 HTTP/1.1
Host: www.googleapis.com
Authorization: Bearer {YOUR_ACCESS_TOKEN}
Content-Type: multipart/mixed; boundary="foo_bar"
--foo_bar
Content-Type: application/http
GET /gmail/v1/users/me/messages/1732ebdcb9b8cccf
--foo_bar
Content-Type: application/http
GET /gmail/v1/users/me/messages/1732ecadae4907e2
--foo_bar--
Creating request with Postman - body screenshot
Creating request with Postman - headers screenshot
Just wanted to say that Lucila's answer is now the correct one - the global (https://www.googleapis.com/batch) endpoint is now deprecated, and you must make a post request to your request-specific endpoint (https://www.googleapis.com/batch/gmail/v1 for gmail, for instance).
See this link for additional context.
Apologies for making a new answer for this, I don't have enough reputation to leave a comment.

How to get request digest value from provider hosted app?

I am developing SharePoint 2013 Provider hosted app using javascript REST Api. In order to perform create (POST), or update (MERGE) operations on sharepoint items I need to set the 'X-RequestDigest' header with the request.
When in SharePoint-hosted apps I was able to use the http://contoso.sharepoint.com/SharePointHostedApp/_api/contextinfo service to retrieve the request digest value; however, I am having trouble getting that value when in a provider hosted app.
The first difference of provider hosted app is that now we need to make a cross-domain request since we are not running in a sharepoint site, but in a different domain hosted on a different server. To be clear: instead of
$.ajax({
url: appWebUrl + '/_api/contextinfo',
method: "POST",
headers: { "Accept": "application/json; odata=verbose" }
})
I assumed we need to use the SP.RequestExecutor to execute a cross domain request.
When I construct the request it looks like the following (I've changed the actual urls to something fake, but basically we're telling the proxy to use the host web has the target and get the /_api/contextinfo endpoint):
https://contoso-6f921c6addc19f.sharepoint.com/ProviderHostedApp/_api/SP.AppContextSite(#target)/contextinfo?#target=%27https://contoso.sharepoint.com%27
However, I receive this error: Cannot find resource for the request contextinfo. meaning that the endpoint does not exist.
I made sure to use the POST method with the correct application/json;odata=verbose headers with an empty body.
How do I get the request digest value from the /_api/contextinfo service to the provider hosted app?
Based on what I've researched:
We can't use $('#__REQUESTDIGEST').val(); because that is not available to a provider hosted app.
We need to use some from of cross-domain request since I'm running outside of sharepoint.
I have tried setting the target of the cross-domain request to both the hostWebUrl and the appWebUrl and both give the same error.
There must be some way to get this value, otherwise we would only be limited to read operations when using JavaScript. Has anyone else solved this using javascript?
Technically I could try to implement the needed services using the CSOM on server and exposing them using WebAPI or WCF but it seem unreasonable to have to implement that.
UPDATE:
I went ahead and tried adding a WebAPI controller which exposes a service that retrieves the request digest value. This actually does retrieve a request digest value; however, when attempting to use this in the header of future calls I receive the error: "The security validation for this page is invalid and might be corrupted. Please use your web browser's Back button to try your operation again." I'm guessing that the request digest value has some referer header information in it which indicates it was requested by the server; however, the future requests made with it are from the browser, and this mismatch might be an acceptable reason for it be invalid.
Few more notes on the attempt at adding the webAPI controller.
I based my code off of this example: http://code.msdn.microsoft.com/SharePoint-2013-Perform-335d925b but converted it to use the newer HttpClient.
I overloaded the Page_Load method, stored the contextTokenString in a variable that could be accessed by the WebAPI controller then parsed/used it when requesting the contextinfo.
Does anyone know if this is a correct diagnosis of that error? Is there something encoded in the request digest value that would prevent it from be able to be retrieved like I suggested?
I have also opened a related question on the MSDN forums since I'm desperate to find an answer:
http://social.msdn.microsoft.com/Forums/sharepoint/en-US/f601fddd-3747-4152-b2d1-4e89f0a771c4/question-about-limitation-of-providerhosted-apps-is-it-possible-to-make-rest-calls-with-javascript?forum=sharepointdevelopmentprevious
I find it very hard to believe this could be a limitation of provider hosted applications, but given all testing I've done, I'm starting to doubt the viability of provider-hosted applications when you want to write in javascript.
Begging for Help!
I realize you've already answered your own question within the context of a provider-hosted app, but for developers like myself who need to access the REST API from a language not based in the .NET framework, (and who cannot write their project as a web app) I'd like to expand on the subject a bit more. I was tasked with writing an iPad app recently that required this functionality, and ended up reverse-engineering the following:
Step 1 - Authentication
Not going to actually cover this, as there are plenty of examples online that demonstrate the more common methods. The Microsoft.SharePoint.Client libraries mostly seem to use claims-based authentication when working with SharePoint Online, with the token being requested through the endpoint found at: https://login.microsoftonline.com/RST2.srf
Step 2 - Acquiring the Request Digest (Dumb Approach)
If you're feeling lazy, you can always take your authenticated cookies, make a GET request to the homepage of the target web, and use a regular expression like:
/(<input (?:[^>]*?)name="?__REQUESTDIGEST"?(?:[^>]*?)\/>)/i
to scrape the HTML from the response. From there, it'd just be a matter of extracting the value attribute for your digest.
Step 2 - Acquiring the Request Digest (SOAP Approach)
The CSOM libraries currently use a SOAP endpoint when acquiring the request digest it uses for its API calls. You can do the same by making a SOAP request to the $(SPWebUrl)/_vti_bin/sites.asmx web service similar to the following:
POST $(SPWebUrl)/_vti_bin/sites.asmx HTTP/1.1
Content-Type: text/xml
SOAPAction: http://schemas.microsoft.com/sharepoint/soap/GetUpdatedFormDigestInformation
X-RequestForceAuthentication: true
Host: $(SPSiteHostname)
Expect: 100-continue
Accept-Encoding: gzip, deflate
Cookie: $(Authenticated Cookies - Either "FedAuth=...; rtFa=..." or "SPOIDCRL=...")
Content-Length: $(Whatever)
<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<GetUpdatedFormDigestInformation xmlns="http://schemas.microsoft.com/sharepoint/soap/" />
</soap:Body>
</soap:Envelope>
When executed successfully, the response body will look something like:
<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<soap:Body>
<GetUpdatedFormDigestInformationResponse xmlns="http://schemas.microsoft.com/sharepoint/soap/">
<GetUpdatedFormDigestInformationResult>
<DigestValue>0x1122334455 ... FF,27 Jul 2015 03:06:54 -0000</DigestValue>
<TimeoutSeconds>1800</TimeoutSeconds>
<WebFullUrl>$(SPWebUrl)</WebFullUrl>
<LibraryVersion>16.0.3208.1222</LibraryVersion>
<SupportedSchemaVersions>14.0.0.0,15.0.0.0</SupportedSchemaVersions>
</GetUpdatedFormDigestInformationResult>
</GetUpdatedFormDigestInformationResponse>
</soap:Body>
</soap:Envelope>
At that point, you can just extract your request digest from the DigestValue block.
Step 2 - Acquiring the Request Digest (REST Approach)
The last approach I'm aware of uses an OData request made to the $(SPWebUrl)/_api/contextinfo endpoint:
POST $(SPWebUrl)/_api/contextinfo HTTP/1.1
Host: $(SPSiteHostname)
DataServiceVersion: 3.0
Accept: application/json; odata=nometadata
Content-Type: application/json; odata=verbose
Cookie: $(Authenticated Cookies)
Content-Length: 2
{}
When executed successfully, the response body will look like the following:
{
"FormDigestTimeoutSeconds" : 1800,
"FormDigestValue" : "0x1122334455 ... FF,27 Jul 2015 03:06:54 -0000",
"LibraryVersion" : "16.0.4230.1217",
"SiteFullUrl" : "$(SPSiteUrl)",
"SupportedSchemaVersions" : ["14.0.0.0", "15.0.0.0"],
"WebFullUrl" : "$(SPWebUrl)"
}
The request digest can then be extracted from the FormDigestValue property.
Step 2 - Acquiring the Request Digest (CSOM Approach)
If you're using CSOM, you have functionality for dealing with this built-in. (probably JSOM, too, unless it uses the __REQUESTDIGEST input) Microsoft.SharePoint.Client.ClientContext uses the SOAP approach internally to manage its request digest and publicly exposes this functionality through its GetFormDigestDirect method.
ClientContext clientContext = new ClientContext(webUrl);
// ...
FormDigestInfo formDigest = clientContext.GetFormDigestDirect();
// X-RequestDigest header value
string headerValue = formDigest.DigestValue;
// Digest expiration
DateTime expirationDate = formDigest.Expiration;
Usage Notes: While ClientContext maintains and reuses a cached form digest for its requests, this method does not give you access to that cached value. Instead, this method requests a brand new form digest with each call, so you'll want to setup your own caching mechanism in order to re-use unexpired digests across multiple requests.
Step 2 - Acquiring the Request Digest (JSOM Approach)
If you're using the JSOM API and don't have access to a __REQUESTDIGEST input value, you can access the ClientContext's cached digest with the following extensions. (Thanks to bdimag for pointing out the cache)
Step 3 - Acquiring New Request Digests
Assuming you use the request digest before the TimeoutSeconds have elapsed, a valid REST request made like the following:
POST $(SPWebUrl)/_api/web/lists/getByTitle('MyList')/getchanges HTTP/1.1
Host: $(SPSiteHostname)
DataServiceVersion: 3.0
Accept: application/json; odata=nometadata
Content-Type: application/json; odata=verbose
X-RequestDigest: $(Request Digest)
Cookie: $(Authenticated Cookies)
Content-Length: 140
{
"query" : {
"__metadata" : {
"type" : "SP.ChangeQuery"
},
"Add" : "True",
"Item" : "True",
"Update" : "True"
}
}
should result in a successful response. If you inspect the headers of that response, you'll find something like:
HTTP/1.1 200 OK
Cache-Control: private, max-age=0
Content-Type: application/json;odata=fullmetadata;streaming=true;charset=utf-8
...
X-RequestDigest: 0xAABBCC...00,03 Sep 2014 18:09:34 -0000
...
Extracting the X-RequestDigest response header will allow you to use it in a subsequent call. (I'm guessing that the timeout starts over from the time of your new response + $(TimeoutSeconds) from the original digest request, but I've yet to confirm)
Unfortunately, the X-RequestDigest header is only returned by REST requests that actually require a request digest. You will not receive the header for requests where a request digest is unrequired, such as: $(SPWebUrl)/_api/web/lists/getByTitle('MyList')/items. Should you find yourself needing a new digest after the original has timed out, you'll need to make another request to the $(SPWebUrl)/_vti_bin/sites.asmx web service.
Step ??? - Handling Errors
A few example responses from when our requests fail:
The following response comes from a REST request made to the $(SPWebUrl)/_api/contextinfo endpoint. (no authentication cookies specified)
HTTP/1.1 403 Forbidden
Cache-Control: private, max-age=0
Content-Type: application/json;odata=nometadata;charset=utf-8
...
Server: Microsoft-IIS/8.5
X-SharePointHealthScore: 0
X-Forms_Based_Auth_Required: $(SPRootSiteUrl)/_forms/default.aspx?ReturnUrl=/_layouts/15/error.aspx&Source=%2f_vti_bin%2fclient.svc%2fcontextinfo
X-Forms_Based_Auth_Return_Url: $(SPRootSiteUrl)/_layouts/15/error.aspx
X-MSDAVEXT_Error: 917656; Access+denied.+Before+opening+files+in+this+location%2c+you+must+first+browse+to+the+web+site+and+select+the+option+to+login+automatically.
DATASERVICEVERSION: 3.0
X-AspNet-Version: 4.0.30319
X-IDCRL_AUTH_PARAMS_V1: IDCRL Type="BPOSIDCRL", EndPoint="$(SiteRelativeUrl)/_vti_bin/idcrl.svc/", RootDomain="sharepoint.com", Policy="MBI"
...
Date: Wed, 12 Aug 2015 02:27:35 GMT
Content-Length: 201
{
"odata.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."
}
}
}
Next, a response originating from a REST request made with an expired request digest (Note the X-RequestDigest header specified in the response.. Not sure if that's usable, but it's worth a shot):
HTTP/1.1 403 FORBIDDEN
Cache-Control: private, max-age=0
Content-Type: application/json;odata=fullmetadata;charset=utf-8
...
Server: Microsoft-IIS/8.5
Set-Cookie: rtFa=$(RtfaAuthCookie)
Set-Cookie: FedAuth=$(FedAuth)
X-SharePointHealthScore: 0
X-RequestDigest: 0x19EFFF80617AB2E48B0A9FF0ABA1440B5301E7445F3859177771BF6A39C7E4A74643108D862505A2C99350B0EDB871EF3DDE960BB68060601268818027F04956,12 Aug 2015 02:39:22 -0000
DATASERVICEVERSION: 3.0
X-AspNet-Version: 4.0.30319
...
Date: Wed, 12 Aug 2015 02:39:22 GMT
Content-Length: 253
{
"odata.error" : {
"code" : "-2130575251, Microsoft.SharePoint.SPException",
"message" : {
"lang" : "en-US",
"value" : "The security validation for this page is invalid and might be corrupted. Please use your web browser's Back button to try your operation again."
}
}
}
Ok, I made a fresh provider hosted application to re-test the problem.
You can view the repository here:
https://github.com/mattmazzola/providerhosted_01
After comparing this new application and the old one, I realized I had a misunderstanding of how the SP.RequestExecutor expected urls to be constructed. I thought it was required to use the SP.AppContextSite() endpoint.
I was incorrectly constructing a request to the appWeb with a url similar to the following:
https://contoso-6f921c6addc19f.sharepoint.com/ProviderHostedApp/_api/SP.AppContextSite(#target)/contextinfo?#target=%27https%3A%2F%2Fcontoso-6f921c6addc19f.sharepoint.com%2FProviderHostedApp%27
As you can see, the #target was set to the appWeb url but infact when making request to the appWeb using RequestExecutor you do no need to do this. It is simply appweburl + "/_api/contextinfo". It is only when making requests for resources existing on the hostWeb that you need use the AppContextSite and set the #target.
You can see the full code in the linked solution for more details. I have added a screenshot of the solution.
You mus remember that in permissions level exist a check that disable all service under _api
_api/web/lists
_api/search/query?querytext=’SharePoint’
_api/SP.UserProfiles.PeopleManager
You enable that ensure
site settings->site permissions->permissions level->read->
Integration client features
Use remote interface
I found the solution in
https://letrasandnumeros.com/2017/02/28/unauthorizedaccessexception-sharepoint-_api/
The RequestExecutor actually takes care of the RequestDigest for you. You don't have to get it.
If for some reason, you still want to get the RequestDigest value, try doing the call without changing the context site.

Resources