feat: support maxAge authorization param (@workos-inc/node >= 10.6)#49
Conversation
Dead code: nothing imported ./core/client/types.js and it was not exposed via package.json exports. Its AuthorizationURLOptions had also drifted from the SDK (wrong clientId optionality, unbound PKCE fields). Public types come straight from @workos-inc/node.
Surface the OIDC max_age authorization-URL param (added to UserManagementAuthorizationURLOptions in @workos-inc/node v10.6.0) while keeping the wide ^8 || ^9 || ^10 peer range.
GetAuthorizationUrlOptions gains maxAge via a version-gated conditional type: it resolves to { maxAge?: number } when the installed SDK supports the field and { maxAge?: never } otherwise, so the option is visible exactly when it works -- no false promise on older peers. The value is forwarded to getAuthorizationUrl only when provided.
- bump devDependency @workos-inc/node to ^10.6.0 (peer range unchanged)
- add tests: forwarded when set, omitted when unset
| type VersionedAuthParams = | ||
| 'maxAge' extends keyof UserManagementAuthorizationURLOptions | ||
| ? { maxAge?: number } |
There was a problem hiding this comment.
🚩 Conditional type depends on UserManagementAuthorizationURLOptions existing in all supported peer versions
The VersionedAuthParams conditional type (src/core/session/types.ts:243-245) imports UserManagementAuthorizationURLOptions from @workos-inc/node. The library's peerDependencies accept ^8.0.0 || ^9.0.0 || ^10.0.0. When this library is published, the emitted .d.ts will contain this import. If a consumer uses a peer version (e.g. v8.x or v9.x) where UserManagementAuthorizationURLOptions is not an exported type, their TypeScript build will fail on the import — before the conditional type can even evaluate. The PR's design comment says the conditional type is read "from the consumer's installed version at their compile time," which works correctly if the type exists in all supported peers (just with varying keys). Worth verifying that v8 and v9 of the SDK actually export this type name.
Was this helpful? React with 👍 or 👎 to provide feedback.
There was a problem hiding this comment.
Good flag — this is the right invariant to check. Verified it holds: UserManagementAuthorizationURLOptions is exported across the entire ^8 || ^9 || ^10 peer range, so the emitted .d.ts import resolves on every supported peer (the conditional then varies only by available keys, as intended).
Checked against the published artifacts a consumer actually installs — import type { UserManagementAuthorizationURLOptions } plus a tsc --noEmit resolution test:
| published peer | import resolves | 'maxAge' in keyof → branch |
|---|---|---|
| 8.0.0 | ✅ | no → { maxAge?: never } |
| 8.13.0 | ✅ | no → { maxAge?: never } |
| 9.0.0 | ✅ | no → { maxAge?: never } |
| 10.7.0 | ✅ | yes → { maxAge?: number } |
The test imports the type directly (export type _ = UserManagementAuthorizationURLOptions), so a missing export would surface as "Module has no exported member" — it didn't on any version. (v8.0.0 initially showed unrelated errors from a transitive jose .d.ts under the default tsc target; isolating with --skipLibCheck confirmed our import resolves.)
Export chain is stable across the range:
index.ts → export * from './user-management/interfaces' → export * from './authorization-url-options.interface' (which declares export type UserManagementAuthorizationURLOptions).
Bonus: on v8/v9 'maxAge' is genuinely absent from the type's keys, so the conditional resolves to { maxAge?: never } — the option is correctly hidden (and rejected) on older peers, which is exactly the intended behavior.
Greptile SummaryThis PR exposes the WorkOS
Confidence Score: 5/5The maxAge authorization URL path is implemented and covered by focused runtime and type checks. No code issues were identified. The changed behavior is narrow: the new option is conditionally exposed in types, forwarded only when defined, and omitted otherwise. Targeted checks cover the main runtime behavior and TypeScript contract. None.
What T-Rex did
Reviews (2): Last reviewed commit: "refactor: keep GetAuthorizationUrlOption..." | Re-trigger Greptile |
… gate from SDK method Addresses PR review feedback: - Keep GetAuthorizationUrlOptions an interface (it had become a type alias), so consumers can still extend it via module-augmentation declaration merging. - Derive the SDK options type from the getAuthorizationUrl method via Parameters<> instead of importing UserManagementAuthorizationURLOptions by name, so the version gate never depends on that type being exported by a given peer -- only on the method existing, which it does across ^8 || ^9 || ^10. maxAge stays version-gated: number on @workos-inc/node >= 10.6, never on older peers. Verified against published 8.0.0 / 8.13.0 / 9.0.0.
What
Adds support for the
maxAgeauthorization-URL parameter (OIDCmax_age) on the sign-in/sign-up/authorization flow, and removes a dead types file.Why
maxAgewas added toUserManagementAuthorizationURLOptionsin @workos-inc/node v10.6.0. We want to expose it without raising the peer-dependency floor — the package still supports the full^8 || ^9 || ^10range, so consumers aren't forced into a breaking SDK major upgrade for one optional param.How
GetAuthorizationUrlOptionsgainsmaxAgevia a version-gated conditional type incore/session/types.ts:>= 10.6peers it resolves to{ maxAge?: number }— settable, and forwarded togetAuthorizationUrl.^8/^9/<10.6peers the SDK type lacks the key, so it resolves to{ maxAge?: never }— the option is rejected at compile time rather than advertised and silently dropped at runtime (the SDK whitelists params and discards unknown keys).keyofis read from the consumer's installed@workos-inc/nodeat their compile time, so the surface tracks the peer. The value is forwarded only when provided:...(options.maxAge !== undefined && { maxAge: options.maxAge }).The devDependency is bumped to
^10.6.0so authkit's own build sees the field; the peerDependency range is unchanged.Commits
refactor:remove unusedcore/client/types.ts— dead code (no importers, not exposed viaexports), and a drifted copy of SDK types.feat:maxAgesupport.Testing
tsc --noEmit✅ ·tsc -p tsconfig.build.json✅ ·vitest --run273 passed (+2) ✅ ·oxlint0 warnings / 0 errors ✅maxAgeforwarded when set, omitted when unset.maxAgeaccepted on a>=10.6peer and rejected on an older peer for both fresh object literals and pre-built option objects (theneverbranch is what makes the non-literal case honest — an empty object type would only block fresh literals).