Using a managed service identity to call into SharePoint Online. Possible?

%3CLINGO-SUB%20id%3D%22lingo-sub-264651%22%20slang%3D%22en-US%22%3EUsing%20a%20managed%20service%20identity%20to%20call%20into%20SharePoint%20Online.%20Possible%3F%3C%2FLINGO-SUB%3E%3CLINGO-BODY%20id%3D%22lingo-body-264651%22%20slang%3D%22en-US%22%3E%3CP%3EHi%20All%2C%3C%2FP%3E%3CP%3E%26nbsp%3B%3C%2FP%3E%3CP%3EI%20have%20been%20playing%20around%20with%20Managed%20Service%20Identity%20in%20Azure%20Logic%20Apps%20and%20Azure%20Function%20Apps.%20I%20think%20it%20is%20the%20best%20thing%20since%20sliced%20bread%20and%20am%20trying%20to%20enable%20various%20scenarios%2C%20one%20of%20which%20is%20using%20the%20MSI%20to%20get%20an%20app-only%20token%20and%20call%20into%20SharePoint%20Online.%3C%2FP%3E%3CP%3E%26nbsp%3B%3C%2FP%3E%3CP%3EUsing%20Logic%20Apps%2C%20I%20generated%20a%20managed%20service%20identity%20for%20my%20app%2C%20and%20granted%20it%20Sites.readwrite.All%20on%20the%20SharePoint%20application.%20When%20then%20using%20the%20HTTP%20action%20I%20was%20able%20to%20call%20REST%20endpoints%20while%20using%20Managed%20Service%20Identity%20as%20Authentication%20and%20using%20https%3A%2F%2F%3CTENANT%3E.sharepoint.com%20as%20the%20audience.%3C%2FTENANT%3E%3C%2FP%3E%3CP%3E%26nbsp%3B%3C%2FP%3E%3CP%3EI%20then%20though%20I'd%20take%20it%20a%20step%20further%20and%20create%20a%20function%20app%20and%20follow%20the%20same%20pattern.%20I%20created%20the%20app%2C%20generated%20the%20MSI%2C%20added%20it%20the%20Sites.readwrite.All%20role%20same%20way%20I%20did%20with%20the%20Logic%20App.%3C%2FP%3E%3CP%3E%26nbsp%3B%3C%2FP%3E%3CP%3EI%20then%20used%20the%20code%20below%20to%20retrieve%20an%20access%20token%20and%20try%20and%20generate%20a%20clientcontext%3A%3C%2FP%3E%3CP%3E%26nbsp%3B%3C%2FP%3E%3CP%3E%26nbsp%3B%3C%2FP%3E%3CDIV%3E%3CSTRONG%3E%23r%20%22Newtonsoft.Json%22%3C%2FSTRONG%3E%3C%2FDIV%3E%3CDIV%3E%3CSTRONG%3Eusing%20Newtonsoft.Json%3B%3C%2FSTRONG%3E%3CBR%20%2F%3E%3CSTRONG%3Eusing%20System%3B%3C%2FSTRONG%3E%3CBR%20%2F%3E%3CSTRONG%3Eusing%20System.Net%3B%20%3C%2FSTRONG%3E%3CBR%20%2F%3E%3CSTRONG%3Eusing%20System.Net.Http%3B%3C%2FSTRONG%3E%3CBR%20%2F%3E%3CSTRONG%3Eusing%20System.Net.Http.Headers%3B%3C%2FSTRONG%3E%3CBR%20%2F%3E%3CSTRONG%3Eusing%20Microsoft.SharePoint.Client%3B%3C%2FSTRONG%3E%3C%2FDIV%3E%3CDIV%3E%3CSTRONG%3Epublic%20static%20void%20Run(string%20input%2C%20TraceWriter%20log)%3C%2FSTRONG%3E%3CBR%20%2F%3E%3CSTRONG%3E%7B%3C%2FSTRONG%3E%3CBR%20%2F%3E%3CSTRONG%3E%26nbsp%3B%26nbsp%3B%26nbsp%3B%20string%20resource%20%3D%20%22https%3A%2F%2F%3CTENANT%3E.sharepoint.com%22%3B%3C%2FTENANT%3E%3C%2FSTRONG%3E%3CBR%20%2F%3E%3CSTRONG%3E%26nbsp%3B%26nbsp%3B%26nbsp%3B%20string%20apiversion%20%3D%20%222017-09-01%22%3B%3C%2FSTRONG%3E%3C%2FDIV%3E%3CDIV%3E%26nbsp%3B%26nbsp%3B%26nbsp%3B%20%3CSTRONG%3Eusing%20(var%20client%20%3D%20new%20HttpClient())%3C%2FSTRONG%3E%3CBR%20%2F%3E%3CSTRONG%3E%26nbsp%3B%26nbsp%3B%26nbsp%3B%20%7B%3C%2FSTRONG%3E%3CBR%20%2F%3E%3CSTRONG%3E%26nbsp%3B%26nbsp%3B%26nbsp%3B%26nbsp%3B%26nbsp%3B%26nbsp%3B%26nbsp%3B%20client.DefaultRequestHeaders.Add(%22Secret%22%2C%20Environment.GetEnvironmentVariable(%22MSI_SECRET%22))%3B%3C%2FSTRONG%3E%3CBR%20%2F%3E%3CSTRONG%3E%26nbsp%3B%26nbsp%3B%26nbsp%3B%26nbsp%3B%26nbsp%3B%26nbsp%3B%26nbsp%3B%20var%20response%20%3D%20client.GetAsync(String.Format(%22%7B0%7D%2F%3Fresource%3D%7B1%7D%26amp%3Bapi-version%3D%7B2%7D%22%2C%20Environment.GetEnvironmentVariable(%22MSI_ENDPOINT%22)%2C%20resource%2C%20apiversion)).Result%3B%3C%2FSTRONG%3E%3CBR%20%2F%3E%3CSTRONG%3E%26nbsp%3B%26nbsp%3B%26nbsp%3B%26nbsp%3B%26nbsp%3B%26nbsp%3B%26nbsp%3B%26nbsp%3B%26nbsp%3B%26nbsp%3B%26nbsp%3B%20var%20responseContent%20%3D%20response.Content%3B%20%3C%2FSTRONG%3E%3CBR%20%2F%3E%3CSTRONG%3E%26nbsp%3B%26nbsp%3B%26nbsp%3B%26nbsp%3B%26nbsp%3B%26nbsp%3B%26nbsp%3B%26nbsp%3B%26nbsp%3B%26nbsp%3B%26nbsp%3B%20string%20responseString%20%3D%20responseContent.ReadAsStringAsync().Result.ToString()%3B%3C%2FSTRONG%3E%3CBR%20%2F%3E%3CSTRONG%3E%26nbsp%3B%26nbsp%3B%26nbsp%3B%26nbsp%3B%26nbsp%3B%26nbsp%3B%26nbsp%3B%26nbsp%3B%26nbsp%3B%26nbsp%3B%26nbsp%3B%20var%20json%20%3D%20JsonConvert.DeserializeObject%3CDYNAMIC%3E(responseString)%3B%3C%2FDYNAMIC%3E%3C%2FSTRONG%3E%3CBR%20%2F%3E%3CSTRONG%3E%26nbsp%3B%26nbsp%3B%26nbsp%3B%26nbsp%3B%26nbsp%3B%26nbsp%3B%26nbsp%3B%26nbsp%3B%26nbsp%3B%26nbsp%3B%26nbsp%3B%20string%20accesstoken%20%3D%20json.access_token.ToString()%3C%2FSTRONG%3E%3C%2FDIV%3E%3CDIV%3E%26nbsp%3B%26nbsp%3B%26nbsp%3B%26nbsp%3B%26nbsp%3B%26nbsp%3B%26nbsp%3B%26nbsp%3B%26nbsp%3B%26nbsp%3B%26nbsp%3B%20%3CSTRONG%3EClientContext%20ctx%20%3D%20new%20ClientContext(%22%3CSITEURL%3E%22)%3B%3C%2FSITEURL%3E%3C%2FSTRONG%3E%3CBR%20%2F%3E%3CSTRONG%3E%26nbsp%3B%26nbsp%3B%26nbsp%3B%26nbsp%3B%26nbsp%3B%26nbsp%3B%26nbsp%3B%26nbsp%3B%26nbsp%3B%26nbsp%3B%26nbsp%3B%20ctx.AuthenticationMode%20%3D%20ClientAuthenticationMode.Anonymous%3B%3C%2FSTRONG%3E%3CBR%20%2F%3E%3CSTRONG%3E%26nbsp%3B%26nbsp%3B%26nbsp%3B%26nbsp%3B%26nbsp%3B%26nbsp%3B%26nbsp%3B%26nbsp%3B%26nbsp%3B%26nbsp%3B%26nbsp%3B%20ctx.FormDigestHandlingEnabled%20%3D%20false%3B%3C%2FSTRONG%3E%3CBR%20%2F%3E%3CSTRONG%3E%26nbsp%3B%26nbsp%3B%26nbsp%3B%26nbsp%3B%26nbsp%3B%26nbsp%3B%26nbsp%3B%26nbsp%3B%26nbsp%3B%26nbsp%3B%26nbsp%3B%20ctx.ExecutingWebRequest%20%2B%3D%20delegate%20(object%20sender%2C%20WebRequestEventArgs%20e)%7B%20%3C%2FSTRONG%3E%3CBR%20%2F%3E%3CSTRONG%3E%26nbsp%3B%26nbsp%3B%26nbsp%3B%26nbsp%3B%26nbsp%3B%26nbsp%3B%26nbsp%3B%26nbsp%3B%26nbsp%3B%26nbsp%3B%26nbsp%3B%26nbsp%3B%26nbsp%3B%26nbsp%3B%26nbsp%3B%20e.WebRequestExecutor.RequestHeaders%5B%22Authorization%22%5D%20%3D%20%22Bearer%20%22%20%2B%20accesstoken%3B%3C%2FSTRONG%3E%3CBR%20%2F%3E%3CSTRONG%3E%26nbsp%3B%26nbsp%3B%26nbsp%3B%26nbsp%3B%26nbsp%3B%26nbsp%3B%26nbsp%3B%26nbsp%3B%26nbsp%3B%26nbsp%3B%26nbsp%3B%20%7D%3B%3C%2FSTRONG%3E%3CBR%20%2F%3E%3CSTRONG%3E%26nbsp%3B%26nbsp%3B%26nbsp%3B%26nbsp%3B%26nbsp%3B%26nbsp%3B%26nbsp%3B%26nbsp%3B%26nbsp%3B%26nbsp%3B%26nbsp%3B%20Web%20web%20%3D%20ctx.Web%3B%3C%2FSTRONG%3E%3CBR%20%2F%3E%3CSTRONG%3E%26nbsp%3B%26nbsp%3B%26nbsp%3B%26nbsp%3B%26nbsp%3B%26nbsp%3B%26nbsp%3B%26nbsp%3B%26nbsp%3B%26nbsp%3B%26nbsp%3B%20ctx.Load(web)%3B%3C%2FSTRONG%3E%3CBR%20%2F%3E%3CSTRONG%3E%26nbsp%3B%26nbsp%3B%26nbsp%3B%26nbsp%3B%26nbsp%3B%26nbsp%3B%26nbsp%3B%26nbsp%3B%26nbsp%3B%26nbsp%3B%26nbsp%3B%20ctx.ExecuteQuery()%3B%3C%2FSTRONG%3E%3CBR%20%2F%3E%3CSTRONG%3E%26nbsp%3B%26nbsp%3B%26nbsp%3B%26nbsp%3B%26nbsp%3B%26nbsp%3B%26nbsp%3B%26nbsp%3B%26nbsp%3B%26nbsp%3B%26nbsp%3B%20log.Info(web.Id.ToString())%3B%3C%2FSTRONG%3E%3CBR%20%2F%3E%3CSTRONG%3E%26nbsp%3B%26nbsp%3B%26nbsp%3B%20%7D%3C%2FSTRONG%3E%3CBR%20%2F%3E%3CSTRONG%3E%7D%3C%2FSTRONG%3E%3C%2FDIV%3E%3CP%3E%26nbsp%3B%3C%2FP%3E%3CP%3EThe%20bearer%20token%20is%20generated%2C%20but%20requests%20fail%20with%20a%20401%20access%20denied%20(%3CSPAN%3Ereason%3D%22There%20has%20been%20an%20error%20authenticating%20the%20request.%22%3Bcategory%3D%22invalid_client%22%3C%2FSPAN%3E)%3C%2FP%3E%3CP%3E%26nbsp%3B%3C%2FP%3E%3CP%3EI%20have%20tried%20to%20change%20the%20audience%20to%26nbsp%3B00000003-0000-0ff1-ce00-000000000000%2F%3CTENANT%3E.sharepoint.com%40%3CTENANTID%3E%22%20but%20that%20gives%20a%20different%20401%20error%2C%20basically%20stating%20it%20cannot%20validate%20the%20audience%20uri.%20%3CSPAN%3E(%22error_description%22%3A%22Exception%20of%20type%20'Microsoft.IdentityModel.Tokens.AudienceUriValidationFailedException'%20was%20thrown.).%20I%20have%20also%20replace%20the%20CSOM%20call%20with%20a%20REST%20call%20mimicking%20the%20same%20call%20I%20did%20using%20the%20Logic%20App.%3C%2FSPAN%3E%3C%2FTENANTID%3E%3C%2FTENANT%3E%3C%2FP%3E%3CP%3E%26nbsp%3B%3C%2FP%3E%3CP%3EMy%20understanding%20of%20oauth%202%20is%20not%20good%20enough%20to%20understand%20why%20I'm%20running%20into%20an%20issue%20and%20where%20to%20look%20next.%3C%2FP%3E%3CP%3E%26nbsp%3B%3C%2FP%3E%3CP%3EWhy%20is%20the%20Logic%20App%20call%20using%20the%20HTTP%20action%20working%2C%20and%20why%20is%20the%20Function%20App%20not%20working%3F%3F%3C%2FP%3E%3CP%3E%26nbsp%3B%3C%2FP%3E%3CP%3EAnyone%3F%3C%2FP%3E%3CP%3E%26nbsp%3B%3C%2FP%3E%3C%2FLINGO-BODY%3E%3CLINGO-LABS%20id%3D%22lingo-labs-264651%22%20slang%3D%22en-US%22%3E%3CLINGO-LABEL%3EFunctions%3C%2FLINGO-LABEL%3E%3CLINGO-LABEL%3EMSI%3C%2FLINGO-LABEL%3E%3CLINGO-LABEL%3ESharePoint%20Online%3C%2FLINGO-LABEL%3E%3C%2FLINGO-LABS%3E%3CLINGO-SUB%20id%3D%22lingo-sub-269997%22%20slang%3D%22en-US%22%3ERe%3A%20Using%20a%20managed%20service%20identity%20to%20call%20into%20SharePoint%20Online.%20Possible%3F%3C%2FLINGO-SUB%3E%3CLINGO-BODY%20id%3D%22lingo-body-269997%22%20slang%3D%22en-US%22%3E%3CP%3EHi%2C%20looking%20at%20this%3A%26nbsp%3B%3CA%20href%3D%22https%3A%2F%2Fdocs.microsoft.com%2Fen-us%2Fazure%2Factive-directory%2Fmanaged-identities-azure-resources%2Fservices-support-msi%22%20target%3D%22_blank%22%20rel%3D%22noopener%20noreferrer%20noopener%20noreferrer%20noopener%20noreferrer%20noopener%20noreferrer%22%3Ehttps%3A%2F%2Fdocs.microsoft.com%2Fen-us%2Fazure%2Factive-directory%2Fmanaged-identities-azure-resources%2Fservices-support-msi%3C%2FA%3E%3C%2FP%3E%3CP%3E%26nbsp%3B%3C%2FP%3E%3CP%3EIt%20seems%20there%20isn't%20no%20support%20for%20o365%2Fspo%20identities%3C%2FP%3E%3CP%3E%26nbsp%3B%3C%2FP%3E%3CP%3E%26nbsp%3B%3C%2FP%3E%3C%2FLINGO-BODY%3E%3CLINGO-SUB%20id%3D%22lingo-sub-545648%22%20slang%3D%22en-US%22%3ERe%3A%20Using%20a%20managed%20service%20identity%20to%20call%20into%20SharePoint%20Online.%20Possible%3F%3C%2FLINGO-SUB%3E%3CLINGO-BODY%20id%3D%22lingo-body-545648%22%20slang%3D%22en-US%22%3EHi%20Mark%2C%3CBR%20%2F%3E%3CBR%20%2F%3EHow%20did%20you%20go%20with%20your%20spikes%20of%20SharePoint%20and%20Managed%20Identities%3F%3CBR%20%2F%3EAny%20white%20smoke%20scenarios%3F%3CBR%20%2F%3E%3CBR%20%2F%3ECheers%3C%2FLINGO-BODY%3E
Highlighted
New Contributor

Hi All,

 

I have been playing around with Managed Service Identity in Azure Logic Apps and Azure Function Apps. I think it is the best thing since sliced bread and am trying to enable various scenarios, one of which is using the MSI to get an app-only token and call into SharePoint Online.

 

Using Logic Apps, I generated a managed service identity for my app, and granted it Sites.readwrite.All on the SharePoint application. When then using the HTTP action I was able to call REST endpoints while using Managed Service Identity as Authentication and using https://<tenant>.sharepoint.com as the audience.

 

I then though I'd take it a step further and create a function app and follow the same pattern. I created the app, generated the MSI, added it the Sites.readwrite.All role same way I did with the Logic App.

 

I then used the code below to retrieve an access token and try and generate a clientcontext:

 

 

#r "Newtonsoft.Json"
using Newtonsoft.Json;
using System;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using Microsoft.SharePoint.Client;
public static void Run(string input, TraceWriter log)
{
    string resource = "https://<tenant>.sharepoint.com";
    string apiversion = "2017-09-01";
    using (var client = new HttpClient())
    {
        client.DefaultRequestHeaders.Add("Secret", Environment.GetEnvironmentVariable("MSI_SECRET"));
        var response = client.GetAsync(String.Format("{0}/?resource={1}&api-version={2}", Environment.GetEnvironmentVariable("MSI_ENDPOINT"), resource, apiversion)).Result;
            var responseContent = response.Content;
            string responseString = responseContent.ReadAsStringAsync().Result.ToString();
            var json = JsonConvert.DeserializeObject<dynamic>(responseString);
            string accesstoken = json.access_token.ToString()
            ClientContext ctx = new ClientContext("<siteurl>");
            ctx.AuthenticationMode = ClientAuthenticationMode.Anonymous;
            ctx.FormDigestHandlingEnabled = false;
            ctx.ExecutingWebRequest += delegate (object sender, WebRequestEventArgs e){
                e.WebRequestExecutor.RequestHeaders["Authorization"] = "Bearer " + accesstoken;
            };
            Web web = ctx.Web;
            ctx.Load(web);
            ctx.ExecuteQuery();
            log.Info(web.Id.ToString());
    }
}

 

The bearer token is generated, but requests fail with a 401 access denied (reason="There has been an error authenticating the request.";category="invalid_client")

 

I have tried to change the audience to 00000003-0000-0ff1-ce00-000000000000/<tenant>.sharepoint.com@<tenantid>" but that gives a different 401 error, basically stating it cannot validate the audience uri. ("error_description":"Exception of type 'Microsoft.IdentityModel.Tokens.AudienceUriValidationFailedException' was thrown.). I have also replace the CSOM call with a REST call mimicking the same call I did using the Logic App.

 

My understanding of oauth 2 is not good enough to understand why I'm running into an issue and where to look next.

 

Why is the Logic App call using the HTTP action working, and why is the Function App not working??

 

Anyone?

 

2 Replies
Highlighted
Highlighted
Hi Mark,

How did you go with your spikes of SharePoint and Managed Identities?
Any white smoke scenarios?

Cheers