Authenticated Copilot Studio agents on any SharePoint site
Tutorial, use cases and test scenarios for publishing a Copilot Studio agent through the Microsoft 365 Agents SDK - one SharePoint web part for the whole tenant, single sign-on with Microsoft Entra ID, and Copilot Studio sharing as the access control. Built and tested end to end in a live Microsoft demo tenant.
How the Agents SDK web part works
The page talks to Copilot Studio directly with the user's own Entra ID token - no host site, no hidden files, no anonymous path.
What you get
- One SPFx package and one Entra SPA app for every site in the tenant.
- Silent single sign-on with the SharePoint session, including MFA; one-click fallback when a browser blocks it.
- The agent knows who is chatting (
System.User.DisplayNameand more). - Copilot Studio sharing enforced on every site; changes apply on the next page load.
- Inline, themable chat; several agents per page; no Microsoft 365 Copilot license needed.
Three independent gates
- Microsoft Entra ID - the user is signed in (token for
CopilotStudio.Copilots.Invoke). - Copilot Studio sharing - the agent is shared with the user or their group.
- SharePoint permission - the user can open the page (Visitor is enough).
How this implementation uses authorization code + PKCE
Microsoft deprecated the older implicit grant, not the SPA application type. This web part uses MSAL Browser 4.30, which uses the OAuth 2.0 authorization-code flow with Proof Key for Code Exchange (PKCE). The deployed Entra app has both implicit-token settings turned off.
Implicit grant
- Requested tokens directly from the authorization endpoint.
- Returned tokens in the browser URL fragment.
- No PKCE binding for the authorization response.
- Implicit access-token and ID-token issuance are disabled on our app registration.
SPA + authorization code + PKCE
- MSAL requests a short-lived authorization code.
- A one-time PKCE verifier binds the code to the browser request.
- MSAL redeems the code at the token endpoint and manages renewal.
- No client secret is placed in SharePoint or JavaScript.
CopilotStudio.Copilots.Invoke, and reports enableAccessTokenIssuance: false plus enableIdTokenIssuance: false.FlowFrom the SharePoint page to the agent
Step by stepWhat the code does
SharePoint loads the tenant-wide SPFx web part
The page supplies the current tenant, signed-in SharePoint user and the agent's Microsoft 365 Agents SDK connection string.
No Copilot Studio credential or secret is stored in the page configuration.
The web part initializes MSAL as a public client
MSAL receives the single-tenant client ID, tenant authority and the SharePoint origin as its SPA redirect URI. A public client has no client secret.
Microsoft Entra ID recognizes the request as a browser SPA request.
MSAL first checks its token cache
acquireTokenSilent looks for the correct account (matched to the SharePoint UPN) and a usable delegated token.
A valid cached token avoids another round trip and no user interface appears.
MSAL reuses the existing Entra session
If the cache has no usable token, ssoSilent starts an authorization-code request in a hidden frame with login_hint set to the SharePoint user.
The existing SharePoint / Entra session normally satisfies sign-in and MFA without another prompt.
PKCE binds the authorization code to this browser request
MSAL creates a one-time code verifier and sends its derived challenge to Microsoft Entra ID. The returned authorization code can only be redeemed with that verifier.
A stolen authorization code cannot be redeemed by a different client.
MSAL redeems the code for a delegated Power Platform token
MSAL calls the Microsoft identity platform token endpoint and requests the scope derived from the connection string (for example https://api.powerplatform.com/.default).
The token represents the signed-in user and contains the consented CopilotStudio.Copilots.Invoke permission.
The Copilot Studio client starts the conversation
The web part gives the access token and connection settings to CopilotStudioClient, then bridges its activity stream to Bot Framework Web Chat.
The token is sent only to the Power Platform / Copilot Studio endpoint.
Copilot Studio checks both identity and sharing
Copilot Studio validates the token, identifies the user and enforces whether the agent is shared with that user or one of their groups.
Shared users chat; unshared users receive the agent's access-denied response.
Interactive fallback uses the same secure flow
If silent sign-in is blocked by browser privacy controls or Conditional Access, the user selects Sign in with Microsoft. acquireTokenPopup still uses authorization code + PKCE.
The popup does not switch to implicit grant. One completed sign-in reconnects the other agent web parts on the page.
ConfigurationVerified Entra settings
| Setting | Value used by this implementation | Status |
|---|---|---|
| Application type | Single-page application (public client) | Supported |
| OAuth flow | Authorization code with Proof Key for Code Exchange (PKCE) | Supported |
| Implicit access-token issuance | Disabled (enableAccessTokenIssuance: false) | Verified |
| Implicit ID-token issuance | Disabled (enableIdTokenIssuance: false) | Verified |
| Sign-in audience | Single tenant (AzureADMyOrg) | Verified |
| SPA redirect URI | https://<tenant>.sharepoint.com | Verified |
| Copilot Studio permission | Power Platform API / CopilotStudio.Copilots.Invoke (delegated) | Verified |
| Client credential | None - a browser public client must not contain a secret | Verified |
| Authentication library | MSAL Browser 4.30 (PublicClientApplication) | Verified |
| Current token cache | localStorage; use sessionStorage or memoryStorage if policy requires less persistence | Optional hardening |
Code pathMSAL configuration and token acquisition
Public-client configuration
A browser SPA has a client ID and redirect URI, but no client secret.
const pca = new PublicClientApplication({
auth: {
clientId: "<single-tenant-app-client-id>",
authority: "https://login.microsoftonline.com/<tenant-id>",
redirectUri: window.location.origin,
navigateToLoginRequestUrl: false
},
cache: {
cacheLocation: "localStorage"
// Use "sessionStorage" or "memoryStorage" for less persistence.
}
});
await pca.initialize();Silent first, interactive only when required
All three MSAL APIs below use authorization code + PKCE in MSAL Browser 4.
const scopes = [CopilotStudioClient.scopeFromSettings(settings)];
// 1. Cache / refresh token.
const account = findAccount(pca, sharePointUserUpn);
if (account) {
return await pca.acquireTokenSilent({ scopes, account });
}
// 2. Existing SharePoint / Entra session (authorization code + PKCE).
return await pca.ssoSilent({ scopes, loginHint: sharePointUserUpn });
// 3. User-initiated fallback; still authorization code + PKCE.
return await pca.acquireTokenPopup({ scopes, loginHint: sharePointUserUpn });VerifyHow to confirm implicit grant is disabled
Portal checks
- Entra admin center > App registrations > your app > Authentication.
- Confirm the redirect URI is under Single-page application.
- Under Implicit grant and hybrid flows, leave both token checkboxes clear.
- Under API permissions, confirm delegated Power Platform API / CopilotStudio.Copilots.Invoke.
- Confirm Supported account types is your organization only.
Azure CLI check
az ad app show --id <app-client-id> --query "{
signInAudience: signInAudience,
spaRedirectUris: spa.redirectUris,
implicitAccessToken: web.implicitGrantSettings.enableAccessTokenIssuance,
implicitIdToken: web.implicitGrantSettings.enableIdTokenIssuance
}" -o jsonExpected shape:
{
"signInAudience": "AzureADMyOrg",
"spaRedirectUris": ["https://<tenant>.sharepoint.com"],
"implicitAccessToken": false,
"implicitIdToken": false
}Current cache and hardening choice
The tested package uses localStorage so several web parts and browser tabs can reuse the account. MSAL Browser 4 encrypts local-storage artifacts while the browser session exists, but Microsoft states that cache encryption is not a replacement for XSS prevention.
Where policy requires less persistence, change the MSAL cache to sessionStorage (Microsoft default) or memoryStorage (strongest isolation, but sign-in state is lost on refresh).
What the Agents SDK web part enables
Status reflects this engagement: Validated = executed in the demo tenant, To validate = run it in your tenant before relying on it, By design = supported pattern that was not tested here.
One assistant on every intranet site
Employees open the intranet home, HR, IT or a project site and chat with the same company agent, already signed in.
How: One tenant-wide SPFx package and one Entra SPA app serve every site. Each page stores only the agent's connection string.
Evidence: Agent A answered every shared tester on every site they could open (8 of 8 runs across Sites A, B and C).
Only approved people can chat
HR shares the HR agent with the HR team. Anyone else who opens the page is refused, on every site.
How: Copilot Studio sharing (End user access) is enforced on every Agents SDK call. Use Entra security groups in production.
Evidence: Unshared users got You don't have access to talk to this bot on every site. Revoking and re-sharing took effect on the next page load.
The agent knows who is chatting
The agent greets employees by name and can use their identity in topics, for example to look up their own requests.
How: With Authenticate with Microsoft, Copilot Studio receives the signed-in user, so System.User.* variables are filled in.
Evidence: Every greeting used the tester's name (Hello Agents SDK User A!). The web part footer shows the signed-in account.
Single sign-on, also with MFA
People already signed in to SharePoint start chatting without another prompt. If the browser blocks silent sign-in, one click fixes it.
How: MSAL acquireTokenSilent, then ssoSilent with the SharePoint user as login hint. Fallback: Sign in with Microsoft popup; one sign-in serves every agent on the page.
Evidence: 24 of 24 Agents SDK sessions signed in silently. With silent SSO blocked, one click on the sign-in button led to an answer in about 20 s.
Several agents on one page, or one agent per site
A hub page shows an HR agent and an IT agent side by side; each department site shows its own agent.
How: Every web part instance has its own connection string and conversation.
Evidence: Each test page hosted three agent web parts with independent results.
No host site to open up
A person may use the agent but must not see the agent owner's site. They still chat from their own site.
How: The web part calls Copilot Studio directly - there is no .agent file or host site in the path.
Evidence: Shared User C with no access to Site A chatted from Site B. The Agent link (SharePoint channel) blocked the same user.
Employees without Microsoft 365 Copilot
Employees without a Microsoft 365 Copilot license use the agent; usage is billed to Copilot Studio capacity.
How: Standard-harness agents reached through the Agents SDK consume the tenant's Copilot Studio capacity (Copilot Credits).
Evidence: Every test user had Microsoft 365 E5 (no Teams) only - no Microsoft 365 Copilot or Copilot Studio license.
Never reachable anonymously
Security requires that the agent cannot be used without an Entra sign-in, even if a URL leaks.
How: Agents SDK endpoints accept only user tokens for CopilotStudio.Copilots.Invoke.
Evidence: Anonymous calls to both connection strings returned HTTP 401 (re-checked on 7 Oct 2026). The hosted embed of the GitHub Copilot harness agent did not enforce sign-in.
Update once, every site follows
IT ships a new web part version once and every site gets it; makers publish agent changes without touching pages.
How: Tenant app catalog with Enable this app and add it to all sites; agent changes go live when the agent is published.
Evidence: Package 1.0.1.0 -> 1.0.3.0 was rolled out tenant-wide without editing a page; the regression run on Site A passed.
Answers that respect SharePoint permissions
The agent answers from policy documents in SharePoint and only uses documents the user can read.
How: SharePoint knowledge with Microsoft authentication runs generative answers on behalf of the signed-in user (documented for Teams, Power Apps and Microsoft 365 Copilot).
Evidence: Not tested through the Agents SDK in this engagement - validate before relying on it.
Agent that knows the page it is on
On a project site the agent already knows the project and tailors its answers.
How: Post a custom event with site/page context after the first agent message and read it in an OnEventActivity topic (Microsoft Learn pattern). Needs a small web part extension.
Evidence: Not implemented in this web part yet.
Same agent in other web apps
The agent is also embedded in a line-of-business portal or a native app with the same sign-in.
How: Copilot Studio client library for JavaScript, .NET or Python with the same connection string and a user token (service principal tokens are not supported).
Evidence: Integrate with web or native apps - not tested here.
Build it step by step
About 60-90 minutes the first time; adding the agent to another site takes about 2 minutes. Scripts and the SPFx solution are in the repository. Every checkpoint below was observed in the demo tenant.
Part 1Prepare the agent in Copilot Studio
Create a standard-harness agent
The Copilot Studio client library (Agents SDK) supports agents built on the standard harness.
- Copilot Studio > Home > Other ways to build > Agent (Standard), enter a name, select Create.
- Add a description and instructions on the Overview page.
Require Microsoft sign-in
The Agents SDK only works with authenticated agents, and it is what makes the agent know the user.
- Settings > Security > Authentication > Authenticate with Microsoft (default for new agents) > Save.
Greet the user by name (recommended)
A personalised greeting proves on every page that the user's identity reaches the agent.
- Topics > System > Conversation Start > ... > Open code editor.
- Replace only the text of the first message (keep the existing
id), then Save.
text:
- "Hello {System.User.DisplayName}! I'm {System.Bot.Name}. How can I help?"Publish and copy the connection string
Pages connect to the published agent through this string. It contains no secret.
- Select Publish.
- Channels > Web app (or Native app) > Microsoft 365 Agents SDK > Copy.
https://<environment>.environment.api.powerplatform.com/copilotstudio/dataverse-backed/authenticated/bots/<schema-name>/conversations?api-version=2022-03-01-preview/bots/<agent schema name>/conversations?api-version=....Share the agent
Sharing decides who can chat - on every site and page.
- ... > Share > add users or Entra security groups > keep End user access > Share.
Part 2One-time tenant setup
Register the Entra SPA app
SPFx's built-in token provider cannot get the Copilot Studio scope, so the web part signs in with its own app.
- Copy
scripts/variables.sample.ps1toscripts/variables.ps1and fill it in. - Run the script (idempotent). It adds the SPA redirect URI (your SharePoint origin), the delegated Power Platform API > CopilotStudio.Copilots.Invoke permission and tenant-wide consent.
. .\scripts\variables.ps1
az login --tenant $TenantDomain --allow-no-subscriptions
.\scripts\New-AgentsSdkSpaApp.ps1 -TenantId $TenantDomain -SharePointRootUrl $SharePointRootUrl"adminConsent": "granted"; note the appClientId.Build the SharePoint package
The package carries the app ID as tenant default, so page authors only paste a connection string.
- Node 22 is required for SPFx 1.21.1. MSAL, the Agents SDK client and Web Chat are bundled - no CDN or API-access approvals.
.\scripts\Build-SpfxPackage.ps1 -AppClientId $AppClientId -Version $PackageVersion -NodePath $Node22Path
# -> spfx-copilot-agent\sharepoint\solution\copilot-studio-agent-webpart.sppkgcopilot-studio-agent-webpart.sppkg is created.Deploy it to every site
One deployment makes the web part available on all current and future sites.
- SharePoint admin center > Active sites > Apps > Membership > add yourself as site admin of the app catalog.
- App catalog > Manage apps > Upload the
.sppkg(Replace for updates) > Enable this app and add it to all sites > Enable app.
Part 3Add the agent to your sites
Add the web part and paste the connection string
Each page decides which agent it shows; repeat on as many sites as you like.
- Edit the page > + > Copilot Studio Agent (Agents SDK).
- Edit properties > Integration mode Microsoft 365 Agents SDK > paste the connection string. Leave app and tenant ID empty (package defaults).
- Republish the page.
Give people access to the site
SharePoint still decides who can open the page. Visitor (Read) is enough.
- Grant site or page access as usual. Agent sharing and site permissions are independent - people need both.
Part 4Verify
Run the core test scenarios
Five checks prove sign-in, identity, access control and the security boundary.
- Shared user: greeting by name and an answer (TC-01, TC-02, TC-05).
- Unshared user: You don't have access to talk to this bot (TC-06).
- Anonymous call to the connection string returns HTTP 401 (TC-04).
Invoke-WebRequest -Method Post -Uri $AgentConnectionString -ContentType 'application/json' `
-Body '{"emitStartConversationEvent":true}' -SkipHttpErrorCheck | Select-Object StatusCode # 401Run the resilience checks
Confirms the experience when silent sign-in is blocked, access changes, or a user restarts.
- Silent sign-in blocked: Sign in with Microsoft opens the popup on the first click (TC-03).
- Remove and re-add a user on the agent (TC-07, TC-08); select New chat (TC-22).
Automate the regression run (optional)
Re-run the same scenarios after every agent or package change.
- Edit
tests/agents-sdk-matrix/settings.py, then run the harness (Edge + Playwright, one profile per test user).
cd tests\agents-sdk-matrix
.\launch-edge.ps1
foreach ($p in 'mario','a','b','c','d') { python run_matrix.py $p }
python run_extra_scenarios.py a A # TC-03 sign-in fallback + TC-22 New chat
python summarize.pyoutput/results/summary.md shows the expected result for every persona and site.27 scenarios with expected and actual results
Executed in the demo tenant on 7 Oct 2026 with 4 test users, 3 sites, 2 agents and the maker as control (web part 1.0.3.0). Select a row for steps, expected result, actual result and evidence.
Results in the demo tenant
Defect found and fixed during testing: TC-03 (sign-in popup blocked on pages with several agents) - fixed in web part 1.0.2.0/1.0.3.0 and re-tested.
By area
| Area | Scenarios | Pass | Limitation / finding | Not run |
|---|---|---|---|---|
| Sign-in and identity | 4 | 4 | 0 | 0 |
| Access control | 6 | 5 | 0 | 1 |
| Sites and pages | 5 | 4 | 0 | 1 |
| Licensing and capacity | 2 | 1 | 0 | 1 |
| Agent types | 2 | 0 | 2 | 0 |
| Lifecycle and operations | 4 | 2 | 0 | 2 |
| Extended scenarios | 4 | 0 | 0 | 4 |
| ID | Scenario | Area | Priority | Result in the demo tenant |
|---|---|---|---|---|
| ▸ TC-01 | Silent single sign-on for MFA-protected users | Sign-in and identity | P1 | Pass |
| ▸ TC-02 | The agent knows who is chatting | Sign-in and identity | P2 | Pass |
| ▸ TC-03 | One-click sign-in when silent SSO is blocked | Sign-in and identity | P1 | Pass |
| ▸ TC-04 | No anonymous access to the agent endpoint | Sign-in and identity | P1 | Pass |
| ▸ TC-05 | Shared users can chat | Access control | P1 | Pass |
| ▸ TC-06 | Unshared users are blocked | Access control | P1 | Pass |
| ▸ TC-07 | Removing a user from sharing blocks them | Access control | P1 | Pass |
| ▸ TC-08 | Re-sharing restores access | Access control | P2 | Pass |
| ▸ TC-09 | Access through an Entra security group | Access control | P1 | Not run |
| ▸ TC-10 | Agent sharing and site permissions stay independent | Access control | P1 | Pass |
| ▸ TC-11 | One package and one Entra app serve many sites | Sites and pages | P1 | Pass |
| ▸ TC-12 | Visitor (Read) permission is enough | Sites and pages | P2 | Pass |
| ▸ TC-13 | No host-site dependency | Sites and pages | P2 | Pass |
| ▸ TC-14 | Several agents on one page | Sites and pages | P3 | Pass |
| ▸ TC-15 | Page opened in Teams, Viva Connections or the mobile app | Sites and pages | P2 | Not run |
| ▸ TC-16 | Users without Microsoft 365 Copilot licenses | Licensing and capacity | P2 | Pass |
| ▸ TC-17 | Usage is billed to Copilot Studio capacity | Licensing and capacity | P2 | Not run |
| ▸ TC-18 | GitHub Copilot harness agent through the Agents SDK | Agent types | P3 | Confirmed limitation |
| ▸ TC-19 | Hosted web chat embed is not an access boundary (comparison) | Agent types | P1 | Security finding |
| ▸ TC-20 | A web part update reaches every site | Lifecycle and operations | P2 | Pass |
| ▸ TC-21 | Published agent changes reach every site | Lifecycle and operations | P2 | Not run |
| ▸ TC-22 | New chat starts a fresh conversation | Lifecycle and operations | P3 | Pass |
| ▸ TC-23 | Conversation longer than the token lifetime | Lifecycle and operations | P3 | Not run |
| ▸ TC-24 | Clear error for a wrong connection string | Extended scenarios | P3 | Not run |
| ▸ TC-25 | Page context reaches the agent (extension) | Extended scenarios | P3 | Not run |
| ▸ TC-26 | SharePoint knowledge respects the user's permissions | Extended scenarios | P1 | Not run |
| ▸ TC-27 | Guest (B2B) users | Extended scenarios | P3 | Not run |
Test setup
Agent A = standard harness, shared with User A and User B. Agent B (Weather) = GitHub Copilot harness, shared with User A and User C. User D has no agent. Every site page hosts Agent A and Agent B via the Agents SDK web part plus Agent B's hosted embed for comparison. Test users: Microsoft 365 E5 (no Teams), no Copilot licenses, MFA.
| Persona | Agent sharing | Site A | Site B | Site C |
|---|---|---|---|---|
| User A | Agent A + Agent B | Visitor | Member | Owner |
| User B | Agent A | Member | Visitor | No access |
| User C | Agent B | No access | Member | Visitor |
| User D | None | Visitor | No access | Member |
| MarioR (maker) | Owner of both | Owner | Owner | Owner |
Reading the results
Pass behaved as expected. Confirmed limitation documented product limitation reproduced. Security finding behaviour to be aware of (not the Agents SDK path). Not run recommended for your tenant - the expected result is the requirement to verify.
The core set (TC-01, TC-02, TC-04, TC-05, TC-06) is the quick regression after any agent or package change.
From demo results to your sign-off
1. Use the Excel test plan
The Excel test plan lists every scenario with preconditions, steps, expected result and the demo result. Fill in Your result, Tester, Date and Notes; the Summary sheet counts passes, failures and the P1 pass rate.
2. Start with P1 and the core set
11 scenarios are P1 (must pass before go-live), including the ones this engagement could not run: security-group sharing (TC-09) and SharePoint knowledge permissions (TC-26, when used).
3. Automate the regression
The Playwright harness drives real Edge windows, one profile per test user, and records greeting, answer, errors and a screenshot per web part.
cd tests\agents-sdk-matrix
.\launch-edge.ps1
foreach ($p in 'mario','a','b','c','d') { python run_matrix.py $p }
python run_extra_scenarios.py a A # TC-03 + TC-22
python summarize.py4. Keep test accounts safe
Test-user passwords and MFA seeds stay in a secrets folder outside the repository ($env:AGENTS_SDK_TEST_SECRETS_DIR). Delete the test users and the SPA app registration when you are done (guide section 13).
Not supported, and known design limits
Not supported
- GitHub Copilot harness agents through the Agents SDK - Copilot Studio replies This action doesn't support agents built with the GitHub Copilot harness. - verified for every user including the owner. Build SharePoint-facing agents on the standard harness.
- SPFx built-in token provider (
AadTokenProvider) - Cannot obtainCopilotStudio.Copilots.Invoke(sp-dev-docs #10775). The web part uses MSAL.js with its own SPA app instead. - App-only (service principal) tokens - Not supported by the Agents SDK client (Microsoft Learn). Use Direct Line for server-to-server scenarios.
- Hosted web chat embed as an access boundary - Copilot Studio offers the embed code only for agents without authentication. The GitHub Copilot harness embed answered unshared and signed-out users in this test.
Design limits of this web part
- Token lifetime - the client receives an access token, not a token provider. If a long-open conversation stops answering, New chat signs in again silently (TC-23).
- Embedded hosts - Teams, Viva Connections or the mobile app can block the hidden sign-in frame or popups; validate TC-15 before using these entry points.
- Page context - not passed to the agent yet; the documented event pattern needs a small extension (UC-11).
- Capacity - unlicensed users consume Copilot Studio capacity; monitor it (TC-17).
Companion material and references
Companion material
- Customer pattern: SharePoint channel + Agent link vs. Agents SDK - the customer's setup reproduced in the same tenant and compared
- Test plan (Excel) - record your own results
Source code and guide
https://github.com/patmeh1/copilot-studio-agents-sdk-sharepoint
docs/Agents-SDK-SharePoint-Tutorial.mdanddocs/Agents-SDK-SharePoint-Use-Cases-and-Test-Scenarios.md- this page as markdowndocs/Agents-SDK-SharePoint-Guide.md- full guide, findings and troubleshootingspfx-copilot-agent/,scripts/,tests/agents-sdk-matrix/- web part, setup scripts, test harness