pnp / pnpjs

Fluent JavaScript API for SharePoint and Microsoft Graph REST APIs
https://pnp.github.io/pnpjs/
Other
753 stars 305 forks source link

Graph permissions required to access SharePoint REST API? #2979

Closed gruering closed 5 months ago

gruering commented 5 months ago

What version of PnPjs library you are using

3.x

Minor Version Number

3.23.0

Target environment

All

Additional environment details

Accessing users personal site from an SPFx WebPart on another site and thus from a different base url (-my)

Question/Request

This is a follow-up question to my original question https://github.com/pnp/pnpjs/issues/2966 that was already answered by @patrick-rodgers and @juliemturner . As already mentioned updating my code to use the current context besides the bearer token to create a new spfi works: const mySp = spfi(personalUrl.href).using(SPFx(context), BearerToken(token));

What still irritates me, however, is that I need authorizations on the graph for this. Although I am only using the SharePoint endpoint, my code only works when adding the permissions scopes to the graph. If I do not add the scopes, they are also missing on the bearer token and the calls to the SharePoint sites fail with 403.

As it is normally not needed to add graph permissions to access SharePoint endpoints, I wanted to make sure if this is really the right way to do it.

Please let me know if there is a simpler way to access the users personal site without needing additional permissions scopes on the graph.

Thank you for your support.

juliemturner commented 5 months ago

That doesn't really make any sense, unless you're making graph calls you don't need graph permissions...

If you want to share code that provides a full picture from creating the SPFI object through calls that are failing I can see if anything jumps out.

patrick-rodgers commented 5 months ago

Can you share the scopes and calls you are making?

gruering commented 5 months ago
      const token = await provider.getToken(`${personalUrl.protocol}//${personalUrl.hostname}`, true);   

      // create new context with personal site token
      const mySp = spfi(personalUrl.href).using(SPFx(context?.sharePointContext), BearerToken(token));

      // check if the list was created, or if it already existed
      const sfMyReservations = await mySp.web.lists.ensure(
         "sfMyReservations", "Reservation List", 100, false, { Hidden: true }
      )

I get a 403 error when trying to create a list without setting the following scopes on the graph: Sites.Manage.All, Sites.ReadWrite.All After adding the scopes to code works. (Please note that this issue only occurs when accessing the mysite from another site which is the reason why I am using the bearer token)

patrick-rodgers commented 5 months ago

What is provider.getToken? That doesn't appear to be part of our library.

juliemturner commented 5 months ago

So I'm guessing somewhat because I don't have access to your package.json but the first line is

const token = await provider.getToken(${personalUrl.protocol}//${personalUrl.hostname}, true);

Are you by chance using the Microsoft Graph Toolkit? That's the only thing I can think of that uses the term 'provider'...

gruering commented 5 months ago

const provider = await context.aadTokenProviderFactory.getTokenProvider();

Sorry, I did not post all the code. No, I am not using the Graph Tookit. I am just using the aadTokenProvider of the context.

gruering commented 5 months ago

here you have all the lines together...

      const personalUrl = new URL(profile.PersonalUrl);

      // get a bearer token from the personal site url
      const provider = await context.aadTokenProviderFactory.getTokenProvider();
      const token = await provider.getToken(`${personalUrl.protocol}//${personalUrl.hostname}`, true);    

      // create new context with personal site token
      const mySp = spfi(personalUrl.href).using(SPFx(context), BearerToken(token));

      // check if the list was created, or if it already existed
      const sfMyReservations = await mySp.web.lists.ensure(
         "sfMyReservations", "Reservation List", 100, false, { Hidden: true }
      )

context is from the WebPart

juliemturner commented 5 months ago

Do you have anything in your package-solution.json file under "webApiPermissionRequests"?

gruering commented 5 months ago

yes, the already mentioned permissions:

    "webApiPermissionRequests": [
      {
        "resource": "Microsoft Graph",
        "scope": "Sites.ReadWrite.All"
      },      
      {
        "resource": "Microsoft Graph",
        "scope": "Sites.Manage.All"
      }
    ], 
juliemturner commented 5 months ago

yes, the already mentioned permissions:

Yes but you didn't share them... and the train of things hasn't been super clear, I'm trying to remote debug your solution via asynchronous posts...

So what I think you're saying is if you remove those requests and then rebuild/redeploy the code the ensures the lists fails with a 403?

gruering commented 5 months ago

I have created a demo sample for the issue and uploaded it to my github repository: https://github.com/gruering/MySiteSample/blob/main/src/webparts/mySiteSample/MySiteSampleWebPart.ts

It is a simple WebPart that tries to create a list in the MySite and write an entry if available. The sample does not contain the scopes in webApiPermissionRequests. If you try it on a tenant without these permissions on the "SharePoint Online Client Extensibility Web Application Principal" it will result in 403 errors.

If you add Sites.Manage.All and Sites.ReadWrite.All to the graph (and after a short delay) the solutions works.

bcameron1231 commented 5 months ago

So this is an interesting scenario where when you request a token on the Personal Sites, either the Graph scope or a SharePoint scope actually works (this is a bug in my opinion, cc: @patrick-rodgers). I've seen this in practice and have reproduced it myself.

However, because you're not calling Graph APIs, I would remove the Graph API scope if you don't need it. What you would need to add to the SharePoint Online Client Extensibility Principal would be the SharePoint AllSites.Manage permission to do what you're trying to achieve.

gruering commented 5 months ago

Many thanks to all of you for your feedbacks @patrick-rodgers, @juliemturner, @bcameron1231. I was able to test the code successfully with the SharePoint "AllSites.Manage" permission. I will remove the graph permissions and use those. Makes much more sense now :smile: I thought I had already tested these, but I probably didn't wait long enough for the token to get the permissions.

github-actions[bot] commented 5 months ago

This issue is locked for inactivity or age. If you have a related issue please open a new issue and reference this one. Closed issues are not tracked.