Skip to content

fix(msteams): address review feedback on #40884 β€” schema, types, env validation#47934

Open
krrrke wants to merge 8 commits intoopenclaw:mainfrom
krrrke:fix/msteams-auth-review
Open

fix(msteams): address review feedback on #40884 β€” schema, types, env validation#47934
krrrke wants to merge 8 commits intoopenclaw:mainfrom
krrrke:fix/msteams-auth-review

Conversation

@krrrke
Copy link
Copy Markdown

@krrrke krrrke commented Mar 16, 2026

Summary

Builds on #40884 (federated credentials and certificate auth support) to fix review issues and add env-backed auth validation:

  • Make appPassword optional β€” was required but set to empty string for non-secret auth; now optional in MSTeamsCredentials
  • Deduplicate MSTeamsAuthType β€” derived from MSTeamsConfig["authType"] instead of a standalone union in two files
  • Add Zod schema fields β€” authType, certPemFile, certKeyFile, sendX5C, ficClientId, widAssertionFile added to MSTeamsConfigSchema (P1 blocker β€” configs using these fields were rejected)
  • Restore env var hints β€” error messages now include MSTEAMS_APP_ID/MSTEAMS_TENANT_ID alongside new auth type guidance
  • Validate auth material across sources β€” presence helpers check typed config + SDK plain env vars; rejects empty configs that would silently fall through to managed identity auth; accepts mixed-source configs (e.g. certPemFile in config, certKeyFile in env)

No regressions from prior state

None of the known limitations below are regressions β€” they are pre-existing gaps in the original feature (#40884). This PR strictly improves the state: adds missing schema validation, fixes type design, restores dropped error hints, and adds auth material presence checks that did not exist before.

Known limitations (addressed in follow-up PR)

  • authType is not auto-inferred from environment β€” if authType is omitted, resolveMSTeamsCredentials defaults to clientSecret even when certificate/federated env vars are present. Requires a unified auth-source resolution helper.
  • hasConfiguredMSTeamsCredentials does not check MSTEAMS_APP_ID/MSTEAMS_TENANT_ID env vars β€” env-only deployments may appear unconfigured in onboarding.
  • connections__serviceConnection__settings__* env vars are not checked in presence validation β€” cannot reliably attribute them to the Teams channel vs other Agents SDK connections. The SDK merges them at runtime.
  • Integration tests for the SDK env merge path are deferred to the follow-up.

These will be resolved in a follow-up PR that introduces a shared readTeamsAuthPresence helper and DefaultAzureCredential support.

AI Disclosure

  • AI-assisted (Claude Code + Codex review)
  • Fully tested β€” 233/233 msteams tests passing
  • pnpm build && pnpm check && pnpm test green
  • Authors understand what the code does

Fixes #40855
Related: #40884

@openclaw-barnacle openclaw-barnacle bot added channel: msteams Channel integration: msteams size: M labels Mar 16, 2026
@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps bot commented Mar 16, 2026

Greptile Summary

This PR addresses review feedback on the federated credentials and certificate auth feature (#40884): it makes appPassword optional, derives MSTeamsAuthType from a single source of truth, adds missing Zod schema fields that were blocking configs from validating, restores env var names in error hints, and adds auth-material presence checks for the new auth types.

Key changes:

  • MSTeamsCredentials.appPassword is now optional; authType is always present, defaulting to "clientSecret".
  • hasConfiguredMSTeamsCredentials and resolveMSTeamsCredentials dispatch on authType to check/resolve the correct auth material.
  • MSTeamsConfigSchema now includes authType, certPemFile, certKeyFile, sendX5C, ficClientId, widAssertionFile β€” configs using these fields were previously rejected at validation.
  • buildMSTeamsAuthConfig in sdk.ts switches on authType to populate the SDK config object correctly.

Potential issue found:

  • In sdk.ts, the certificate branch assigns base.certPemFile = creds.certPemFile and base.certKeyFile = creds.certKeyFile unconditionally β€” even when those values are undefined (e.g., env-only cert configs). This is inconsistent with the federated branch which uses if (creds.ficClientId) guards. If the SDK checks 'certPemFile' in config rather than truthiness, this could prevent the SDK's own env var fallback, silently breaking cert auth when only env vars are used.

Confidence Score: 3/5

  • Mostly safe to merge; one logic inconsistency in sdk.ts may break certificate auth when cert files are supplied via env vars only.
  • The schema, type, and error message changes are clean and correct. The resolveMSTeamsCredentials refactor is well-structured and well-tested. However, buildMSTeamsAuthConfig in sdk.ts unconditionally assigns certPemFile/certKeyFile as undefined when env-only cert config is used, unlike the federated case which uses conditional guards. Whether this breaks runtime cert auth depends on whether the SDK checks 'certPemFile' in config (broken) or falls back on truthiness (fine). The existing tests for the env-only cert path only assert authType, not end-to-end SDK behaviour, so this gap is not caught.
  • extensions/msteams/src/sdk.ts β€” the certificate branch of buildMSTeamsAuthConfig
Prompt To Fix All With AI
This is a comment left during a code review.
Path: extensions/msteams/src/sdk.ts
Line: 22-25

Comment:
**Unconditional undefined assignment may block SDK env var fallback**

`certPemFile` and `certKeyFile` are assigned to `base` unconditionally, including when they are `undefined` (e.g. when cert files come from env vars only). This is inconsistent with the federated credential case below, which uses `if (creds.ficClientId)` guards.

If `creds.certPemFile` is `undefined` (because the file path was set via `process.env.certPemFile` rather than typed config), `base.certPemFile` is explicitly set to `undefined`. Depending on how `getAuthConfigWithDefaults` inspects its argument β€” e.g. `'certPemFile' in config` vs `config.certPemFile` β€” the SDK's own env var resolution may be bypassed, leaving the adapter without a cert path at runtime.

The federated case correctly avoids this:
```ts
if (creds.ficClientId) base.FICClientId = creds.ficClientId;
if (creds.widAssertionFile) base.WIDAssertionFile = creds.widAssertionFile;
```

Consider aligning the certificate case:
```suggestion
    case "certificate":
      if (creds.certPemFile) base.certPemFile = creds.certPemFile;
      if (creds.certKeyFile) base.certKeyFile = creds.certKeyFile;
      if (creds.sendX5C != null) base.sendX5C = creds.sendX5C;
      break;
```

How can I resolve this? If you propose a fix, please make it concise.

---

This is a comment left during a code review.
Path: extensions/msteams/src/token.test.ts
Line: 69-84

Comment:
**Test only asserts `authType`, not resolved cert file paths**

The test verifies that `hasCertificateAuthMaterial` passes (so `authType` is returned), but it never asserts that `certPemFile`/`certKeyFile` are correctly propagated when the values come from env vars exclusively. Since `resolveMSTeamsCredentials` currently assigns `certPemFile: cfg?.certPemFile` (which is `undefined` when only the env var is set), a consuming component that relies on `creds.certPemFile` being non-null would see `undefined`. Adding assertions like:

```ts
expect(resolved?.certPemFile).toBeUndefined(); // explicitly documenting env-only sourcing
// or, if the intent is for the SDK to handle the env var:
expect(resolved?.certPemFile).toBe("/env/cert.pem"); // if paths should be forwarded
```

…would clarify the intended contract and catch regressions if the behavior changes.

How can I resolve this? If you propose a fix, please make it concise.

Last reviewed commit: 703377a

Comment on lines +22 to +25
base.certPemFile = creds.certPemFile;
base.certKeyFile = creds.certKeyFile;
if (creds.sendX5C != null) base.sendX5C = creds.sendX5C;
break;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unconditional undefined assignment may block SDK env var fallback

certPemFile and certKeyFile are assigned to base unconditionally, including when they are undefined (e.g. when cert files come from env vars only). This is inconsistent with the federated credential case below, which uses if (creds.ficClientId) guards.

If creds.certPemFile is undefined (because the file path was set via process.env.certPemFile rather than typed config), base.certPemFile is explicitly set to undefined. Depending on how getAuthConfigWithDefaults inspects its argument β€” e.g. 'certPemFile' in config vs config.certPemFile β€” the SDK's own env var resolution may be bypassed, leaving the adapter without a cert path at runtime.

The federated case correctly avoids this:

if (creds.ficClientId) base.FICClientId = creds.ficClientId;
if (creds.widAssertionFile) base.WIDAssertionFile = creds.widAssertionFile;

Consider aligning the certificate case:

Suggested change
base.certPemFile = creds.certPemFile;
base.certKeyFile = creds.certKeyFile;
if (creds.sendX5C != null) base.sendX5C = creds.sendX5C;
break;
case "certificate":
if (creds.certPemFile) base.certPemFile = creds.certPemFile;
if (creds.certKeyFile) base.certKeyFile = creds.certKeyFile;
if (creds.sendX5C != null) base.sendX5C = creds.sendX5C;
break;
Prompt To Fix With AI
This is a comment left during a code review.
Path: extensions/msteams/src/sdk.ts
Line: 22-25

Comment:
**Unconditional undefined assignment may block SDK env var fallback**

`certPemFile` and `certKeyFile` are assigned to `base` unconditionally, including when they are `undefined` (e.g. when cert files come from env vars only). This is inconsistent with the federated credential case below, which uses `if (creds.ficClientId)` guards.

If `creds.certPemFile` is `undefined` (because the file path was set via `process.env.certPemFile` rather than typed config), `base.certPemFile` is explicitly set to `undefined`. Depending on how `getAuthConfigWithDefaults` inspects its argument β€” e.g. `'certPemFile' in config` vs `config.certPemFile` β€” the SDK's own env var resolution may be bypassed, leaving the adapter without a cert path at runtime.

The federated case correctly avoids this:
```ts
if (creds.ficClientId) base.FICClientId = creds.ficClientId;
if (creds.widAssertionFile) base.WIDAssertionFile = creds.widAssertionFile;
```

Consider aligning the certificate case:
```suggestion
    case "certificate":
      if (creds.certPemFile) base.certPemFile = creds.certPemFile;
      if (creds.certKeyFile) base.certKeyFile = creds.certKeyFile;
      if (creds.sendX5C != null) base.sendX5C = creds.sendX5C;
      break;
```

How can I resolve this? If you propose a fix, please make it concise.

Comment on lines +69 to +84
it("resolves certificate auth via SDK plain env vars", () => {
process.env.certPemFile = "/env/cert.pem";
process.env.certKeyFile = "/env/key.pem";
try {
const resolved = resolveMSTeamsCredentials({
appId: "app-id",
tenantId: "tenant-id",
authType: "certificate",
});

expect(resolved?.authType).toBe("certificate");
} finally {
delete process.env.certPemFile;
delete process.env.certKeyFile;
}
});
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Test only asserts authType, not resolved cert file paths

The test verifies that hasCertificateAuthMaterial passes (so authType is returned), but it never asserts that certPemFile/certKeyFile are correctly propagated when the values come from env vars exclusively. Since resolveMSTeamsCredentials currently assigns certPemFile: cfg?.certPemFile (which is undefined when only the env var is set), a consuming component that relies on creds.certPemFile being non-null would see undefined. Adding assertions like:

expect(resolved?.certPemFile).toBeUndefined(); // explicitly documenting env-only sourcing
// or, if the intent is for the SDK to handle the env var:
expect(resolved?.certPemFile).toBe("/env/cert.pem"); // if paths should be forwarded

…would clarify the intended contract and catch regressions if the behavior changes.

Prompt To Fix With AI
This is a comment left during a code review.
Path: extensions/msteams/src/token.test.ts
Line: 69-84

Comment:
**Test only asserts `authType`, not resolved cert file paths**

The test verifies that `hasCertificateAuthMaterial` passes (so `authType` is returned), but it never asserts that `certPemFile`/`certKeyFile` are correctly propagated when the values come from env vars exclusively. Since `resolveMSTeamsCredentials` currently assigns `certPemFile: cfg?.certPemFile` (which is `undefined` when only the env var is set), a consuming component that relies on `creds.certPemFile` being non-null would see `undefined`. Adding assertions like:

```ts
expect(resolved?.certPemFile).toBeUndefined(); // explicitly documenting env-only sourcing
// or, if the intent is for the SDK to handle the env var:
expect(resolved?.certPemFile).toBe("/env/cert.pem"); // if paths should be forwarded
```

…would clarify the intended contract and catch regressions if the behavior changes.

How can I resolve this? If you propose a fix, please make it concise.

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

πŸ’‘ Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 507c826a94

ℹ️ About Codex in GitHub

Codex has been enabled to automatically 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 πŸ‘.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment on lines +37 to +38
const hasPem = Boolean(cfg?.certPemFile || env.certPemFile);
const hasKey = Boolean(cfg?.certKeyFile || env.certKeyFile);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Treat blank auth material as missing

The new presence checks treat any non-empty string as valid auth material, so whitespace-only values (for example certPemFile: " " or ficClientId: " ") are considered configured. In that scenario resolveMSTeamsCredentials returns credentials and the channel proceeds to runtime auth instead of failing fast as "missing credentials," and a blank config value can also mask otherwise-correct env fallbacks. Trimming/normalizing these fields before the Boolean checks would avoid this false-positive path.

Useful? React with πŸ‘Β / πŸ‘Ž.

fengyang0317 and others added 7 commits March 16, 2026 01:25
…enclaw#40855)

Add support for alternative authentication methods in the MS Teams
channel, enabling passwordless bot authentication for enterprise
tenants that block client secrets via tenant-wide policy.

New config fields under channels.msteams:
- authType: 'clientSecret' (default) | 'certificate' | 'federatedCredential'
- certPemFile / certKeyFile / sendX5C: for certificate-based auth
- ficClientId: FIC client ID for federated credential auth
- widAssertionFile: workload identity assertion file path

Fully backward compatible: existing appId + appPassword configs
continue to work unchanged.
Test coverage for resolveMSTeamsCredentials and hasConfiguredMSTeamsCredentials
across all three auth types introduced in the preceding commit. Fixed existing
test expectation to include authType field in resolved credentials result.
Verifies certificate auth requires both cert files, federated auth requires
ficClientId or widAssertionFile, and clientSecret is the default authType.
Variable appPassword in MSTeamsCredentials (token.ts) was declared as
required but set to empty string for certificate and federated credential
auth paths. Changed to optional to accurately represent the type contract.
Guarded clientSecret assignment in sdk.ts to handle undefined appPassword.
Added test assertions verifying appPassword is undefined for certificate
and federated credential auth.
Type MSTeamsAuthType in token.ts was a standalone union duplicating the
inline type in MSTeamsConfig.authType (types.msteams.ts). Replaced with
NonNullable<MSTeamsConfig["authType"]> to maintain a single source of
truth. Adding a new auth method now only requires updating types.msteams.ts.
Fields authType, certPemFile, certKeyFile, sendX5C, ficClientId, and
widAssertionFile were added to MSTeamsConfig (types.msteams.ts) but not
to MSTeamsConfigSchema (zod-schema.providers-core.ts). The strict Zod
schema rejected configs using these fields as unrecognized properties.
Added cross-field validation in superRefine: authType "certificate"
requires certPemFile and certKeyFile, authType "federatedCredential"
requires ficClientId or widAssertionFile.
Error messages in errors.ts and probe.ts dropped references to environment
variables MSTEAMS_APP_ID and MSTEAMS_TENANT_ID when the new auth types were
added. Restored env var hints alongside the new auth type guidance so users
configuring via environment variables still get actionable error messages.
Added test assertions verifying env var names appear in auth error hint.
Added presence helpers that check certificate and federated credential
auth material across typed config, SDK plain env vars (certPemFile,
certKeyFile, FICClientId, WIDAssertionFile), and SDK connection env
vars (connections__*__settings__*). Credential resolution and
hasConfiguredMSTeamsCredentials now reject empty configs that have no
auth material in any source, preventing silent fallthrough to managed
identity auth in the SDK. Configs backed by SDK env vars are accepted.
@krrrke krrrke force-pushed the fix/msteams-auth-review branch from 507c826 to 5b3a11b Compare March 16, 2026 06:34
@krrrke
Copy link
Copy Markdown
Author

krrrke commented Mar 16, 2026

CI failures in this run are pre-existing issues on main, not related to this PR's changes:

  1. tsgo type error β€” normalizeAgentId not found in src/gateway/hooks.ts:276,349. This PR does not touch src/gateway/hooks.ts or any agent/gateway code.
  2. Startup memory check β€” status --json used 930 MB RSS vs 900 MB limit. This is a flaky resource constraint on the CI runner, unrelated to msteams extension changes.

Both failures reproduce on current main without this PR's commits. The msteams test suite passes locally (244/244 tests, pnpm build && pnpm check green except for the same pre-existing normalizeAgentId error).

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

πŸ’‘ Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 5b3a11ba67

ℹ️ About Codex in GitHub

Codex has been enabled to automatically 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 πŸ‘.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment on lines +107 to +108
certPemFile: cfg?.certPemFile,
certKeyFile: cfg?.certKeyFile,
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Normalize certificate values before constructing credentials

Fresh evidence in this commit: trimPresence is only used for presence checks, but resolveMSTeamsCredentials still copies raw cfg certificate strings into credentials, so a whitespace-only certPemFile/certKeyFile is forwarded instead of treated as missing. In mixed-source setups (valid env cert path + blank config value), buildMSTeamsAuthConfig then treats that whitespace as truthy and sets it on base, which can override the SDK env fallback and cause auth to fail even though usable env values exist.

Useful? React with πŸ‘Β / πŸ‘Ž.

Aligned certificate case in buildMSTeamsAuthConfig with federated case β€”
only assign certPemFile/certKeyFile to base when present, so undefined
values do not shadow the SDK's env var resolution. Added explicit test
assertions documenting that env-only cert paths are not forwarded in
credentials (SDK merges them at runtime).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

channel: msteams Channel integration: msteams size: M

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feature]: Support federated credentials / managed identity for MS Teams Bot Framework authentication

2 participants