Sharing SharePoint online site and file with external users using CSOM

With lots if questions and requirements moving around, I thought of making a post about sharing a SharePoint online document / file with external user.

Good news is Microsoft has released APIs for both CSOM (https://msdn.microsoft.com/en-us/library/office/microsoft.sharepoint.client.web.shareobject.aspx) and REST to share an OBJECT (Could be web or document) with external users. Instead of I writing much I will let my code talk much for me :)

This blog post is concentrated around sharing object using CSOM. I will later publish a blog about how to share files using REST APIs for MS and other platform users. Till then sorry for non MS platforms!

Prerequisites


You must first enable external sharing to your site collection from your office 365 SharePoint Admin Console. Follow this article to do so;

https://support.office.com/en-us/article/Share-your-Office-365-sites-with-external-users-89502322-bfbb-43d6-9207-4030f8ce26e0

Lets get started


  • Take reference to latest SharePoint client library nuget "Microsoft.SharePointOnline.CSOM". It is important to take the correct package from nuget to get the updated methods included in the library or you will land up blaming MS about not including ShareObject method in DLL :) Trust me I did that many times! Ok, so my version here is "16.1.0.0" for "Microsoft.SharePoint.Client" and "Microsoft.SharePoint.Client.Runtime".
  • Here is the method of ShareObject look like;

public static SharingResult ShareObject(
    ClientRuntimeContext context,
    string url,
    string peoplePickerInput,
    string roleValue,
    int groupId,
    bool propagateAcl,
    bool sendEmail,
    bool includeAnonymousLinkInEmail,
    string emailSubject,
    string emailBody
)
 
  • If I look at above method, some of the argument in it looks little difficult to understand. Let me give insight of it;
  1. ClientRuntimeContext: This isnt anything but your clientcontext object. The most common name used for it is ctx, You will understand more about it when you see the actual code.
  2. URL: this should be full path of the object which needs sharing. Example for web is "https://contoso.sharepoint.com/site/shareexample/shareweb" and example for document is "https://contoso.sharepoint.com/site/shareexample/shareweb/shared documents/myfile.pdf"
  3. peoplePickerInput: Its a JSON string representing the external user. For example, here is the value for my live account: "[{"Key" : "nbkhubani@live.in", "Description" : "nbkhubani@live.in", "DisplayText" : "nbkhubani@live.in", "EntityType" : "", "ProviderDisplayName" : "", "ProviderName" : "", "IsResolved" : true, "EntityData" : {"SPUserID" : "nbkhubani@live.in", "Email" : "nbkhubani@live.in", "IsBlocked" : "False", "PrincipalType" : "UNVALIDATED_EMAIL_ADDRESS", "AccountName" : "nbkhubani@live.in", "SIPAddress" : "nbkhubani@live.in"}, "MultipleMatches" : []}]"
  4.  roleValue: The sharing role value for the type of permission to grant on the object. There are few different options for this dependent on the sharing style. If you share a document you will need to use either of following values:
        "role:1073741827"
        ”role:1073741826”
        When you are however sharing a site for external user, you’ll need to use following format:
        ”group:{groupId}” – {groupId} should be replaced with the unique group id from the site where external user will be assigned to. 
  5. groupId: The ID of the group to be added to. Use zero if not adding to a permissions group. Not actually used by the code even when user is added to existing group. See description for roleValue.
  6. propagateAcl: A flag to determine if permissions should be pushed to items with unique permissions.
Rest of the parameters are self-explanatory so I am not writing their description here.

Code

Here is the code look like;


          //sit details and credentials
            string site = "";
            string user = "";
            string password = "";


            var securePassword = new SecureString();
            //Convert string to secure string 
            foreach (char c in password)
                securePassword.AppendChar(c);

            using (ClientContext ctx = new ClientContext(site))
            {

                ctx.Credentials = new SharePointOnlineCredentials(user, securePassword);

                //Lets load the root web
                Web rootWeb = ctx.Site.RootWeb;
                ctx.Load(rootWeb, rw => rw.Url);
                ctx.ExecuteQuery();

                //get the web URL
                string webUrl = rootWeb.Url;

                string roleValue = "group:8"; // int depends on the group IDs at site

                int groupId = 0;

                bool propageAcl = false; // Not relevant for external accounts

                bool sendEmail = true;

                bool includedAnonymousLinkInEmail = false;

                string emailSubject = null;

                string emailBody = "Nitin Khubani Sharing Site";

                string peoplePickerValue = ResolvePeoplePickerValueForEmail(rootWeb, "nbkhubani@live.in");

                SharingResult result = Web.ShareObject(ctx, webUrl, peoplePickerValue,

                                                roleValue, groupId, propageAcl,

                                                sendEmail, includedAnonymousLinkInEmail,

                                                emailSubject, emailBody, true);

                ctx.Load(result);
                ctx.ExecuteQuery();

                if(result.StatusCode == SharingOperationStatusCode.CompletedSuccessfully)
                {
                    Console.Write("You are a share champ!");
                    Console.ReadLine();
                }
                else
                {
                    Console.Write("Ooops! You need one step more to become share champ! See the error in result please :)");
                    Console.ReadLine();
                }

            }


And the ResolvePeoplePickerValueForEmail Method code is here; 



///

        /// get the people picker value from spweb and email
        ///

        /// SPWeb object retrieved from client context
        /// Email address (Should be MS email)
        ///
        public static string ResolvePeoplePickerValueForEmail(Web web, string emailAddress)
        {
            ClientPeoplePickerQueryParameters param = new ClientPeoplePickerQueryParameters();
            param.PrincipalSource = Microsoft.SharePoint.Client.Utilities.PrincipalSource.All;
            param.PrincipalType = Microsoft.SharePoint.Client.Utilities.PrincipalType.All;
            param.MaximumEntitySuggestions = 30;
            param.QueryString = emailAddress;
            param.AllowEmailAddresses = true;
            param.AllowOnlyEmailAddresses = false;
            param.AllUrlZones = false;
            param.ForceClaims = false;
            param.Required = true;
            param.SharePointGroupID = 0;
            param.UrlZone = 0;
            param.UrlZoneSpecified = false;

            // Resolve people picker value based on email
            var ret = ClientPeoplePickerWebServiceInterface.ClientPeoplePickerResolveUser(web.Context, param);
            web.Context.ExecuteQuery();

            // Return people picker return value
            return string.Format("[{0}]", ret.Value);
        }

Download

You can download the sample code from here.

Url: https://1drv.ms/u/s!AhpMZHT1qpBQgcBcnOQxBOCbiAfPxQ

Screenshot

Here is a sample email I received when I shared site with my live accout;



Here is how I can login and my site look like (There isnt much to see in this screenshot though, still have fun if you can!)





Credits (most important)

Majority of of the code and post is inspired / copied from below Microsoft PnP article and MSDN post;


https://blogs.msdn.microsoft.com/vesku/2015/10/02/external-sharing-api-for-sharepoint-and-onedrive-for-business 

https://github.com/SharePoint/PnP/tree/master/Samples/Core.ExternalSharing

Comments

Popular Posts