问题
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?
回答1:
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|employee.mail@tenant.onmicrosoft.com");
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)
回答2:
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
来源:https://stackoverflow.com/questions/54771270/msal-ad-token-not-valid-with-sharepoint-online-csom