How to add Remote Event Receivers to SharePoint List through REST

2.1k Views Asked by At

Adding Event receivers to al List through REST: Msdn has page about REST calls to add Event Receivers.

https://msdn.microsoft.com/en-us/library/office/jj245049.aspx . 
POST http://<sitecollection>/<site>/_api/web/lists(listid)/eventReceivers

What is the bearer token it needs to attach ? I got OAuth token with all the permission for SharePoint online, Still I am not able to get the events if I attach event receivers by above POST call. Could anyone please point me, what Token it needs to attach event receivers, and how to get that token.

2

There are 2 best solutions below

1
On

Please add the http/error exception the app rise here. Otherwise none will be able give you good answer.

Regarding the office365 oauth you have two steps. First your app asks for token_id and receive one, second phase is to get access_token with the token_id you received from the first call. With the access_token you can use SharePoint services if your app have the necessary permissions. More details here: https://msdn.microsoft.com/en-us/office/office365/howto/common-app-authentication-tasks

I haven worked with REST remote receivers, but this might be an alternative to your task https://github.com/OfficeDev/PnP/tree/master/Samples/Core.EventReceivers.

This is also a good read how app can be connected to the offcie365, but in python: http://dev.office.com/code-samples-detail/2139

You can also search across the PnP examples and see if there isn't example that matches your requirements.

1
On

I stumbled upon this problem while creating a nodejs app which automatically creates event receivers. I found out that if I used the PnP Powershell module to create Remote Event Receivers, the events would trigger. If I used REST API with bearer token, it wouldn't work. So I installed Fiddler Web Debugger and inspected the calls that the PnP module does when authenticating.

The problem appears to be that the Event Receiver endpoint is just a wrapper for the good ol' SOAP based SharePoint API which requires that you provide a cookie and a X-RequestDigest header along with your request. (The SharePoint REST API should in my opinion provide a meaningfull error message to point this out, but it does not. On the contrary it returns a 204 status code if you do a POST with a bearer token, and if you do a GET afterwards it will appear as if the Remote Event Receiver was added, but it'll never trigger for the event you subscribed as pointed out earlier.)

My solution only requires a username/password of a privileged user in Azure AD. You do not need to create an Azure AD Application as you won't be needing a bearer token.


1. Request a security token

For SharePoint Online we have to get a security token from this endpoint: https://login.microsoftonline.com/rst2.srf. We will later exchange this token for a cookie which we need to create a working Remote Event Receiver.

Make sure that you put in the correct username and password in this node:

<wsse:UsernameToken wsu:Id="user">
                <wsse:Username>{USERNAME GOES HERE}</wsse:Username>
                <wsse:Password>{PASSWORD GOES HERE}</wsse:Password>
            </wsse:UsernameToken>

Also, update the timestamp in this node (not sure if this node can be removed):

<wsu:Timestamp Id="Timestamp">
                <wsu:Created>2019-05-24T09:10:17.3179897Z</wsu:Created>
                <wsu:Expires>2019-05-25T09:10:17.3179897Z</wsu:Expires>
            </wsu:Timestamp>

This is what the request will look like:

POST https://login.microsoftonline.com/rst2.srf HTTP/1.1
Content-Type: application/soap+xml; charset=utf-8
Host: login.microsoftonline.com
Content-Length: 1869
Expect: 100-continue

<?xml version="1.0" encoding="UTF-8"?>
<S:Envelope xmlns:S="http://www.w3.org/2003/05/soap-envelope" xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" xmlns:wsa="http://www.w3.org/2005/08/addressing" xmlns:wst="http://schemas.xmlsoap.org/ws/2005/02/trust">
  <S:Header>
    <wsa:Action S:mustUnderstand="1">http://schemas.xmlsoap.org/ws/2005/02/trust/RST/Issue</wsa:Action>
    <wsa:To S:mustUnderstand="1">https://login.microsoftonline.com/rst2.srf</wsa:To>
    <ps:AuthInfo xmlns:ps="http://schemas.microsoft.com/LiveID/SoapServices/v1" Id="PPAuthInfo">
      <ps:BinaryVersion>5</ps:BinaryVersion>
      <ps:HostingApp>Managed IDCRL</ps:HostingApp>
    </ps:AuthInfo>
    <wsse:Security>
            <wsse:UsernameToken wsu:Id="user">
                <wsse:Username>{USERNAME GOES HERE}</wsse:Username>
                <wsse:Password>{PASSWORD GOES HERE}</wsse:Password>
            </wsse:UsernameToken>
            <wsu:Timestamp Id="Timestamp">
                <wsu:Created>2019-05-24T09:10:17.3179897Z</wsu:Created>
                <wsu:Expires>2019-05-25T09:10:17.3179897Z</wsu:Expires>
            </wsu:Timestamp>
</wsse:Security>
  </S:Header>
  <S:Body>
    <wst:RequestSecurityToken xmlns:wst="http://schemas.xmlsoap.org/ws/2005/02/trust" Id="RST0">
      <wst:RequestType>http://schemas.xmlsoap.org/ws/2005/02/trust/Issue</wst:RequestType>
      <wsp:AppliesTo>
        <wsa:EndpointReference>
          <wsa:Address>sharepoint.com</wsa:Address>
        </wsa:EndpointReference>
      </wsp:AppliesTo>
      <wsp:PolicyReference URI="MBI"></wsp:PolicyReference>
    </wst:RequestSecurityToken>
  </S:Body>
</S:Envelope>

The response should contain a security token in this node (I've altered the token. The real token should be much longer):

<wsse:BinarySecurityToken Id="Compact0"
                    xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">t=EwDoAk6hBwAUamW55wMmWmiTTNEIaEUGbezNi9M5dTTKDfrZBTzqBswF4EgpEiBQgG1f9isd0lT3KFDE8pHKDaW0pgjiIbhWejs5SYC&amp;p=
                </wsse:BinarySecurityToken>

Here is the complete response:

HTTP/1.1 200 OK
Cache-Control: no-cache, no-store
Pragma: no-cache
Content-Type: application/soap+xml; charset=utf-8
Expires: -1
Strict-Transport-Security: max-age=31536000; includeSubDomains
X-Content-Type-Options: nosniff
x-ms-request-id: [REMOVED BY AUTHOR]
P3P: CP="DSP CUR OTPi IND OTRi ONL FIN"
Set-Cookie: fpc=[REMOVED BY AUTHOR]; expires=Sun, 23-Jun-2019 09:10:15 GMT; path=/; secure; HttpOnly
Set-Cookie: x-ms-gateway-slice=prod; path=/; secure; HttpOnly
Set-Cookie: stsservicecookie=ests; path=/; secure; HttpOnly
Date: Fri, 24 May 2019 09:10:15 GMT
Content-Length: 3497

<?xml version="1.0" encoding="utf-8"?>
<S:Envelope
    xmlns:wsa="http://www.w3.org/2005/08/addressing"
    xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"
    xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"
    xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy"
    xmlns:wst="http://schemas.xmlsoap.org/ws/2005/02/trust"
    xmlns:S="http://www.w3.org/2003/05/soap-envelope">
    <S:Header>
        <wsa:Action S:mustUnderstand="1" wsu:Id="Action">http://schemas.xmlsoap.org/ws/2005/02/trust/RSTR/Issue</wsa:Action>
        <wsa:To S:mustUnderstand="1" wsu:Id="To">http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous</wsa:To>
        <wsse:Security S:mustUnderstand="1">
            <wsu:Timestamp wsu:Id="TS"
                xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
                <wsu:Created>2019-05-24T09:10:15.869581Z</wsu:Created>
                <wsu:Expires>2019-05-24T09:15:15.869581Z</wsu:Expires>
            </wsu:Timestamp>
        </wsse:Security>
    </S:Header>
    <S:Body
        xmlns:S="http://www.w3.org/2003/05/soap-envelope">
        <wst:RequestSecurityTokenResponse
            xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"
            xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy"
            xmlns:wst="http://schemas.xmlsoap.org/ws/2005/02/trust">
            <wst:TokenType>urn:passport:compact</wst:TokenType>
            <wsp:AppliesTo>
                <wsa:EndpointReference
                    xmlns:wsa="http://www.w3.org/2005/08/addressing">
                    <wsa:Address>sharepoint.com</wsa:Address>
                </wsa:EndpointReference>
            </wsp:AppliesTo>
            <wst:Lifetime>
                <wsu:Created>2019-05-24T09:10:15Z</wsu:Created>
                <wsu:Expires>2019-05-24T17:10:15Z</wsu:Expires>
            </wst:Lifetime>
            <wst:RequestedSecurityToken>
                <wsse:BinarySecurityToken Id="Compact0"
                    xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">t=EwDoAk6hBwAUamW55wMmWmiTTNEIaEUGbezNi9M5dTTKDfrZBTzqBswF4EgpEiBQgG1f9isd0lT3KFDE8pHKDaW0pgjiIbhWejs5SYC&amp;p=
                </wsse:BinarySecurityToken>
            </wst:RequestedSecurityToken>
            <wst:RequestedAttachedReference>
                <wsse:SecurityTokenReference
                    xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
                    <wsse:Reference URI="[REMOVED BY AUTHOR]"></wsse:Reference>
                </wsse:SecurityTokenReference>
            </wst:RequestedAttachedReference>
            <wst:RequestedUnattachedReference>
                <wsse:SecurityTokenReference
                    xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
                    <wsse:Reference URI="[REMOVED BY AUTHOR]"></wsse:Reference>
                </wsse:SecurityTokenReference>
            </wst:RequestedUnattachedReference>
        </wst:RequestSecurityTokenResponse>
    </S:Body>
</S:Envelope>

2. Exchange the security token for a cookie

This request is much simpler as no body is needed. Just add the security token to the Authorization as seen below.

GET https://{YOUR TENANT}.sharepoint.com/_vti_bin/idcrl.svc/ HTTP/1.1
Authorization: BPOSIDCRL t=EwDoAk6hBwAUamW55wMmWmiTTNEIaEUGbezNi9M5dTTKDfrZBTzqBswF4EgpEiBQgG1f9isd0lT3KFDE8pHKDaW0pgjiIbhWejs5SYC&p=
X-IDCRL_ACCEPTED: t
Host: {YOUR TENANT}.sharepoint.com

The response should give you a cookie (the cookie in the example is not valid. The real cookie would contain a longer string). Extract the SPOIDCRL=SOMETHING from the cookie.

HTTP/1.1 200 OK
Cache-Control: private
P3P: CP="ALL IND DSP COR ADM CONo CUR CUSo IVAo IVDo PSA PSD TAI TELo OUR SAMo CNT COM INT NAV ONL PHY PRE PUR UNI"
Set-Cookie: SPOIDCRL=77u/PD94bWwgdmVyc2lvkE9PTwvU1A+; path=/; secure; HttpOnly
X-SharePointHealthScore: 2
X-AspNet-Version: 4.0.30319
SPRequestGuid: [REMOVED BY AUTHOR]
request-id: [REMOVED BY AUTHOR]
MS-CV: [REMOVED BY AUTHOR]
Strict-Transport-Security: max-age=31536000
X-FRAME-OPTIONS: SAMEORIGIN
SPRequestDuration: 319
SPIisLatency: 0
X-Powered-By: ASP.NET
MicrosoftSharePointTeamServices: 16.0.0.8908
X-Content-Type-Options: nosniff
X-MS-InvokeApp: 1; RequireReadOnly
X-MSEdge-Ref: Ref A: [REMOVED BY AUTHOR] Ref B: [REMOVED BY AUTHOR] Ref C: 2019-05-24T09:10:15Z
Date: Fri, 24 May 2019 09:10:15 GMT
Content-Length: 0

3. Get digest value to be used as X-RequestDigest

We also need the X-RequestDigest value together with the cookie in order to do any alterations with the SharePoint REST API. To get the digest value we're gonna POST to {tenant}.sharepoint.com/sites/{your site}/_api/contextinfo. From the response we must grab the value of FormDigestValue. Remember to attach the cookie from the response we got in step 2.

POST /sites/{YOUR SITE}/_api/contextinfo HTTP/1.1
Host: {TENANT}.sharepoint.com
Content-Type: application/json; charset=utf-8
Accept: application/json
OData-Version: 4.0
Cookie:  SPOIDCRL=77u/PD94bWwgdmVyc2lvkE9PTwvU1A+

Response body:

{
    "@odata.context": "https://{tenant}.sharepoint.com/sites/{your site}/_api/$metadata#SP.ContextWebInformation",
    "FormDigestTimeoutSeconds": 1800,
    "FormDigestValue": "0xA36B0B40BAF03EC,24 May 2019 20:36:14 -0000",
    "LibraryVersion": "16.0.8908.1212",
    "SiteFullUrl": "https://{tenant}.sharepoint.com/sites/{your site}",
    "SupportedSchemaVersions": [
        "14.0.0.0",
        "15.0.0.0"
    ],
    "WebFullUrl": "https://{tenant}.sharepoint.com/sites/{your site}"
}

4. Create a new Remote Event Receiver

At last we can do what we came to do, let's create a new Remote Event Receiver using the SharePoint REST API.

POST /sites/{your site}/_api/web/lists/getbytitle('Documents')/eventreceivers HTTP/1.1
Host: {tenant}.sharepoint.com
Content-Type: application/json; charset=utf-8
Accept: application/json
OData-Version: 4.0
X-RequestDigest: 0xA36B0B40BAF03EC,24 May 2019 20:36:14 -0000
Cookie: SPOIDCRL=77u/PD94bWwgdmVyc2lvkE9PTwvU1A+


{
"ReceiverAssembly": null,
"ReceiverClass": null,
"ReceiverName": "TestRERItemDeleted",
"SequenceNumber": 10000,
"Synchronization": 2,
"EventType": 10003,
"ReceiverUrl": "https://your-endpoint-where-you-want-the-requests-to-go"
}

I should point out that if you need to update or delete the event receiver, you need to authenticate as the same user who created the receiver in the first place. For example if you try to delete the receiver with a bearer token, you'll get an error. You must authenticate and use the cookie together with the x-requestdigest. If you just want to GET the receivers, you would be fine with a bearer token.