Forum Discussion
SharePoint Online REST API using Azure Function Managed Identity
I have created an Azure Function which uses .NET Core 8.0. and i enabled its managed identity:-
also i accessed the azure function from "Enterprise Application", and i copied its AppID:-
Then i run those commands as per this official documentation for Microsft @ https://learn.microsoft.com/en-us/sharepoint/dev/apis/webhooks/sharepoint-webhooks-using-azd-template#grant-the-function-app-access-to-sharepoint-online :-
# This script requires the modules Microsoft.Graph.Authentication, Microsoft.Graph.Applications, Microsoft.Graph.Identity.SignIns, which can be installed with the cmdlet Install-Module below:
# Install-Module Microsoft.Graph.Authentication, Microsoft.Graph.Applications, Microsoft.Graph.Identity.SignIns -Scope CurrentUser -Repository PSGallery -Force
Connect-MgGraph -Scope "Application.Read.All", "AppRoleAssignment.ReadWrite.All"
$managedIdentityObjectId = "******" # 'Object (principal) ID' of the managed identity
$scopeName = "Sites.Selected"
$resourceAppPrincipalObj = Get-MgServicePrincipal -Filter "displayName eq 'Office 365 SharePoint Online'" # SPO
$targetAppPrincipalAppRole = $resourceAppPrincipalObj.AppRoles | ? Value -eq $scopeName$appRoleAssignment = @{
"principalId" = $managedIdentityObjectId
"resourceId" = $resourceAppPrincipalObj.Id
"appRoleId" = $targetAppPrincipalAppRole.Id
}
New-MgServicePrincipalAppRoleAssignment -ServicePrincipalId $managedIdentityObjectId -BodyParameter $appRoleAssignment | Format-List
and this command, on the Analytics site:-
Connect-PnPOnline -Url "https://YOUR_SHAREPOINT_TENANT_PREFIX.sharepoint.com/sites/analytics" -Interactive -ClientId "YOUR_PNP_APP_CLIENT_ID"
Grant-PnPAzureADAppSitePermission -AppId "****" -DisplayName "YOUR_FUNC_APP_NAME" -Permissions Manage
Everything went well, then I verify the above for the Analytics site, as follow:-
here is my code inside Azure Function, to get the items inside a list named "Call Transfer Log Data":-
accessToken = await GetJwtTokenUsingSystemManagedIdentity();
try
{string siteUrl = "https://***.sharepoint.com/sites/analytics";
string listName = "Call Transfer Log Data";
string tenant = "****";
string site = "analytics";
string listTitle = "Call Transfer Log Data";
siteUrl = $"https://{tenant}.sharepoint.com/sites/{site}";
string apiBaseUrl = $"{siteUrl}/_api/web/lists/GetByTitle('{listTitle}')/items";
var httpClient = new HttpClient();
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
httpClient.DefaultRequestHeaders.Accept.ParseAdd("application/json;odata=verbose");
string filterDate = DateTime.UtcNow.AddDays(-120).ToString("yyyy-MM-ddTHH:mm:ssZ");
string requestUrl = $"{apiBaseUrl}?$filter=Modified ge datetime'{filterDate}'&$top=100&$orderby=Modified desc";bool hasMore = true;
int page = 1;
List<CallTransferLogData> responseContent = new List<CallTransferLogData>();
while (hasMore)
{
Console.WriteLine($"Fetching page {page}...");
var request = new HttpRequestMessage(HttpMethod.Get, requestUrl);
var response = await httpClient.SendAsync(request);
Console.WriteLine("Raw response ");
Console.WriteLine(response);
string content = await response.Content.ReadAsStringAsync();using JsonDocument doc = JsonDocument.Parse(content);
Console.WriteLine($"Parse doc {page}...");
Console.WriteLine("Raw response content:");
Console.WriteLine(content);
var root = doc.RootElement.GetProperty("d");
Console.WriteLine($"Building Root {page}...");
// Process results
foreach (var item in root.GetProperty("results").EnumerateArray())
{//code goes here;;
}//end of tryprivate static async Task<string> GetJwtTokenUsingSystemManagedIdentity()
{
string resource = "https://****.sharepoint.com/.default";
var credential = new DefaultAzureCredential();
var tokenRequestContext = new TokenRequestContext(new[] { resource });
var token = await credential.GetTokenAsync(tokenRequestContext);
Console.WriteLine("Toekn is " + token.Token)
; return token.Token;
}
but the content will be {"error_description":"ID3035: The request was not valid or is malformed."}
the full response will be:-
2025-04-22T16:31:38Z [Information] StatusCode: 401, ReasonPhrase: 'Unauthorized', Version: 1.1, Content: System.Net.Http.HttpConnectionResponseContent, Headers:
2025-04-22T16:31:38Z [Information] {
2025-04-22T16:31:38Z [Information] Cache-Control: private
2025-04-22T16:31:38Z [Information] Server: Microsoft-IIS/10.0
2025-04-22T16:31:38Z [Information] X-NetworkStatistics: 0,********0
2025-04-22T16:31:38Z [Information] x-ms-diagnostics: 3001000;reason="There has been an error authenticating the request.";category="invalid_client"
2025-04-22T16:31:38Z [Information] IsOCDI: 0
2025-04-22T16:31:38Z [Information] X-DataBoundary: NONE
2025-04-22T16:31:38Z [Information] X-1DSCollectorUrl: https://mobile.events.data.microsoft.com/OneCollector/1.0/
2025-04-22T16:31:38Z [Information] X-AriaCollectorURL: https://browser.pipe.aria.microsoft.com/Collector/3.0/
2025-04-22T16:31:38Z [Information] SPRequestGuid: 4***2
2025-04-22T16:31:38Z [Information] request-id: 4***2
2025-04-22T16:31:38Z [Information] MS-CV: o****/Q8g.0
2025-04-22T16:31:38Z [Information] SPRequestDuration: 56
2025-04-22T16:31:38Z [Information] SPIisLatency: 2
2025-04-22T16:31:38Z [Information] X-Powered-By: ASP.NET
2025-04-22T16:31:38Z [Information] MicrosoftSharePointTeamServices: 16.0.0.26002
2025-04-22T16:31:38Z [Information] X-Content-Type-Options: nosniff
2025-04-22T16:31:38Z [Information] X-MS-InvokeApp: 1; RequireReadOnly
2025-04-22T16:31:38Z [Information] P3P: CP="ALL ****"
2025-04-22T16:31:38Z [Information] WWW-Authenticate: Bearer realm="e****20",client_id="00000003-******00-000000000000",trusted_issuers="00000001-0000-0000-c000-000000000000@*,D****@*,https://sts.windows.net/*/,https://login.microsoftonline.com/*/v2.0,00000003-0000-0ff1-ce00-000000000000@***b",authorization_uri="https://login.microsoftonline.com/common/oauth2/authorize"
2025-04-22T16:31:38Z [Information] Date: Tue, 22 Apr 2025 16:31:36 GMT
2025-04-22T16:31:38Z [Information] Content-Length: 74
2025-04-22T16:31:38Z [Information] }
Also when i decode the token, i got those valid claims for all the properties.. So why the code is not working? Thanks