I am able to retrieve and use an access token via MSAL in a desktop .Net project. I can successfully retrieve tokens and they are valid in my Graph calls.
However, trying to use the access token with SharePoint Online CSOM results in a 401: Unauthorized. This is similar to accessing sharepoint REST apis using msal throws 401 (except I am using C# and the latest CSOM).
It is my understanding that MSFT is trying to move devs away from ADAL and towards MSAL, but there seems to be some compatibility issue with the tokens.
Has anyone been able to specify the necessary scopes and leverage an OAuth token with bearer authorization from MSAL to access SharePoint Online?
You can use the SharePoint client object model (CSOM) to retrieve, update, and manage data in SharePoint. SharePoint makes the CSOM available in several forms: . NET Framework redistributable assemblies.
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).
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)
Then, you have to upload the certificate in your AAD Application (the ".cer" file):
After that, you have to authorize the SharePoint APIs:
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)
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);
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;
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)
Not sure why are you using MSAL directly to access SharePoint Online API. The easiest way should be using Microsoft.SharePointOnline.CSOM
var clientContext = new ClientContext(siteUrl);
clientContext.Credentials = new SharePointOnlineCredentials(userName, securedPassword);
and you are done with CSOM API.
But I think that it should be possible with MSAL as well.
Keep in mind that token that you acquire is granted for some resource. Copy parse your token to http://jwt.ms/ and take a look at aud
value
if this field contains https://graph.microsoft.com
it just cannot be valid for you https://yourtenant.sharepoint.com
and you need different one!
You should be able request correct token using Microsoft.Identity.Client
var authority = $"https://login.microsoftonline.com/{tenantId}";
var app = ConfidentialClientApplicationBuilder.Create(clientId)
.WithClientSecret(clientSecret)
.WithAuthority(new Uri(authority))
.Build();
var scopes = new[] { "https://yourtenant.sharepoint.com/.default" };
var authenticationResult = await app.AcquireTokenForClient(scopes)
.ExecuteAsync();
As a result authenticationResult.AccessToken
should contain access token valid for SharePoint Online REST API. (https://yourtenant.sharepoint.com/.default
means that you requests all scopes configured for your Azure AD application)
When you decode new jet token it should looks like this
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With