As far as I know, you can't request SharePoint REST API with a Client / Secret Token through AAD. But it works with a certificate. Below a step by step:
As I was facing the same problems, I'll post here how I managed to connect to SharePoint API through AAD Application with MSAL (OAuth v2 and AAD v2 endpoint). It's in C#.
First, I only succeeded with a certificate (the client / secret method doesn't work as far as I know).
Create certificate
For testing purposes, I've created a self-signed certificate with the "New-PnPAzureCertificate" like this :
$secPassword = ConvertTo-SecureString -String "MyPassword" -AsPlainText -Force
$cert = New-PnPAzureCertificate -OutCert "CertSPUser.cer" -OutPfx "CertSPUser.pfx" -ValidYears 10 -CertificatePassword $secPassword -CommonName "CertSPUser" -Country "FR" -State "France"
(The -Country
and the -State
parameters doesn't matter for the test)
(It also works with the New-SelfSignedCertificate command)
Register certificate
Then, you have to upload the certificate in your AAD Application (the ".cer" file):
Configure Application API Permissions
After that, you have to authorize the SharePoint APIs:
Try the access through Daemon App (C#)NuGet Packages (hope I forgot nothing)
- Microsoft.SharePointOnline.CSOM
- Microsoft.Identity.Client
To make things work, you have to it in 3 steps (I've simplified it, but you better separate the actions into methods with some try/catch)
Get Pfx Certificate
For this step, I highly recommand to use a KeyVault (see links on the bottom of the post)
string certPath = System.IO.Path.GetFullPath(@"C:\PathTo\CertSPUser.pfx");
X509Certificate2 certificate = new X509Certificate2(certPath, "MyPassword", X509KeyStorageFlags.MachineKeySet);
Get Token
string tenantId = "yourTenant.onmicrosoft.com" // Or "TenantId"
string applicationId = "IdOfYourAADApp"
IConfidentialClientApplication confApp = ConfidentialClientApplicationBuilder.Create(applicationId)
.WithAuthority($"https://login.microsoftonline.com/{tenantId}")
.WithCertificate(certificate)
.Build();
string sharePointUrl = "https://yourSharePoint.sharepoint.com" // Or "https://yourSharePoint-admin.sharepoint.com" if you want to access the User Profile REST API
var scopes = new[] { $"{sharePointUrl}/.default" };
var authenticationResult = await confApp.AcquireTokenForClient(scopes).ExecuteAsync();
string token = authenticationResult.AccessToken;
Test your connection
ClientContext ctx = new ClientContext(sharePointUrl);
ctx.ExecutingWebRequest += (s, e) =>
{
e.WebRequestExecutor.RequestHeaders["Authorization"] = "Bearer " + token;
};
Web web = ctx.Web;
ctx.Load(web);
ctx.Load(web);
ctx.ExecuteQuery();
// OR if you connect to User Profile ("yourSharePoint-admin.sharepoint.com")
/*
PeopleManager peopleManager = new PeopleManager(ctx);
var personProperties = peopleManager.GetUserProfileProperties("i:0#.f|membership|[email protected]");
ctx.ExecuteQuery();
*/
And if I didn't miss anything, you should get some Web / User Info ! 😉
Hope that it helps.
Edit (11/14/2020) : Even if in my screen capture on the API Permissions I've added the Application Permission "User.ReadWrite.All", if you try to update the User Profile, it won't work. To solve this, you have to register your AAD Application as a legacy SharePoint App-Only principal (Client ID / Secret). More info here.
Thanks to @laurakokkarinen and @mmsharepoint for their articles that really helped me (here and here)