CRM Online to SharePoint Online Integration using REST and ADFS

Accessing SharePoint Online 2013 REST services with SSO via ADFS (Active Directory Federation Services) from CRM Online provides loads of potential opportunities, especially now that SharePoint offers a huge REST API. You can call REST from a CRM Online Plugin or Custom Workflow activity with no dependencies on SharePoint Client dll’s or Azure getting in the way, It is fairly awesome I must say.

I use the HttpWebRequest class to perform SOAP requests to perform the authentication part of this integration, which is totally supported within Sandboxed Plugins and Custom Workflow Activities. You can read more about the restrictions of the CRM Sandboxed environment here http://msdn.microsoft.com/en-us/library/gg334752.aspx. Once you get authenticated and obtain the cookies you are free to fire REST calls off by simply providing the cookies along with the request.

The best diagram I found to describe the authentication process visually was from Wictor Wilen’s blog http://www.wictorwilen.se/

Auth Process

I wanted to explore the issue of authentication between CRM Online and SharePoint Online from a server to server perspective and as you can imagine authentication is a big hurdle for this direct type of communication as every example I have seen so far includes Azure. Well at least until I started to look at what others were doing with Windows 8 Apps and Active Authentication. Omar Venado http://blogs.msdn.com/b/omarv/ and fellow MVP Wictor Wilen http://www.wictorwilen.se/ had some really great articles to help me build out a solution focused on this scenario. The code attached to this blog is heavily derived from Omar’s Windows 8 App code though now is useable in non Windows 8 App projects such as C# Console, SSIS Packages and of course CRM Plugin/Workflow projects. I have added some background reading at the bottom of this post so you can see the articles I used to get my code to work.

An example of how simple this code is to use

Uri spSite = new Uri(https://myOrg.sharepoint.com/sites/mySite”);

bool success = SpoAuthUtility.Create(spSite, username@domain.com, WebUtility.HtmlEncode(“Pword123″), false);

string odataQuery = “_api/web/lists”;

Uri url = new Uri(String.Format(“{0}/{1}”, SpoAuthUtility.Current.SiteUrl, odataQuery));

// Send a json odata request to SPO rest services to fetch all list items for the list.

byte[] result = HTTPHelper.SendODataJsonRequest(

url,

“GET”, // reading data from SP through the rest api usually uses the GET verb

null,

(HttpWebRequest)HttpWebRequest.Create(url),

SpoAuthUtility.Current // pass in the helper object that allows us to make authenticated calls to SPO rest services

);

string response = Encoding.UTF8.GetString(result, 0, result.Length);

You can authenticate using corporate credentials if inside your network, username/password if outside your network (e.g CRM Online O365) and you can also use your onmicrosoft.com accounts. Remember that my primary focus was on server to server integration possibilities, not user to server contexts such as in the current SharePoint CRM List Component model.

The attached C# Console app contains 3 files to provide you with an example of how to use the 2 cs classes HTTPHelper and SPOAuthUtility. To authenticate and start calling REST from CRM Plugins/Workflows simple include the HTTPHelper and SPOAuthUtility cs files into your Plugin/Workflow project.

Example App is available here https://skydrive.live.com/redir?resid=492A170E77E43399!830

Background reading

http://blogs.msdn.com/b/omarv/archive/2012/10/25/windows-8-store-apps-office-365-enterprise-preview-sharepoint-online.aspx

http://blogs.msdn.com/b/omarv/archive/2012/11/15/developing-windows-store-apps-for-sharepoint-online-with-sso-single-sign-on.aspx

http://www.wictorwilen.se/Post/How-to-do-active-authentication-to-Office-365-and-SharePoint-Online.aspx

http://allthatjs.com/2012/03/28/remote-authentication-in-sharepoint-online/

About these ads

About Rhett Clinton MVP
Dynamics CRM MVP

31 Responses to CRM Online to SharePoint Online Integration using REST and ADFS

  1. mahenderpal says:

    Nice Post Rhett !!

  2. mahenderpal says:

    Reblogged this on Mahender Pal and commented:
    Nice Post for Sharepoint Online and CRM Online Integration

  3. Pingback: Mahender Pal

  4. Vikram Singh says:

    Great Post Rhett ! thanks for sharing knowledge !!!

    I am trying this inside sandbox plugin but getting error “System.Security.SecurityException: That assembly does not allow partially trusted callers”. May be reason is HttpUtility is not supported in partial trust, not sure but got information from here

    http://social.msdn.microsoft.com/Forums/en-US/302377cf-97bd-4fe9-b7d0-fa7f08152927/systemsecuritysecurityexception-that-assembly-does-not-allow-partially-trusted-callers

    If I am trying this through console application or on-premise plugin(non-sandbox) then I am getting error “Given key was not present in the dictionary” in below line

    if (dynamicObject["AuthURL"] != null)
    {
    corpAdfsProxyUrl = new Uri(Convert.ToString(dynamicObject["AuthURL"]));
    }

    Please suggest, if I am missing something.

    thanks for your help !!!

    • Hi Vikram, I have only used the HttpUtility to HTMLEncode the password as I found when having characters like & in the password it would fail with Bad Request. I have found that WebUtility.HtmlEncode which is apart of System.Net works fine.

      Regards the Given key not found then it suggests the auth url is not found for you, this is the sts server. Inspect the return response and see if it is giving an error, you can view this in the code or by using Fiddler.

      Regards,
      Rhett

      • Vikram Singh says:

        Hi Rhett, Thank you for your help !!!

        #1, I have removed HttpUtility.HtmlEncode, now getting error “access security critical method ‘System.Web.Script.Serialization.JavaScriptSerializer..ctor()’ failed”

        #2, I tried to debug it and dynamic dynamicObject having only following Keys and values combination:

        State,4
        UserState,1
        Login,myuser@org.onmicrosoft.com
        NameSpaceType, Managed
        FederationBrandNme, ORG.ONMICROSOFT.COM

        Please help on this.

      • Hi Vikram, I have updated the SPOAuthUtility class file as there was an issue in that O365 onmicrosoft accounts do not return a AuthUrl like federated ADFS accounts do when trying to retrieve the sts server address. This means the dynamic object will not be needed for your scenario so I added a simple check to make sure the json string contains the AuthURL value before serializing the string to dynamic.

        This should work for you now, just download the updated zip file.

        Cheers,
        Rhett

  5. Pingback: MSDynamicsWorld.com CRM Community Full Articles

  6. Pingback: Dynamics CRM field experts show off next gen integration; Can Microsoft keep up with the channel? - MSDynamicsWorld.com CRM Community Full Articles - Microsoft Dynamics CRM - Microsoft Dynamics Community

  7. Vikram Singh says:

    Wonderful, thank you so much Rhett ! It’s working now in console application.
    What about sandbox plugin?

    • Most definitely, I am providing a console main class to demonstrate its use but the exact same code works within a sandbox plugin. This was the main aim of this solution.

      Cheers, Rhett

      • Vikram Singh says:

        Hi Rhett, We are getting above #2 errror while using same code inside sandbox pluign. Have you tried to use same code inside sandbox plugin ?

      • Have you tried using the latest code sample I have uploaded, I have tested this using AD sync and onmicrosoft accounts.

  8. Pingback: CRM Online to SharePoint Online Integration using REST and ADFS - Mahender Pal - Microsoft Dynamics CRM - Microsoft Dynamics Community

  9. Dave Brockelsby MCT says:

    Great example Rhett! Saved me a lot ot time. Could you help me find the proper way to sent Update, Create and Delete POSTs via this code example?

    • Hi Dave, one of my most difficult things I’ve found is trying to get the REST services to function as the examples online seem to all be for JavaScript. I recommend looking for other blogs that have covered examples of this already as I do not have thorough knowledge of working with the full SharePoint REST interface yet.

      Cheers,
      Rhett

  10. Hi Rhett,

    I did tried to used your code in a console application and it worked very well but using same code inside sandbox plugin fails with the following error message

    Assembly ‘System.Web.Extensions, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35′ is a conditionally APTCA assembly which is not enabled in the current AppDomain. To enable this assembly to be used by partial trust or security transparent code, please add assembly name ‘System.Web.Extensions, public key = xxxxx to the PartialTrustVisibleAssemblies list when creating the AppDomain.

    Any idea why it failing for CRM online? Any help will be really appreciated.

  11. Hi Rhett,

    Thanks a l lot for getting back to me. I did tried in a console application with partial trust and I got the following error.

    Assembly ‘TestConsoleAppSPRest, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null’ is marked with the AllowPartiallyTrustedCallersAttribute, and uses the level 2 security transparency model. Level 2 transparency causes all methods in AllowPartiallyTrustedCallers assemblies to become security transparent by default, which may be the cause of this exception.\n\nAssembly ‘System.Web.Extensions, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35′ is a conditionally APTCA assembly which is not enabled in the current AppDomain. To enable this assembly to be used by partial trust or security transparent code, please add assembly name ‘System.Web.Extensions, PublicKey=xxxxxxxxx’ to the the PartialTrustVisibleAssemblies list when creating the AppDomain.”

    This assembly is marked as conditionally APTCA (allow partially trusted callers attribute), which means that code running in the Sandbox won’t be able to call into it without product changes to CRM. Without this change, there isn’t a supported workaround that I am aware of.

    If you have any other thoughts please share it.

    Thanks a lot

  12. Hi Rhett,

    I don’t think we can remove the System.Web.Extensions because System.Web.Script.Serialization.JavaScriptSerializer needs that reference.

    Thanks

    • The javascript serializer was used in a later version of this utility but now I use Newtonsoft to handle json responses. Prior to this I just just used the xmldocument or similar to handle the response but I’m having trouble trying to find the original version of my project.

      If you have time you could just remove the reference and handle the response without it. It is used only once I think.

      Otherwise I will try to get this updated soon.

      Cheers, Rhett

  13. Hi Rhett,

    Tried the Newtonsoft and the console app works fine but CRM complains about Newtonsoft component missing error. Looks like I need to use ILMerge which probably not a supported way for Microsoft. I will let you know how this goes. Is any other way you could think about avoiding ILMerge?

    Cheers

  14. Hi Rhett,

    Tried the ILMerge and when the plugin runs I am getting System.TypeLoadException error.

    Thanks

    • Hi Rhett,

      We raised a support call with Microsoft and suggested that to host a web service in Windows Azure which acts as an interim between SP 2013 online and CRM 2011 online. This will help in synching the data between the two applications. So that means no other options to automate this from a plugin sandbox mode :-(

      • Aravindan Muniappan says:

        Hi Rhett, I have used your code for sharepoint integration. Thank You for sharing us the code. Instead of using JavaScript Serializer, I have used DataContractJsonSerializer to deserialize JSON response. Below is the code snapshot.

        // convert string to stream
        byte[] byteArray = Encoding.UTF8.GetBytes(Encoding.UTF8.GetString(result, 0, result.Length));
        //byte[] byteArray = Encoding.ASCII.GetBytes(contents);
        MemoryStream stream = new MemoryStream(byteArray);

        //MemoryStream stream1 = new MemoryStream();
        DataContractJsonSerializer ser = new DataContractJsonSerializer(typeof(ContextInformation));
        stream.Position = 0;
        ContextInformation ci = (ContextInformation)ser.ReadObject(stream);
        digest = (ci != null) ? ci.d.GetContextWebInformation.FormDigestValue : String.Empty;

        Thanks,
        Aravind

      • That’s great Aravind, thanks for sharing. When I used that I didn’t like that some objects had d as the parent object so I found it inconsistent when trying to make a generic style function. But it works well if you code for specific scenarios as you have done with your example.

        Many thanks,
        Rhett

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: