pnp / pnpjs

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

accessing users personal site from another site collection results in 403 #2966

Closed gruering closed 3 months ago

gruering commented 3 months ago

What version of PnPjs library you are using

3.x

Minor Version Number

3.23.0

Target environment

SharePoint Framework

Additional environment details

I'm using the webpart context in a site to access the users personal site. Also tried adding the bearer token myself with behaviour with no luck.

Question/Request

Accessing the current users personal site (aka MySite) from a webpart on another site results in a 403 authorization error. I think the reason is the different base url (-my.sharepoint.com) that prevents adding the authentication cookies to the request because they would cross the domain. Using the BearerToken Queryable didn't help either. So far the only way to successfully access the MySite from another domain was by using the graph with the users personal site id or also by using the AadHttpClient and REST.

However, I would prefer using sp.web.lists etc. because working with list items via graph is not very convenient... Is there any way I can get to make this work with pnp js?

patrick-rodgers commented 3 months ago

Can you share your code that is not working when you say "Using the BearerToken Queryable didn't help either."?

Yes, it would be two different token audiences (-my and not -my) so you would need two tokens.

gruering commented 3 months ago
      // get a bearer token from the personal site url
      const provider = await context?.sharePointContext.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(BearerToken(token));

      const listData: any[] = await mySp.web.lists.getByTitle("sfMeetingNotes").items.select("Title", "Modified").filter(`Title eq '${context?.meetingTitle}'`)();
patrick-rodgers commented 3 months ago

That looks fine, what is the error you are getting? What permissions does your application have? If you open the token what are the listed scopes?

gruering commented 3 months ago

I do not see any error. The rest call to the list never happens. Token should be okay because I can successfully access the rest endpoint by using AadHttpClient. Scopes: Channel.ReadBasic.All Directory.Read.All Group.ReadWrite.All GroupMember.Read.All Sites.Manage.All Sites.ReadWrite.All Tasks.ReadWrite TeamMember.ReadWrite.All User.Read.All User.ReadBasic.All

patrick-rodgers commented 3 months ago

If the call never happens then I assume you've not added any additional behaviors. Here is an example of using the default behavior with SPFx.

import { spfi, SPFx } from "@pnp/sp";

protected async onInit(): Promise<void> {

    await super.onInit();
    const sp = spfi().using(SPFx(this.context));
}

In your case that line would be:

const sp = spfi().using(SPFx(this.context), BearerToken(token));
gruering commented 3 months ago

Oh, I did not expect that I need to pass the context from the current web when using another token, but it works! Many thanks for your support and the great work you are doing!

juliemturner commented 3 months ago

You don't actually need to pass the context that's just the simplest example that get's you where you want to go. Behind the scenes what's actually happening is that you're getting the "default behaviors" that are added to the timeline when you use the SPFx behavior. So, it's setting up the pipeline for making the calls... If you want to actually understand, check out the documentation on behaviors, and specifically SharePoint queryable behaviors

github-actions[bot] commented 3 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.