feat(typescript): support omitting username/password in basic auth when configured in IR#14406
Conversation
…onfigured in IR Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
🤖 Devin AI EngineerI'll be helping with this pull request! Here's what you should know: ✅ I will automatically:
Note: I can only respond to comments from users who have write access to this repository. ⚙️ Control Options:
|
There was a problem hiding this comment.
Claude Code Review
This repository is configured for manual code reviews. Comment @claude review to trigger a review and subscribe this PR to future pushes, or @claude review once for a one-time review.
Tip: disable this comment in your organization's Code Review settings.
…d flag Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
…-field omit fix Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
…c-auth-optional-ts-sdk
…e empty string internally Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
There was a problem hiding this comment.
🟡 Generator emits unused USERNAME_PARAM/PASSWORD_PARAM constants for omitted auth fields
In writeConstants at BasicAuthProviderGenerator.ts:159-170, both USERNAME_PARAM and PASSWORD_PARAM are always emitted regardless of omit flags. When a field is omitted (e.g. passwordOmit: true), the corresponding constant is never referenced in canCreate, getAuthRequest, or the AuthOptions type — it becomes dead code.
The generated seed output at seed/ts-sdk/basic-auth-optional/src/auth/BasicAuthProvider.ts:7 confirms this: const _PASSWORD_PARAM = "password" as const; — the linter renamed it with an underscore prefix because it's unused. The fix would be to conditionally skip generating the constant when the corresponding omit flag is true.
(Refers to lines 169-170)
Was this helpful? React with 👍 or 👎 to provide feedback.
There was a problem hiding this comment.
Valid observation about the unused constant. This is a minor issue — the generated biome config already renames it with an underscore prefix (_PASSWORD_PARAM), and the constant is still referenced in the module namespace for the AUTH_CONFIG_ERROR_MESSAGE_PASSWORD string template in the non-omit case. Conditionally skipping the constant would require tracking which fields are omitted across both writeConstants and writeOptions, adding complexity for a cosmetic improvement. I'll leave this as-is unless the maintainer wants it addressed.
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
…teral property names Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
| if (username === "" && password === "") { | ||
| return undefined; | ||
| } |
There was a problem hiding this comment.
🟡 BasicAuth.toAuthorizationHeader silently drops header for empty-string credentials in all existing SDKs
The shared core utility template at generators/typescript/utils/core-utilities/src/core/auth/BasicAuth.ts now returns undefined when both username and password are empty strings (line 17). Previously, toAuthorizationHeader({username: "", password: ""}) would return "Basic Og==" (base64 of :), which is valid HTTP Basic Auth. This behavioral change propagates to all generated SDKs using basic auth, including the existing seed/ts-sdk/basic-auth SDK which had no omit fields. If an existing SDK user provides a Supplier<string> that resolves to "" for both credentials, the Authorization header is silently dropped instead of being sent, changing wire behavior.
Was this helpful? React with 👍 or 👎 to provide feedback.
There was a problem hiding this comment.
This is an intentional design choice: when both fields resolve to empty strings, there's no meaningful credential to send, so the header is omitted. In practice this only triggers when both usernameOmit and passwordOmit are true (the generator passes "" directly for omitted fields). For the standard basic-auth case (no omit flags), both fields are required and will never be "". A user explicitly passing { username: "", password: "" } was already sending useless auth (Basic Og==), so this change is a safe improvement.
…lientOptions Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
| omitted entirely. | ||
| type: feat | ||
| createdAt: "2026-04-01" | ||
| irVersion: 65 |
There was a problem hiding this comment.
🔴 versions.yml entry uses irVersion 65 instead of 66, contradicting the 3.61.x series
Version 3.61.2 in generators/typescript/sdk/versions.yml specifies irVersion: 65, but the immediately preceding version 3.61.1 (and 3.61.0-rc.0) both use irVersion: 66. The V66-to-V65 IR migration at packages/cli/generation/ir-migrations/src/migrations/v66-to-v65/migrateFromV66ToV65.ts:17 marks 3.61.0-rc.0 as the firstGeneratorVersionToConsumeNewIR for the TypeScript SDK, meaning all versions ≥ 3.61.0-rc.0 are expected to consume IR v66. Additionally, the seed test runner config at seed/ts-sdk/seed.yml:2 already uses irVersion: v66. Per REVIEW.md: "IR version bumps must be coordinated: update irVersion in the relevant versions.yml entry." Using irVersion 65 for a version after the v66 upgrade is inconsistent and could cause IR version coordination issues.
| irVersion: 65 | |
| irVersion: 66 |
Was this helpful? React with 👍 or 👎 to provide feedback.
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
| encodes `:password`). When neither is provided, the Authorization header is | ||
| omitted entirely. | ||
| type: feat | ||
| createdAt: "2026-04-01" |
There was a problem hiding this comment.
🟡 versions.yml entry for 3.61.2 has createdAt date earlier than the prior version 3.61.1
Version 3.61.2 has createdAt: "2026-04-01" while the lower version 3.61.1 has createdAt: "2026-04-02". A newer version should have a creation date equal to or later than the version it follows. This is likely a typo — the date should be "2026-04-02" or later.
| createdAt: "2026-04-01" | |
| createdAt: "2026-04-02" |
Was this helpful? React with 👍 or 👎 to provide feedback.
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
…c-auth-optional-ts-sdk
There was a problem hiding this comment.
🟡 Generator emits unused PASSWORD_PARAM/USERNAME_PARAM constants for omitted basic auth fields
When usernameOmit or passwordOmit is true, writeConstants() at BasicAuthProviderGenerator.ts:169-170 still unconditionally emits the USERNAME_PARAM / PASSWORD_PARAM constant declarations. However, generateCanCreateStatements() (line 269-274), generateGetAuthRequestStatements() (line 333-343), and writeOptions() (line 441-443) all correctly skip referencing the omitted field's constant — meaning the emitted constant is never used in the generated output.
This is confirmed by the generated seed output at seed/ts-sdk/basic-auth-optional/src/auth/BasicAuthProvider.ts:7, where biome's linter renames the unused constant from PASSWORD_PARAM to _PASSWORD_PARAM. While this doesn't cause a runtime failure, it produces unnecessary declarations that trigger linter warnings or auto-renames in generated SDKs.
(Refers to lines 169-170)
Prompt for agents
In writeConstants() in BasicAuthProviderGenerator.ts, the USERNAME_PARAM and PASSWORD_PARAM constants are emitted unconditionally at lines 169-170. When usernameOmit or passwordOmit is true, the corresponding constant is never referenced by any other generated code (canCreate, getAuthRequest, writeOptions all skip omitted fields). The fix is to conditionally emit each constant only when the field is not omitted. Read this.authScheme.usernameOmit and this.authScheme.passwordOmit (as done in other methods like generateCanCreateStatements) and wrap each constants.push() call in an if (!omit) guard. This aligns with the pattern already used throughout the rest of the class.
Was this helpful? React with 👍 or 👎 to provide feedback.
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
… seed output Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
| if (username === "" && password === "") { | ||
| return undefined; | ||
| } |
There was a problem hiding this comment.
🟡 toAuthorizationHeader silently changes behavior for all regenerated SDKs when both credentials are empty strings
The BasicAuth.toAuthorizationHeader utility is a template copied into every generated TypeScript SDK (~80+ seed SDKs reference this file). The new code returns undefined when both username and password are empty strings "", whereas the old code would encode ":" and return "Basic Og==". This behavioral change propagates to ALL regenerated SDKs — not just the new basic-auth-optional ones — because the template at generators/typescript/utils/core-utilities/src/core/auth/BasicAuth.ts is shared.
In existing non-optional SDKs, the generated auth provider validates credentials with if (username == null), which empty strings pass. So a user providing { username: "", password: "" } would previously get a Basic Og== Authorization header sent, but now gets no header at all. The versions.yml changelog for 3.62.1 does not mention this cross-cutting behavioral change.
Was this helpful? React with 👍 or 👎 to provide feedback.
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
| `Please provide '${PASSWORD_PARAM}' when initializing the client` as const; | ||
| export type Options = AuthOptions; | ||
| export type AuthOptions = { [USERNAME_PARAM]: core.Supplier<string>; [PASSWORD_PARAM]: core.Supplier<string> }; | ||
| export type AuthOptions = { username: core.Supplier<string>; password: core.Supplier<string> }; |
There was a problem hiding this comment.
we should continue using the [USERNAME_PARAM] const and pwd
There was a problem hiding this comment.
Fixed — restored computed property keys [USERNAME_PARAM] and [PASSWORD_PARAM] in the AuthOptions type for both the generator code and all seed outputs/snapshots. See commit a62122c.
…D_PARAM] in AuthOptions type Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Description
Refs #14378
Adds conditional support for omitting username/password in basic auth for the TypeScript SDK generator. When
usernameOmitorpasswordOmitflags are set in the IR'sBasicAuthScheme, the corresponding field is completely removed from the generated SDK'sAuthOptionstype — not just made nullable. Internally, the omitted field is treated as an empty string when encoding the auth header (base64(username:password)with the colon always present).Default behavior (both required) is preserved when no omit flags are set.
Split from #14378 (one PR per generator).
Changes Made
Generator (
BasicAuthProviderGenerator.ts)getAuthOptionsProperties: conditionally excludes each field from theAuthOptionstype entirely when its corresponding omit flag is set (field is removed from the end-user API)generateCanCreateStatements: per-field checks — omitted fields always satisfycanCreate(returntrue), required fields must be presentgenerateGetAuthRequestStatements: refactored intobuildNullCheckshelper — omitted fields use""directly instead of reading from options; null checks are only generated for non-omitted fields; declarations and null checks are interleaved to preserve short-circuit evaluation orderCore utility (
BasicAuth.ts)username?: string; password?: stringunconditionallytoAuthorizationHeaderuses?? ""fallbacks and returnsundefinedwhen both are empty stringsTests & fixtures
basic-auth-pw-omittedtest fixture withpassword: omit: trueseed/ts-sdk/basic-auth-pw-omitted/seed/ts-sdk/basic-auth/output (reflects widenedBasicAuth.tsinterface)Changelog
versions.yml: new 3.62.2 entry (irVersion: 66)Updates since last revision
basic-auth-optional→basic-auth-pw-omittedacross all directories, filenames, and file contents (kebab-case, camelCase, PascalCase variants). The name now accurately reflects what the fixture tests: password omission, not generic "optional" behavior.?markers for non-mandatory auth).buildNullChecksnow interleaves declaration and null-check for each field, preserving the original short-circuit behavior where the password supplier is never evaluated if the username is null.versions.yml(bumped through 3.61.2 → 3.62.2 after main merged Error.cause, test import paths, and other features).Testing
BasicAuth.test.ts: 5 cases covering all credential combinations)basic-auth-pw-omittedfixturebasic-authseed output updated and verifiedBasicAuth.tsinterface is widened unconditionally: Every generated TS SDK (not just those with omit flags) will now haveusername?: string; password?: stringin the core utility. The generator controls what appears inAuthOptions, so the runtime behavior is guarded — but this does widen the type contract for downstream consumers who importBasicAuthdirectly.toAuthorizationHeader({username: "", password: ""})now returnsundefinedinstead ofBasic Og==. This affects theauthHeader != nullguard in generated code.usernameOmitandpasswordOmitare true,canCreate()always returnstrueandgetAuthRequesthardcodes both to"", producingBasic Og==— buttoAuthorizationHeaderthen returnsundefinedsince both are empty. Verify this no-op edge case is acceptable.AuthProviders.test.ts.snapdiff shows null-check blocks collapsed to single lines andAuthOptionstype properties changed from computed names ([USERNAME_PARAM]?:) to literal names (username?:). Verify this is acceptable generated output.seed/ts-sdk/basic-auth/diff: The existing (non-omit) fixture output changed due to the unconditionalBasicAuth.tswidening. Confirm the generated client behavior is still correct for the required-fields case.irVersion: 66. Verify version ordering is correct relative to main.Link to Devin session: https://app.devin.ai/sessions/0786b963284f4799acb409d5373cde0a
Requested by: @Swimburger