1

I have been trying to do an interesting proof-of-concept/experiment

  1. Get SharePoint user's cookies from the browser
  2. Pass these cookies to an Azure Function
  3. By having cookies only - determine who the user is (+)
  4. Run GET/POST REST queries using user's permissions (+)
  5. Run C#/CSOM code using user's permissions (-)

This might be a very nice approach that works very well without the need for some nasty adal.js+iframe solutions that only work with Graph API and don't work with Azure functions.

So, basically, I'm stuck with the last task #5. I have cookies and I have FormDigest, but I don't know how to get CSOM ClientContext using this information. Is it even possible? Can someone, please help me with it? I will be really obliged for any tips.

I'm including a working code below. You can insert your own cookies there to try it out.

class Program
{
    static void Main(string[] args)
    {
        AsyncMain(args);
        Console.ReadKey(false);
    }

    static async void AsyncMain(string[] args)
    {
        string baseurl = "https://contoso.sharepoint.com";
        string rtFa = "REUSED_RtFA_Cookie";
        string fedAuth = "REUSED_fedAUTH_COOKIE";

        var digest = await GetDigest(baseurl, rtFa, fedAuth);

        var baseAddress = new Uri(baseurl);
        var cookieContainer = new CookieContainer();
        using (var handler = new HttpClientHandler() { CookieContainer = cookieContainer })
        using (var client = new HttpClient(handler) { BaseAddress = baseAddress })
        {
            client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
            client.DefaultRequestHeaders.Add("X-RequestDigest", digest);

            cookieContainer.Add(baseAddress, new Cookie("rtFa", rtFa));
            cookieContainer.Add(baseAddress, new Cookie("FedAuth", fedAuth));

            StringContent bodyContent = new StringContent("{\"__metadata\":{\"type\":\"SP.Data.ArtefactsListItem\"},\"Title\":\"NEW VALUE2\"}");
            bodyContent.Headers.ContentType = MediaTypeHeaderValue.Parse("application/json;odata=verbose");
            var result = client.PostAsync("/_api/web/Lists/getByTitle('Artefacts')/items", bodyContent).Result;
            var contents = await result.Content.ReadAsStringAsync();
            result.EnsureSuccessStatusCode();
            Console.WriteLine("SharePoint List Item Created using REST");
        }

        // TODO: Awesome! We can do REST calls using user's cookies, but it's much easier if we could somehow do it using CSOM. How do we get ClientContext from cookies and a Form Digest?
        using (var context = new ClientContext(baseurl))
        {
            // TODO: I want get SPContext having user's cookies and Form Digest:
            context.Credentials = new SharePointOnlineCredentials("", new System.Security.SecureString());
            context.Load(context.Web, web => web.Title);
            context.ExecuteQuery();
            Console.WriteLine("Your site title is: " + context.Web.Title);
        }
    }

    static async Task<string> GetDigest(string baseUrl, string rtFa, string FedAuth)
    {
        var baseAddress = new Uri(baseUrl);
        var cookieContainer = new CookieContainer();
        using (var handler = new HttpClientHandler() { CookieContainer = cookieContainer })
        using (var client = new HttpClient(handler) { BaseAddress = baseAddress })
        {
            client.DefaultRequestHeaders.Add("Accept", "application/json; odata=verbose");
            cookieContainer.Add(baseAddress, new Cookie("rtFa", rtFa));
            cookieContainer.Add(baseAddress, new Cookie("FedAuth", FedAuth));

            var bodyContent = new StringContent("", Encoding.UTF8, "application/json");
            var result = client.PostAsync(baseUrl+ "/_api/contextinfo", bodyContent).Result;
            var contents = await result.Content.ReadAsStringAsync();


            result.EnsureSuccessStatusCode();
            dynamic response = JsonConvert.DeserializeObject(contents);
            dynamic d = response.d;
            string digest = d.GetContextWebInformation.FormDigestValue;

            return digest;
        }
    }
}
1

Basically, the thing #5 you're trying to achieve is implemented in PnP AuthenticationManager's GetWebLoginClientContext. They use webbrowser control to grab the cookie and attach to ClientContext.

However you can't get rid of adal.js on a client side, because fedauth and rtfa cookies are httponly cookies, which means they are not accessible from javascript.

This might be a very nice approach that works very well without the need for some nasty adal.js+iframe solutions that only work with Graph API and don't work with Azure functions.

Could you elaborate what exactly doesn't work for you in case of Azure functions?

  • Thank you for your reply, Sergei! 1) I didn't know fedAuth and rtfa cookies are not accessible from JavaScript. It essentially ruins my experiment, but I'm glad you told me. 2) The issue with Azure function for me was that I could not determine, which SharePoint user called it from JavaScript. I've seen a few articles, but they seem too complex for a seemingly simple task. – Denis Molodtsov Nov 15 '18 at 15:24
  • 1
    It looks trivial, but actually, it's not, because technically you need to "transfer" authentication between different domains. Of course in the easiest scenario, you can send a user's id to the function. But if you want real authentication context, you should setup azure ad auth for functions via Easy Auth, on the client SharePoint side use SPFx AAdHttpClient to authenticate a user and pass access token to the function. – Sergei Sergeev Nov 15 '18 at 16:33

Your Answer

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Not the answer you're looking for? Browse other questions tagged or ask your own question.