0

We are trying to generate token to access SharePoint Online REST API using an app secured by AAD client ID and Client Secret. But getting unauthorized.

I see many articles saying either we have to use SharePoint Add-in method, SharePoint certificate or Graph API along with Client ID and Client Secret to access SharePoint.

My question is, can we make calls to SharePoint using SharePoint REST API in an app secured by Azure Active Directory using a Client ID, Client Secret and without certificate?

Appreciate your help. Thanks in advance.

Note: We do not want to use graph API/SharePoint Add-in.

1 Answer 1

1

I just tried this and it appears that the SharePoint REST API has the same restriction as the SharePoint Client Object Model for apps secured with Azure Active Directory, you must use a Client Id and Certificate rather than a Client Id and Client Secret to authenticate.

Here's what I did and the results I received.

I created an App Registration and granted it Sites.Read.All permission from the SharePoint API.

App registration configured permissions

I then created a new Client Secret and uploaded a certificate.

Client secret configuration

Certificate configuration

I then wrote a Console application with the following code.

using Microsoft.Identity.Client;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using System.Threading.Tasks;
using System.Web.Script.Serialization;

namespace SharePointRestApiTest
{
    class Program
    {
        static void Main(string[] args)
        {
            MakeCallToSharePoint().Wait();
        }

        private static string tenantName = "robwindsortest991";

        private async static Task<string> GetAccessTokenCertificate()
        {
            var clientId = "5ee8ea0a-d890-4ba4-8fd8-649ad874be3c";
            var authority = $"https://login.microsoftonline.com/{tenantName}.onmicrosoft.com/";

            var certName = "AzureFunctionAppTest.pfx";
            var certPassword = "[password]";
            var certPath = Path.Combine(@"E:\Certs\" + certName);
            var cert = new X509Certificate2(System.IO.File.ReadAllBytes(certPath),
                certPassword,
                X509KeyStorageFlags.Exportable |
                X509KeyStorageFlags.MachineKeySet |
                X509KeyStorageFlags.PersistKeySet);

            var azureApp = ConfidentialClientApplicationBuilder.Create(clientId)
                .WithAuthority(authority)
                .WithCertificate(cert)
                .Build();

            var scopes = new string[] { $"https://{tenantName}.sharepoint.com/.default" };
            var authResult = await azureApp.AcquireTokenForClient(scopes).ExecuteAsync();
            return authResult.AccessToken;
        }


        private static async Task<string> GetAccessTokenSecret()
        {
            var clientId = "5ee8ea0a-d890-4ba4-8fd8-649ad874be3c";
            var clientSecret = "[secret]";
            var authority = $"https://login.microsoftonline.com/{tenantName}.onmicrosoft.com/";
            var azureApp = ConfidentialClientApplicationBuilder.Create(clientId)
                .WithAuthority(authority)
                .WithClientSecret(clientSecret)
                .Build();

            var scopes = new string[] { $"https://{tenantName}.sharepoint.com/.default" };
            var authResult = await azureApp.AcquireTokenForClient(scopes).ExecuteAsync();
            return authResult.AccessToken;
        }

        private async static Task MakeCallToSharePoint()
        {
            var token = await GetAccessTokenSecret();

            var url = $"https://{tenantName}.sharepoint.com/sites/demo/" +
                "_api/Web/Lists";
            var result = string.Empty;

            using (var client = new HttpClient())
            {
                client.DefaultRequestHeaders.Clear();
                client.DefaultRequestHeaders.Add("Accept",
                    "application/json;odata=nometadata");
                client.DefaultRequestHeaders.Authorization =
                    new AuthenticationHeaderValue("Bearer", token);
                var response = await client.GetAsync(url);

                response.EnsureSuccessStatusCode();

                result = await response.Content.ReadAsStringAsync();
            }

            Console.WriteLine(result);
        }

    }
}

In the MakeCallToSharePoint method, if I get the token by calling GetAccessTokenSecret the code fails with this response.

Fail HTTP response

In the MakeCallToSharePoint method, if I get the token by calling GetAccessTokenCertificate the code runs successfully with this response.

Success HTTP response

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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