Showing posts with label Remote Event Receiver. Show all posts
Showing posts with label Remote Event Receiver. Show all posts

Thursday, 9 April 2015

SharePoint 2013 Remote Event Receiver using HighTrust App and Self Signed Certificate for On-Premise Environment

Recently I have spent few days on creating some apps for SharePoint 2013. I have used development environment which consist of one standalone sharepoint 2013 server and one more server outside farm for app development. I have learnt after spending days that app development in SharePoint 2013 is not well documented. Specifically how to implement event receiver using high trust app. 

So in this blog, I will gather all my learnings which helped me in implementing high trust app using on-premise environment.


Following are the steps involved:
Create certificate for your domain and
configure SharePoint 2013 trust for your certificate

a) Create self signed certificate for debugging remote event receiver at later step:
  1. Navigate to IIS on SharePoint server
  2. Click on Server Name node from the left side pane
  3. Click on Server Certificates from list of options in middle pane.
  4. Now click on Create Self-Signed Certificate from Actions in right side pane.
  5. Provide name the certificate as HighTrust and click OK
  6. Right click on the HighTrust certificate in IIS and Select Export.
  7. Create new folder named C:\Certs
  8. In Export window, give Export To as C:\Certs\HighTrust.pfx, Password as Password1 and Confirm password same as password field.
  9. Click OK. It will create HighTrust.pfx file under C:\Certs folder.
  10. Now double click on HighTrust from IIS -> Server Name node from left pane -> Server Certificates from middle pane -> List of Certificates
  11. Select Details tab -> Click on Copy to file button.
  12.  It will open Certificate Export wizard, leave default options as it is and click next. On last screen of Copy To wizard, browse to C:\Certs location and name the certificate as HighTrust.cer. Click save button.
  13. Click Next and Finish.

b) Create domain specific certificate:
Creating self signed certificate using IIS is easy but no good if you want to create certificate for specific domain.  We need this certificate after publishing app website.

I have found Richard's blog to learn commands to generate domain specific certificate,
http://rwcchen.blogspot.co.uk/2013/12/sharepoint-2013-domain-certificate-for.html

I) Open Visual Studio Developer Command Prompt and type the following command

  - Generate '.cer' certificate
makecert -r -pe -n "CN=spweb" -b 01/01/2013 -e 11/01/2017 -sky exchange -sy 12 -sp "Microsoft RSA SChannel Cryptographic Provider" -sv "C:\Certs\SPWeb.pvk" "C:\Certs\SPWeb.cer"

- Generate '.pfx' certificate
pvk2pfx -pvk "C:\Certs\spweb.pvk" -spc "C:\Certs\SPWeb.cer" -pfx "C:\Certs\SPWeb.pfx" -pi "Password1"


Note: Date is in US format (mm/dd/yyyy).

II) Import certificate to IIS manager

- Open IIS manager -> Server Certificate -> Import 'SPWeb.pfx' certificate.
- Open MMC.exe and add certificates (Computer Account -> Local Computer)
- Open Personal Store and check if your certificate is added. If not import certificate 'SPWeb.pfx' to this store.
- Copy this certificate to 'Trusted Root Certification Authorities' store.

C) Configure SharePoint 2013 to trust our certificate.

  1. Move 'SPWeb.cer' and 'HighTrust.cer' to SharePoint 2013 server to 'C:\Certs\'.
  2. Download script 'HighTrustConfig-ForSharedCertificate.ps1' from following link, https://msdn.microsoft.com/EN-US/library/office/dn579380.aspx#MultipleAppScript
  3. Open Shell in your SharePoint 2013 server (Start -> All Programs -> Microsoft SharePoint 2013 Products -> SharePoint 2013 Management Shell)
  4. Run above script. It will ask for certificate path (C:\Certs\SPWeb.cer and HighTrust.cer) and Certificate name (SPWeb and HighTrust)
  5. Copy the serial number from [Serial Number] section and Issuer id which is GUID located below Name. In case you lost this information, run following command "Get-SPTrustedSecurityTokenIssuer" and locate your certificate.

Create remote event receiver using provider hosted app (high trust)
Following steps I have copied from following micrsoft article with some modification,

a) Creating app solution
  1. In Visual Studio, choose File, New, Project.
  2. In the New Project wizard, expand the Visual C# or Visual Basic node, and then expand the Office/SharePoint node.
  3. Choose Apps, and then choose to create an App for SharePoint project.
  4. Name the project SPWeb.
  5. Save the project in a location you choose, and then choose OK.
  6. Specify the full URL of the SharePoint developer site. For example, http://SharePointDevSite/
  7. Select the Provider-hosted option, and then choose the Next button.
  8. If you are prompted to specify the type of web project, select ASP.NET Web Forms Application for the continuing example in this topic, and then choose the Next button.
  9. The Configure authentication settings page of the wizard opens. The values that you add to this form will be added to the web.config file automatically. Under How do you want your app to authenticate?, choose Use a certificate.
  10. Click the Browse button next to the Certificate location box and navigate to the location of the self-signed certificate (.pfx file) that you created (C:\Certs). The value of this field should be the full path C:\Certs\HighTrust.pfx.
  11. Type the password for this certificate in the Password box. In this case, it is "Password1".
  12. Type the Issuer ID (which you have copy in earlier section) in the Issuer ID box.
  13. Choose Finish. Much of the configuration is done when the solution opens. Two projects are created in the Visual Studio solution, one for the app for SharePoint and the other for the ASP.NET web application. 
  14. In App project properties, enable App Event Receiver that will add new web service to app website project.
  15. Add new remote event receiver from app project that will also add new web service to app website project.
c) Debug app solution
It's not easy to debug remote event receivers because SharePoint will call event receiver using localhost url so it won't find it. But answer in following forum helped me to solved this problem.

https://social.msdn.microsoft.com/Forums/office/en-US/ff78c575-27a8-4561-93ac-a6a440908871/sp2013-remote-event-receivers-wont-fire-in-onpremises-providerhosted-app?forum=appsforsharepoint

1) Find out localhost url from App website project properties (something like http://localhost:44038)
2) Edit AppManifest.xml from app project and change ~remoteAppUrl with above localhost url for appEventReceiver.svc webservice. This is just for debugging and we have to changed this back when publishing.
3) Open AppManifest.xml and add "Manage" permission for web scope.
4) I have copied following information from above link.

First you need to allow remote connections to IIS on the correct port for all users.  Check the URL property for your web project to find the correct port. From an admin command prompt, run the following from your development machine:
netsh http add urlacl url=http://hh-wks-robb:44038/ user=everyone
Next you need to either disable windows firewall or allow the port through which is what I did.  In the same window, run this:

netsh firewall add portopening TCP 44038 IISExpressWeb enable ALL

After that you need to open the location 

C:\Users\DomainUser\Documents\IISExpress\config\applicationhost.config
Find the existing site node for localhost on the port your VS2012 dev server is running on.  Mine looked like this:


<site name="SPWeb" id="11">
  <application path="/" aplicationPool="Clr4IntegratedAppPool">
    <virtualDirectory path="/" physicalPath="removed for brevity" />
  </application>
  <bindings>
    <binding protocol="http" bindingInformation="*:44038:localhost" />
  </bindings>
</site>
 
You need to add another binding using the name of your development machine. Afterwards, mine looks like this:  

<site name="SPWeb" id="11">
  <application path="/" applicationPool="Clr4IntegratedAppPool">
    <virtualDirectory path="/" physicalPath="Removed for brevity" />
  </application>
  <bindings>
    <binding protocol="http" bindingInformation="*:44038:localhost" />           
    <binding protocol="http" bindingInformation="*:44038:hh-wks-robb" />
  </bindings>
</site>
After this you will need to modify the web.config of your web project in Visual Studio to allow multiple bindings per site.  Right inside the "<system.serviceModel>" tag add this tag:

<serviceHostingEnvironment multipleSiteBindingsEnabled="True"></serviceHostingEnvironment>
  Make sure to stop Visual Studio if it is debugging and then right click the icon for Visual Studio's IISExpress instance and click Exit to make sure it restarts and reloads all configs.  Push F5 to start debugging.Your SharePoint server app should now be able to execute the remote event receivers that are running on your development machine's local IISExpress instance.

d) Add code to update title
1) Edit "AppEventReceiver.svc.cs" and add following code,
public SPRemoteEventResult ProcessEvent(SPRemoteEventProperties properties)
{
   SPRemoteEventResult result = new SPRemoteEventResult();
  
if (properties.EventType == SPRemoteEventType.AppInstalled)
{
using (ClientContext clientContext = TokenHelper.CreateAppEventClientContext(properties, false))
{
if (clientContext != null)
{
var documentsList = clientContext.Web.Lists.GetByTitle("Documents");
clientContext.Load(documentsList);
clientContext.ExecuteQuery();

// debugging url
string opContext = OperationContext.Current.Channel.LocalAddress.Uri.AbsoluteUri.Substring(0, OperationContext.Current.Channel.LocalAddress.Uri.AbsoluteUri.LastIndexOf("/"));
string remoteUrl = string.Format("{0}/RemoteEventReceiver1.svc", opContext);

// deployment
//string remoteUrl = string.Format("https://{0}/
RemoteEventReceiver1.svc", OperationContext.Current.Channel.LocalAddress.Uri.DnsSafeHost + "/services");

EventReceiverDefinitionCreationInformation newEventReceiver = new EventReceiverDefinitionCreationInformation()
{
EventType = EventReceiverType.ItemAdded,
ReceiverName = "RemoteEventReceiver1",
ReceiverUrl = remoteUrl,
SequenceNumber = 1000
};

documentsList.EventReceivers.Add(newEventReceiver);
clientContext.ExecuteQuery();
}
}

}

return result;
}
2) Edit "RemoteEventReceiver1.svc.cs" and add following code,

public void ProcessOneWayEvent(SPRemoteEventProperties properties) {

// On Item Added event, the list item creation executes
    if (properties.EventType == SPRemoteEventType.ItemAdded)
    {
        using (ClientContext clientContext = TokenHelper.CreateRemoteEventReceiverClientContext(properties))
        {
            if (clientContext != null)
            {
                try
                {


                    List lst = clientContext.Web.Lists.GetByTitle(properties.ItemEventProperties.ListTitle);
                    ListItem lstItem = lst.GetItemById(properties.ItemEventProperties.ListItemId);
                    lstItem["Title"] = "Test";
                    lstItem.Update();
                    clientContext.Load(clientContext.Web);
                    clientContext.ExecuteQuery();


                }
                catch (Exception ex) { }
            }
        }
    }
}


Hit F5 and open the app web application. Now open sharepoint site on another tab and try to upload new document in documents library, it should call above event receiver and add title to document.

If you get access denied error, Open AppManifest.xml and in permission tab tick "Allow the app to make app-only calls to SharePoint".