pnp
653 TopicsSharePoint Multi-geo: accessing a user's home-geo custom SharePoint properties from a different geo
Setup M365 tenant, multi-geo (NAM + EMEA). SharePoint custom user profile properties are synced only in the user’s home geo (their Preferred Data Location). SPFx webparts running on the NAM tenant, using PnP/SP or AadTokenProvider. Goal: when an EMEA user opens a NAM page, read their SharePoint custom properties from the EMEA tenant. Issue 1 — Can’t read profile properties from a different geo-tenant What I’m doing const sp = spfi(emeaTenantUrl).using(SPFx(webpartContext), SPFxToken(webpartContext)); const result = await sp.profiles.getPropertiesFor(basicInfo.data.LoginName); Error Access Denied: This application does not have the required permissions to access profile information. The core problem — token is missing my approved scopes Approved Scopes in webApiPermissionManagement (Office 365 SharePoint Online) in SharePoint Admin Center: Sites.Search.All, AllProfiles.Manage, User.Read, User.Read.All, Files.ReadWrite.All, TermStore.ReadWrite.All, Sites.ReadWrite.All, Sites.FullControl.All. Token issued for the SharePoint resource only contains: Files.ReadWrite.All, M365BillingPlatform.Read.All, Sites.FullControl.All, Sites.ReadWrite.All, TermStore.ReadWrite.All. So AllProfiles.Manage, User.Read.All, Sites.Search.All are not in the token and an unexpected M365BillingPlatform.Read.All is. Also tried calling the SharePoint REST API directly with a token from AadTokenProvider → same Access Denied. Questions Why is the issued token missing my approved scopes (and where is M365BillingPlatform.Read.All coming from)? Is there a supported way to read a user’s SharePoint profile custom properties from a different geo-tenant from an SPFx webpart? Approved Scopes in Admin center Issue 2 — Search query template {User.*} properties not resolving cross-geo What I’m doing A SharePoint Search query in the webpart uses a query template that pulls custom user properties dynamically: {|owstaxidmetadataalltagsinfo:{User.customProperty1}} OR {|owstaxidmetadataalltagsinfo:{User.customProperty2}} The SP object is created the same way as in Issue 1. What’s not working When an EMEA user loads the webpart on the NAM tenant, the {User.*} tokens don’t resolve — the query modification shows the static property name itself instead of the user’s values from EMEA. Question How do I get the query template to resolve {User.customProperty} using the user’s home-geo (EMEA) profile values?8Views0likes0CommentsRest API Error:"The parameter Members does not exist in method AddTenantTheme"
Using the following code. $colorPairs = @{ "themePrimary" = "#003538"; "themeLighterAlt" = "#cbe6e7"; "themeLighter" = "#a1cfd1"; "themeLight" = "#7cb8bb"; "themeTertiary" = "#5ba2a5"; "themeSecondary" = "#3f8c90"; "themeDarkAlt" = "#28767a"; "themeDark" = "#166064"; "themeDarker" = "#094b4e"; "neutralLighterAlt" = "#f8f8f8"; "neutralLighter" = "#f4f4f4"; "neutralLight" = "#eaeaea"; "neutralQuaternaryAlt" = "#dadada"; "neutralQuaternary" = "#d0d0d0"; "neutralTertiaryAlt" = "#c8c8c8"; "neutralTertiary" = "#98a9da"; "neutralSecondary" = "#4863b4"; "neutralPrimaryAlt" = "#113191"; "neutralPrimary" = "#002081"; "neutralDark" = "#001963"; "black" = "#001249"; "white" = "#ffffff"; } $content = @{ "name" = "MyCustomTheme" "themeJson" = $jsonBody } | ConvertTo-Json $apiUrl = "https://<<domain>>-admin.sharepoint.com/_api/thememanager/AddTenantTheme" Invoke-PnPSPRestMethod -Url $apiUrl -Method "Post" -Content $content Tried many different combinations but kept getting Invoke-PnPSPRestMethod: {"odata.error":{"code":"-1, Microsoft.SharePoint.Client.InvalidClientQueryException","message":{"lang":"en-US","value":"The parameter Members does not exist in method AddTenantTheme."}}} Wondering if somebody can run this code successfully and let me know what I am missing.30Views0likes2CommentsSPFx CDN URLs not consistently added to Trusted Script Sources (CSP)
Hi everyone, We’re currently investigating an issue related to SharePoint Online Content Security Policy (CSP) and Trusted Script Sources (TSS) for SPFx solutions. [SharePoint Online] [SPFx] [CSP] Scenario We deployed multiple SPFx .sppkg packages via App Catalog These solutions load scripts from an external CDN (cdnBasePath, with includeClientSideAssets: false) Expected Behavior As per documentation, CDN URLs used by SPFx solutions should be available/registered in Trusted Script Sources so that scripts can load under CSP enforcement Observed Behavior Only some CDN URLs (from certain packages) appear in Trusted Script Sources Others are missing, even though they are similarly configured and deployed Due to this, scripts from those missing sources are blocked by CSP, and the extension fails to load Additional Notes No use of eval() or inline scripts in our code Re-deploying packages sometimes resolves the issue (CDN URLs get registered afterward) Behavior appears inconsistent across environments Question Has anyone encountered a similar issue where: Trusted Script Sources were partially auto-populated from App Catalog deployments? CDN URLs from some SPFx packages were not registered automatically? Any insights on: Root cause Known limitations Best practices to ensure consistent registration would be really helpful. Thanks in advance!143Views1like1CommentIs Creating Multiple ClientContext Instances Efficient in CSOM for one Site Url?
Is it efficient to create a new ClientContext for each site URL and for each query, such as getting site users, lists, groups, and group members from the Web object? These queries can return a very large number of items, and CSOM does not support paging when retrieving data from Web properties. Because of this, there is a risk of memory issues. In this case, is creating multiple ClientContext objects a good approach? Also, how does ClientContext work internally, and how does it handle memory and performance?36Views0likes0Commentsany possible to extend client ID and secret for app registered in SharePoint Online
Hi Team, is there any possible to extend client ID and secret for app registered in SharePoint Online? right now, we are not allowed to create any new app registration from SharePoint Online, only way is from Azure AD - App registration. how about the client ID and secret getting expired which is created earlier using SharePoint Online? is there any way like powershell script something to extend the validity of Client ID and secret for app registered in SharePoint Online? if yes, please share the detailed instructions and guidelines to perform. if no, then what is the possible way for next step of actions to do? you can reachout to me - mailto:email address removed for privacy reasons for further discussion about this issue. Thanks for the help!SPFX 1.21.1 in monorepo - utility module could not be exported
I am trying to migrate multiple projects to monorepo. I need to migrate some common utilities functions in a common module. There is an export warning that the module has no exports, while it does has. Running npm run spfx1:build works fine. But when I do npm run spfx1:serve it throws a warning export 'DemoUtilities' (imported as 'DemoUtilities') was not found in 'utilities-library/utils' (module has no exports) Opening the a SharePoint page online, it errors with missing exports. Can you point me to possible solutions? Here is a very simple OOB SPFX solution structure: utilities-library - src -- utils.ts - index.ts - package.json - tsconfig.json spfx1 - config - sharepoint - src -- skipped for brevity - gulpfile.js - package.json - tsconfig.json package.json tsconfig.base.json Specifics for per files utilities-library -> package.json { "name": "utilities-library", "version": "1.0.0", "main": "lib/index.js", "types": "lib/index.d.ts", "scripts": { "start": "node index.js", "test": "echo \"test\"" }, "type": "module", "workspaces": [ "true" ], "devDependencies": { "typescript": "^5.3.5" } } utilities-library ->tsconfig.json { "compilerOptions": { "composite": true, "declaration": true, "outDir": "lib" }, "include": ["**/*.ts"] } utilities-library ->index.ts export { DemoUtilites } from './src/utils'; utilities-library -> utils.ts import { ISPFXContext, spfi, SPFx } from "@pnp/sp"; import "@pnp/sp/webs"; import "@pnp/sp/lists"; export const DemoUtilites = { checkListExists: async ( listTitle: string, context: ISPFXContext ): Promise<boolean> => { const sp = spfi().using(SPFx(context)); return new Promise<boolean>((resolve) => { sp.web.lists .getByTitle(listTitle)() .then(() => { resolve(true); }) .catch((error) => { if (!(error?.status === 404)) { console.log(`Error checking list ${listTitle}`, error); } resolve(false); }); }); } } spfx1 -> gulpfile.js 'use strict'; const build = require('@microsoft/sp-build-web'); const path = require("path"); build.addSuppression(`Warning - [sass] The local CSS class 'ms-Grid' is not camelCase and will not be type-safe.`); build.configureWebpack.mergeConfig({ additionalConfiguration: (generatedConfig) => { generatedConfig.resolve = generatedConfig.resolve || {}; generatedConfig.resolve.alias = { ...(generatedConfig.resolve.alias || {}), 'utilities-library': path.resolve(__dirname, '../utilities-library/lib'), 'utilities-library/utils': path.resolve(__dirname, '../utilities-library/lib/utils') }; return generatedConfig; } }); var getTasks = build.rig.getTasks; build.rig.getTasks = function () { var result = getTasks.call(build.rig); result.set('serve', result.get('serve-deprecated')); return result; }; build.initialize(require('gulp')); spfx1 -> package.json { ... "dependencies": { "@microsoft/decorators": "1.21.1", "@microsoft/sp-core-library": "1.21.1", "@microsoft/sp-dialog": "1.21.1", "@microsoft/sp-listview-extensibility": "1.21.1", "@pnp/sp": "^4.12.0", "utilities-library": "^1.0.0", "office-ui-fabric-react": "^7.204.1", "react": "17.0.1", "react-dom": "17.0.1", "tslib": "2.3.1" }, ... } spfx1 -> tsconfig.json { "extends": "../tsconfig.base.json", "compilerOptions": { "rootDir": "src", "outDir": "lib" }, "include": ["src/**/*.ts", "src/**/*.tsx"], "references": [ { "path": "../utilities-library" } ] } root -> package.json { "name": "monorepo-test", "workspaces": [ "spfx1", "utilities-library" ], "scripts": { "spfx1:build": "gulp build -f ./spfx1/gulpfile.js --workspace spfx1", "spfx1:clean": "gulp clean -f ./spfx1/gulpfile.js --workspace spfx1", "spfx1:serve": "gulp serve -f ./spfx1/gulpfile.js --workspace spfx1 --nobrowser", "spfx1:bundle": "gulp bundle -f ./spfx1/gulpfile.js --workspace spfx1", "spfx1:pkg": "gulp package-solution -f ./spfx1/gulpfile.js --workspace spfx1", "spfx1:prod": "gulp build -f ./spfx1/gulpfile.js --workspace spfx1 --ship & gulp bundle -f ./spfx1/gulpfile.js --workspace spfx1 --ship & gulp package-solution -f ./spfx1/gulpfile.js --workspace spfx1 --ship", "utilities-library:build": "tsc --project utilities-library/tsconfig.json", "utilities-library:watch": "npm run utilities-library:build -- --watch", "clean:node": "rm -rf **/node_modules" }, "devDependencies": { "npm-run-all": "^4.1.5" } } root -> tsconfig.base.json { "extends": "./node_modules/@microsoft/rush-stack-compiler-5.3/includes/tsconfig-web.json", "compilerOptions": { "target": "es5", "forceConsistentCasingInFileNames": true, "module": "esnext", "moduleResolution": "node", "jsx": "react", "declaration": true, "sourceMap": true, "experimentalDecorators": true, "skipLibCheck": true, "outDir": "lib", "inlineSources": false, "noImplicitAny": true, "rootDir": ".", "baseUrl": ".", // Important for path mapping "paths": { "utilities-library/*": [ "utilities-library/lib/*" ] }, "typeRoots": [ "./node_modules/@types", "./node_modules/@microsoft" ], "types": [ "webpack-env" ], "lib": [ "es5", "dom", "es2015.collection", "es2015.promise" ] }, "include": [ "src/**/*.ts", "src/**/*.tsx" ] }274Views0likes2CommentsFunction of "GET IT" button in Sharepoint app?
Hello, I have a question related to Sharepoint deployment. Context: I have two Sharepoint sites. Both have the same webpart application but different version. But they both display the lastest version of the webpart. Questions: - What the function of the "GET IT" button in details of the application if both running different version and still have same webpart? - How can I deploy different version to each site because I plan to have two sites DEV and PRO? Thank in advance.Solved112Views0likes1CommentSharepoint Online: fastest way to retrieve folder count in a document library
Hi all I'm really looking for what the title says. Right now I'm doing Get-PnPFolderItem -ItemType Folder (using Pnp.Powershell but can try other options) but with more than a few 10s of folders, this is REALLY slow. The thing is, I just need a count (recursively), not all info as I'm getting with Get-PnPFolderItem. Any ideas?Solved529Views0likes1Comment401 unauthorised for ExecuteQuery in sharepoint CSOM
Hi, I am trying to connect the sharepoint site with client id and secret but getting 401 unauthroised error while hitting the executequery() method. While doing app registrations both Microsoft graph and share point API permissions with full site control has been given including trusted the app through appinv.aspx. Still getting 401 unauthorised error. Since ACS is retiring, do we need to follow any other permissions for share point level site access. The same execute query is working fine for client id, certificate combination. But not working for client id and secret. static void Main(string[] args) { var authManager = new AuthenticationManager("***************************", "C:\\Program Files\\OpenSSL-Win64\\bin\\certificate.pfx", "*******", "********.onmicrosoft.com"); using (var cc = authManager.GetContext("https://****.sharepoint.com/sites/****")) { cc.Load(cc.Web, p => p.Title); cc.ExecuteQuery(); Console.WriteLine(cc.Web.Title); ListCollection listCollection = cc.Web.Lists; cc.ExecuteQuery(); // this is working fine }; // Replace with your SharePoint Online details string siteUrl = "****************************"; string tenantId = "***************************"; string clientId = "********************************"; string clientSecret = "******************************"; // App secret try { using (var context = GetClientContextWithOAuth(siteUrl, tenantId, clientId, clientSecret)) { // Example: Retrieve web title Web web = context.Web; context.Load(web, w => w.Title); context.ExecuteQuery(); // this is throwing 401 unauthorized error Console.WriteLine("Connected to: " + web.Title); } } catch (Exception ex) { Console.WriteLine("Error: " + ex.Message); } } private static ClientContext GetClientContextWithOAuth(string siteUrl, string tenantId, string clientId, string clientSecret) { // Azure AD OAuth 2.0 endpoint string authority = $"https://login.microsoftonline.com/*******************"; // Use MSAL to acquire an access token var app = ConfidentialClientApplicationBuilder.Create(clientId) .WithClientSecret(clientSecret) .WithAuthority(new Uri(authority)) .Build(); var authResult = app.AcquireTokenForClient(new[] { $"{siteUrl}/.default" }).ExecuteAsync().Result; if (authResult == null) { throw new Exception("Failed to acquire the access token."); } // Use the access token to authenticate the ClientContext var context = new ClientContext(siteUrl); context.ExecutingWebRequest += (sender, e) => { e.WebRequestExecutor.WebRequest.Headers["Authorization"] = "Bearer " + authResult.AccessToken; }; return context; }389Views0likes0Comments