Add Teams delegated auth for plugin tools#77784
Add Teams delegated auth for plugin tools#77784MSNexploder wants to merge 7 commits intoopenclaw:mainfrom
Conversation
|
Codex review: needs maintainer review before merge. Summary Reproducibility: not applicable. as a bug reproduction: this PR adds a new Teams delegated-auth capability. The supplied screenshot and copied output provide high-confidence after-fix proof for consent, six-digit fallback, retry, and downstream profile lookup. Real behavior proof Next step before merge Security Review detailsBest possible solution: Review and land this PR if maintainers accept the delegated-auth SDK/config boundary and latest head checks stay green; otherwise request targeted API or policy changes on the branch. Do we have a high-confidence way to reproduce the issue? Not applicable as a bug reproduction: this PR adds a new Teams delegated-auth capability. The supplied screenshot and copied output provide high-confidence after-fix proof for consent, six-digit fallback, retry, and downstream profile lookup. Is this the best way to solve the issue? Yes, broadly: the generic core policy plus Teams-owned SSO implementation matches the repository owner-boundary rules. Maintainers still need to accept the new token-bearing public config and SDK surface before merge. What I checked:
Likely related people:
Remaining risk / open question:
Codex review notes: model gpt-5.5, reasoning high; reviewed against 4c6f3bc07e89. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 6c6ecd769b
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
|
Addressed the Teams SSO stale challenge-key finding in 27e19a7d7bdb. The fix groups all user-id aliases recorded for one pending sign-in challenge, so clearing via either AAD object id or Teams channel user id removes the full challenge. Added handler-level regression coverage for the mixed-id flow: record consent challenge with both ids, complete verifyState with only one id, then confirm a later six-digit message with the other id reaches normal dispatch. Validation:
|
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 27e19a7d7b
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
3fac718 to
98a9469
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 71b55d98d6
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
1f34650 to
b035ebd
Compare
b035ebd to
03e1a49
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 03e1a49019
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
03e1a49 to
b865817
Compare
There was a problem hiding this comment.
💡 Codex Review
https://github.com/openclaw/openclaw/blob/b865817d5cfe4f16003aa62c54be33b47501203a/extensions/msteams/src/sso.ts#L455-L456
Reject token-exchange invokes for wrong OAuth connection
handleSigninTokenExchangeInvoke trusts value.connectionName from the incoming invoke and falls back to deps.connectionName, but it never rejects a non-empty mismatch. If Teams sends a stale or different connection name (for example in multi-connection setups or misconfigured manifests), this path can report success and the caller clears the pending sign-in challenge, even though delegated tool auth later requests tokens only for the configured connection and continues failing. This creates a false-success sign-in flow and can strand users in missing_consent until a new challenge is triggered.
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
b865817 to
aa13dcb
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: aa13dcbfcf
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
aa13dcb to
d20ff69
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: d20ff69ded
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
d20ff69 to
e9bcf87
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: e9bcf87e0d
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
e9bcf87 to
a9c1de1
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: a9c1de111e
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
…oken invokeResponse send Brad openclaw#2 / codex openclaw#4 on PR openclaw#76262, SSO half. Continue the typed-route migration: `signin/tokenExchange` and `signin/verifyState` now register via `app.on("signin.token-exchange" | "signin.verify-state", ...)`. Per the SDK's router, registering a user route with the same name as a system route removes the system default — so the SDK's built-in handlers (which would call `api.users.token.exchange` themselves and emit a `signin` event nobody currently subscribes to) are silenced, and only ours runs. The SDK wraps a void return into the HTTP 200 InvokeResponse, so the legacy `ctx.sendActivity({ type: "invokeResponse", ... })` ack — broken on the new SDK because it becomes an outbound BF activity instead of the HTTP response — is gone. The handler body is extracted from the activity-catch-all dispatch in `monitor-handler.ts` to a new `signin-invoke.ts`, parallel to `file-consent-invoke.ts`. `isSigninInvokeAuthorized` is now exported from `monitor-handler.ts` so the new handler can reuse it. The activity catch-all skips the SSO invoke names alongside the existing skips for `adaptiveCard/action` and `fileConsent/invoke`. `MSTeamsAppOn` overloads now cover the two SSO routes with their typed ctx (`ISignInTokenExchangeInvokeActivity` / `ISignInVerifyStateInvokeActivity`). Tests in `monitor-handler.sso.test.ts` were rewritten to call the extracted handler directly — the `registered.run(ctx)` shape no longer covers SSO, and the `expect(ctx.sendActivity).toHaveBeenCalledWith({ type: "invokeResponse" })` assertions were dropped to match the new contract (the SDK ack happens via the typed-route return value). Note on overlap with openclaw#77784 (Stefan Stüben, Microsoft): that PR is doing a much bigger SSO rework (sign-in card / sign-in-link / six-digit-code fallbacks plus a `ctx.auth` plumbed to plugin tools). This change is the small migration-correctness fix and is structured so openclaw#77784's SSO body changes drop into the typed-route registrations cleanly on rebase. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
a9c1de1 to
d190c3d
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: d190c3dda8
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
d190c3d to
ccc5f89
Compare
ccc5f89 to
4e29f5a
Compare
Summary
Change Type (select all)
Scope (select all touched areas)
Linked Issue/PR
Real behavior proof (required for external PRs)
pnpm openclaw --dev gateway, Microsoft Teams dev app, Bot Framework OAuth connection, devtunnel on port 3978, local downstream OBO API, and the examplemsteams-graph-profileplugin.msteams-graph-profileplugin configured.hi, run teams whoamito the OpenClaw Teams dev app.hi, run teams whoamiagain.msteams_whoamicall returned the signed-in Teams user profile/email through the downstream OBO API.msteams_whoamireturnedMicrosoft Teams delegated auth unavailable: missing_consent.Root Cause (if applicable)
Regression Test Plan (if applicable)
N/A for a new feature, but the smallest reliable guardrails added/used are:
src/plugins/tools.optional.test.ts,extensions/msteams/src/delegated-auth.test.ts,extensions/msteams/src/monitor-handler.sso.test.ts,extensions/msteams/src/monitor-handler/message-handler.delegated-auth.test.ts,extensions/codex/src/app-server/run-attempt.test.ts,src/agents/pi-embedded-runner/run.delegated-auth-context.test.ts,src/auto-reply/reply/followup-runner.test.ts.User-visible / Behavior Changes
plugins.entries.<pluginId>.auth.delegatedAccess.examples/plugins/msteams-graph-profileandexamples/msteams-obo-downstream-api.Diagram (if applicable)
Security Impact (required)
Yes)Yes)Yes)Yes)Yes)Yes, explain risk + mitigation: Delegated Teams tokens are sensitive. This PR keeps access opt-in and policy-gated by plugin/tool allowlists plus provider, audience, scope, user, tenant, chat, and chat-type checks. Tokens are passed as hidden runtime context to plugin execution, not prompt text or transcript content. The example keeps Microsoft Graph access server-side in a downstream API so client secrets and Graph permissions stay behind an API boundary.Repro + Verification
Environment
Steps
pnpm openclaw --dev gateway.hi, run teams whoamiin Teams.hi, run teams whoami.Expected
Actual
missing_consent.Microsoft Teams delegated auth is connected. Retry the tool now.msteams_whoamireturned the signed-in Teams user profile/email.Evidence
Attach at least one:
Human Verification (required)
What you personally verified (not just CI), and how:
msteams_whoami, and downstream OBO profile lookup.Review Conversations
If a bot review conversation is addressed by this PR, resolve that conversation yourself. Do not leave bot review conversation cleanup for maintainers.
Compatibility / Migration
Yes)Yes)No)plugins.entries.<pluginId>.auth.delegatedAccessand the Teams Bot Framework OAuth connection described in the docs.Risks and Mitigations
Rationale And Design Decisions
Keep core generic
Delegated auth is modeled as a generic plugin capability instead of a Teams-specific special case in core. Core owns policy parsing, tool gating, token claim validation, and runtime propagation; the Teams plugin owns Bot Framework SSO behavior and token acquisition.
Make delegated auth explicit and policy-gated
Plugin delegated auth is opt-in through
plugins.entries.<pluginId>.auth.delegatedAccess.enabled, with optional provider, audience, scope, user, tenant, chat, and chat-type restrictions.Keep optional tools least-privilege
Optional plugin factories receive
ctx.authonly when the plugin itself,group:plugins,*, or a concrete optional tool name is allowlisted. The resolver also handles implicit optional registrations wherenames: []anddeclaredNamescarry the actual tool contract.Resolve auth per execution for cached descriptors
Cached plugin tool descriptors keep descriptor creation fast, but delegated auth is resolved when the cached tool executes, so a previous message’s auth context is never cached into a future run.
Propagate auth through run plumbing, not prompts
The Teams delegated auth context is passed through hidden runtime/run parameters and plugin tool context, including queued replies, follow-up runs, embedded runner attempts, OpenClaw tools, and Codex app-server tool calls.
Teams owns Bot Framework SSO details
The Teams plugin owns Bot Framework token exchange, verify-state, sign-in resource creation, sign-in-link fallback, six-digit code fallback, and Teams user-id fallback handling.
Keep Graph access server-side in the example
The example plugin calls a local downstream API. The downstream API performs Microsoft Graph OBO using its own app registration and delegated permission grant, which keeps Graph client secrets and broader Graph behavior behind a production-shaped API boundary.
Verification
pnpm test src/plugins/tools.optional.test.tspnpm tsgo:core --pretty falsepnpm tsgo:test:src --pretty falsepnpm test src/plugins/tools.optional.test.ts extensions/msteams/src/delegated-auth.test.ts extensions/msteams/src/monitor-handler.sso.test.ts extensions/msteams/src/monitor-handler/message-handler.delegated-auth.test.ts extensions/codex/src/app-server/run-attempt.test.ts src/agents/pi-embedded-runner/run.delegated-auth-context.test.ts src/auto-reply/reply/followup-runner.test.tspnpm tsgo:extensions --pretty falsepnpm tsgo:extensions:test --pretty falsepnpm test src/plugins/tool-descriptor-cache.test.ts src/plugins/tools.optional.test.ts extensions/msteams/src/delegated-auth.test.tspnpm exec oxfmt --check --threads=1 src/plugins/tool-descriptor-cache.ts src/plugins/tool-descriptor-cache.test.ts src/plugins/tools.ts src/plugins/tools.optional.test.ts extensions/msteams/src/delegated-auth.test.tsgit diff --checkpnpm dup:check:coveragemsteams_whoamireturned the signed-in user profile