v2.0.1 — OAuth 2.0 via Public Client Self-registration
Adds OAuth 2.0 Authorization Code flow (with PKCE) as a second authentication option alongside the existing Personal Access Token (PAT) path. Both paths are first-class and fully supported; OAuth is preferred for fresh installs because it offers rotation, per-user audit trail, and browser-based onboarding instead of admin-issued tokens.
Added
- OAuth 2.0 authentication via Public Client Self-registration (RFC 7591). First-run flow: the MCP dynamically registers itself as a public OAuth client at
POST https://app.productboard.com/oauth2/register(no manual app registration needed), then opens a browser-based scope chooser (Read only / Read+Write / Full), then runs the standard Productboard authorize-and-consent flow with PKCE. Tokens are persisted to the platform-native cache directory with file perms0600and refreshed proactively (5-minute buffer before expiry) and reactively (one retry after a 401). The 60-minute refresh-token grace window in Productboard's OAuth implementation is leveraged to handle multi-process token contention without explicit file locking. The dynamically registeredclient_idis persisted separately toregistration.jsonso deletingtokens.jsonto re-authorize does not burn a registration quota slot (PB rate-limits registration to 5/min, 50/day per IP). PRODUCTBOARD_AUTH_MODEenv var. Optional.oauthforces OAuth even ifPRODUCTBOARD_ACCESS_TOKENis set;patrequires the env var. Unset → auto (priority tree: PAT env > OAuth tokens.json > setup flow).PRODUCTBOARD_OAUTH_CLIENT_IDenv var. Optional advanced override. Pre-register your own custom-branded OAuth app in your PB workspace and set this env var to bypass the self-registration step. Most users don't need it.PRODUCTBOARD_OAUTH_CALLBACK_PORTenv var. Optional override of the default7779callback port (also re-register the new URL in your PB OAuth app if using your own pre-registered client).PRODUCTBOARD_OAUTH_TOKEN_PATHenv var. Optional override of the tokens.json location (Docker volumes, multi-tenant test setups).PRODUCTBOARD_OAUTH_REGISTRATION_PATHenv var. Optional override of the registration.json location.PRODUCTBOARD_OAUTH_SCOPESenv var. Optional space- or comma-separated list of scopes; bypasses the chooser page.
Changed
apiRequest/v1ApiRequestare now Bearer-source-agnostic. They consult an injectedAuthResolutioninstead of readingPRODUCTBOARD_ACCESS_TOKENdirectly. PAT mode preserves the previous behavior byte-for-byte.- HTTP 401 now triggers a refresh+retry once in OAuth mode, or a structured "switch to OAuth" hint in PAT mode (instead of a raw
Bad tokenerror). - Bearer values are now validated client-side before assembling the Authorization header: leading/trailing whitespace and embedded CR/LF are rejected with a clean error. This prevents the kind of "Productboard PAT label + newline pasted into env" mishap from echoing the token back in an HTTP-header-validation exception.
Internal
- New
src/auth/directory:types.ts,token-store.ts,oauth-setup.ts,oauth-refresh.ts,oauth-register.ts,resolver.ts. - No new npm dependencies. PKCE uses Node's
crypto; the callback listener uses Node'shttp; browser launch useschild_process.spawn; Dynamic Client Registration uses Node'sfetch.
Migration notes for callers
- Nothing breaks. Existing deployments with
PRODUCTBOARD_ACCESS_TOKENset continue to use PAT auth unchanged. - Fresh installs without an env var will self-register as an OAuth public client and open a browser at first start. Users complete the scope chooser + authorize once; both
registration.jsonandtokens.jsonpersist across restarts and refresh automatically. - Dr.Max tars users: the new package version will be picked up by
roles.jsononce bumped; the OAuth migration happens in tars in a separate phased rollout (see the design spec).
Full changelog: CHANGELOG.md
npm: https://www.npmjs.com/package/@drmaxbdc/productboard-mcp/v/2.0.1