Skip to content

feat: auth/pkce - allow extra token request params - #114

Merged
odsamuels merged 3 commits into
mainfrom
odsamuels/feat/implement-tokenRequestParams-flexibility
Aug 15, 2026
Merged

feat: auth/pkce - allow extra token request params#114
odsamuels merged 3 commits into
mainfrom
odsamuels/feat/implement-tokenRequestParams-flexibility

Conversation

@odsamuels

@odsamuels odsamuels commented Aug 15, 2026

Copy link
Copy Markdown
Contributor

Problem

createPkceProvider hard-coded the token endpoint request body to the standard PKCE fields (grant_type, code, redirect_uri, client_id, code_verifier). Providers like Zendesk require additional provider-specific parameters on the authorization_code token request (e.g. expires_in, refresh_token_expires_in to control max token lifetimes), and there was no way to inject them.

🚨 As a result of what is likely Zendesk's token revamping efforts, CXers/users of the Zendesk CLI were running into failures every ~30 minutes and directed to re-auth.

Solution

This PR adds an optional tokenRequestParams option to PkceProviderOptions. It's a function receiving { handshake, flags } (same context as the other lazy resolvers) and returning a record of extra form-encoded parameters, synchronously or as a promise. exchangeCode resolves it, filters out undefined values, stringifies the rest, and merges them into the token request body alongside the standard PKCE fields.

This allows the internal Zendesk CLI to request tokens for the maximum expiration time, instead of the Zendesk-set default.

The module remains backward compatible with existing consumers.

Reference

Test Plan

  • Confirm tokenRequestParams is optional — existing providers (Outline, Todoist) that don't set it continue to exchange tokens with just the standard PKCE body fields
  • Configure a provider with tokenRequestParams: () => ({ expires_in: 172800, refresh_token_expires_in: 7776000 }) and verify the token POST body includes both extra fields alongside grant_type, code, redirect_uri, client_id, code_verifier

Device and Browser Testing

@odsamuels odsamuels self-assigned this Aug 15, 2026
The lock file had drifted out of sync with package.json (missing
@emnapi optional entries, stale libc metadata), causing npm ci to
fail in CI. No dependency changes were needed for this feature.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

@doistbot doistbot left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This PR adds an optional tokenRequestParams resolver to PkceProviderOptions, letting providers inject extra form-encoded parameters into the PKCE token exchange request body.

I also included a few optional follow-up notes in the details below.

Optional follow-up notes (3)
  • P3 src/auth/providers/pkce.test.ts:84: The test verifies extra params appear in the POST body but doesn't cover two scenarios the PR description lists: (a) a param resolving to undefined being omitted (not sent as the string "undefined"), and (b) the callback receiving the correct handshake and flags from the in-progress exchange. Consider adding these cases.
  • P3 src/auth/providers/pkce.ts:153: The Object.fromEntries(Object.entries(...).filter(...).map(...)) chain hand-rolls filtering+stringifying that the standard URLSearchParams.set() API already handles — and is the pattern already used in buildPkceAuthorizeUrl (oauth.ts:64-65) for the same purpose. Consider a simpler loop consistent with that existing code: ts const body = new URLSearchParams({ grant_type: 'authorization_code', code: input.code, redirect_uri: input.redirectUri, client_id: clientId, code_verifier: verifier, }) for (const [key, value] of Object.entries(extraTokenParams)) { if (value !== undefined) body.set(key, String(value)) }
  • P3 src/auth/providers/pkce.ts:140: tokenUrl and tokenRequestParams are independent resolvers that only read input.handshake and flags, but they're awaited sequentially here. Both can be async (config read / prompt), so this serializes two async steps that could run concurrently. The authorize and refreshToken paths already parallelize their independent lazy resolvers with Promise.all; matching that here (e.g. const [tokenUrl, extraTokenParams] = await Promise.all([resolve(options.tokenUrl, ...), options.tokenRequestParams?.({...}) ?? {}])) avoids adding latency to the exchange when both are async.

Share FeedbackReview Logs

Comment thread src/auth/providers/pkce.ts
@odsamuels

Copy link
Copy Markdown
Contributor Author

@scottlovegrove - I'm going to go ahead and merge this, as I'm trying to extinguish an incident, and other functionality is dependent on getting this out.

The impact and risk are expected to be very low.

An async review is very welcome, as it's my first time dabbling in this repo. Thanks. 🙏

@odsamuels
odsamuels merged commit f6922f6 into main Aug 15, 2026
5 checks passed
@odsamuels
odsamuels deleted the odsamuels/feat/implement-tokenRequestParams-flexibility branch August 15, 2026 04:25
doist-release-bot Bot added a commit that referenced this pull request Aug 15, 2026
## [1.2.0](v1.1.0...v1.2.0) (2026-08-15)

### Features

* auth/pkce - allow extra token request params ([#114](#114)) ([f6922f6](f6922f6))
@doist-release-bot

Copy link
Copy Markdown
Contributor

🎉 This PR is included in version 1.2.0 🎉

The release is available on:

Your semantic-release bot 📦🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants