fix(msteams): address review feedback on #40884 β schema, types, env validation#47934
fix(msteams): address review feedback on #40884 β schema, types, env validation#47934krrrke wants to merge 8 commits intoopenclaw:mainfrom
Conversation
Greptile SummaryThis PR addresses review feedback on the federated credentials and certificate auth feature (#40884): it makes Key changes:
Potential issue found:
Confidence Score: 3/5
Prompt To Fix All With AIThis 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 |
extensions/msteams/src/sdk.ts
Outdated
| base.certPemFile = creds.certPemFile; | ||
| base.certKeyFile = creds.certKeyFile; | ||
| if (creds.sendX5C != null) base.sendX5C = creds.sendX5C; | ||
| break; |
There was a problem hiding this 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:
if (creds.ficClientId) base.FICClientId = creds.ficClientId;
if (creds.widAssertionFile) base.WIDAssertionFile = creds.widAssertionFile;Consider aligning the certificate case:
| 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.| 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; | ||
| } | ||
| }); |
There was a problem hiding this 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:
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.There was a problem hiding this comment.
π‘ 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".
extensions/msteams/src/token.ts
Outdated
| const hasPem = Boolean(cfg?.certPemFile || env.certPemFile); | ||
| const hasKey = Boolean(cfg?.certKeyFile || env.certKeyFile); |
There was a problem hiding this comment.
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 πΒ / π.
β¦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.
507c826 to
5b3a11b
Compare
|
CI failures in this run are pre-existing issues on
Both failures reproduce on current |
There was a problem hiding this comment.
π‘ 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".
extensions/msteams/src/token.ts
Outdated
| certPemFile: cfg?.certPemFile, | ||
| certKeyFile: cfg?.certKeyFile, |
There was a problem hiding this comment.
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).
5b3a11b to
597e428
Compare
Summary
Builds on #40884 (federated credentials and certificate auth support) to fix review issues and add env-backed auth validation:
appPasswordoptional β was required but set to empty string for non-secret auth; now optional inMSTeamsCredentialsMSTeamsAuthTypeβ derived fromMSTeamsConfig["authType"]instead of a standalone union in two filesauthType,certPemFile,certKeyFile,sendX5C,ficClientId,widAssertionFileadded toMSTeamsConfigSchema(P1 blocker β configs using these fields were rejected)MSTEAMS_APP_ID/MSTEAMS_TENANT_IDalongside new auth type guidanceNo 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)
authTypeis not auto-inferred from environment β ifauthTypeis omitted,resolveMSTeamsCredentialsdefaults toclientSecreteven when certificate/federated env vars are present. Requires a unified auth-source resolution helper.hasConfiguredMSTeamsCredentialsdoes not checkMSTEAMS_APP_ID/MSTEAMS_TENANT_IDenv 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.These will be resolved in a follow-up PR that introduces a shared
readTeamsAuthPresencehelper andDefaultAzureCredentialsupport.AI Disclosure
pnpm build && pnpm check && pnpm testgreenFixes #40855
Related: #40884