From bc42519de6075ec9f60d1b3091ef0603b9ead74a Mon Sep 17 00:00:00 2001 From: Ryan Bas Date: Fri, 15 May 2026 12:37:51 -0600 Subject: [PATCH] fix(diagnosis): stop flagging client_id as prohibited inline PAR param Per RFC 9126, client_id is required alongside request_uri in the authorization request after a Pushed Authorization Request. The par:inline-params-with-request-uri rule incorrectly included client_id in its check, causing false positives on valid PAR flows. Co-Authored-By: Claude Opus 4.6 (1M context) --- .changeset/fix-par-inline-params.md | 7 ++++++ .../src/diagnosis/diagnosis-engine.test.ts | 24 +++++++++++++++++++ .../src/diagnosis/diagnosis-engine.ts | 3 +-- 3 files changed, 32 insertions(+), 2 deletions(-) create mode 100644 .changeset/fix-par-inline-params.md diff --git a/.changeset/fix-par-inline-params.md b/.changeset/fix-par-inline-params.md new file mode 100644 index 0000000..bd4253c --- /dev/null +++ b/.changeset/fix-par-inline-params.md @@ -0,0 +1,7 @@ +--- +'@wolfcola/devtools-core': patch +--- + +Fix PAR inline-params rule falsely flagging client_id alongside request_uri + +The `par:inline-params-with-request-uri` diagnosis rule incorrectly treated `client_id` as a prohibited inline parameter. Per RFC 9126, `client_id` is required alongside `request_uri` in the authorization request after a PAR. Only truly prohibited params (`redirect_uri`, `scope`, etc.) now trigger the warning. diff --git a/packages/devtools-core/src/diagnosis/diagnosis-engine.test.ts b/packages/devtools-core/src/diagnosis/diagnosis-engine.test.ts index db218f4..6384340 100644 --- a/packages/devtools-core/src/diagnosis/diagnosis-engine.test.ts +++ b/packages/devtools-core/src/diagnosis/diagnosis-engine.test.ts @@ -935,6 +935,30 @@ describe('PAR rules', () => { const result = runFlowRules(events); expect(result.some((i) => i.id === 'par:inline-params-with-request-uri')).toBe(true); }); + + it('does not flag request_uri with only client_id', () => { + const events = [ + makeNetworkEvent({ + id: 'par-valid', + data: { + _tag: 'network', + url: 'https://auth.example.com/authorize?request_uri=urn:x&client_id=app1', + method: 'GET', + status: 302, + requestHeaders: {}, + responseHeaders: {}, + duration: 50, + }, + oidcSemantics: { + _tag: 'oidc-semantics', + oidcPhase: 'authorize', + par: { requestUri: 'urn:x' }, + }, + }), + ]; + const result = runFlowRules(events); + expect(result.some((i) => i.id === 'par:inline-params-with-request-uri')).toBe(false); + }); }); // ─── Expired JWT via runEventRules ──────────────────────────────────────────── diff --git a/packages/devtools-core/src/diagnosis/diagnosis-engine.ts b/packages/devtools-core/src/diagnosis/diagnosis-engine.ts index 3f9134e..d319706 100644 --- a/packages/devtools-core/src/diagnosis/diagnosis-engine.ts +++ b/packages/devtools-core/src/diagnosis/diagnosis-engine.ts @@ -700,8 +700,7 @@ function collectParIssues(events: readonly AuthEvent[]): IssueCandidate[] { // Authorize with both request_uri AND inline params if (sem.oidcPhase === 'authorize' && sem.par?.requestUri && event.data._tag === 'network') { const url = event.data.url; - const hasInlineParams = - url.includes('client_id=') || url.includes('redirect_uri=') || url.includes('scope='); + const hasInlineParams = url.includes('redirect_uri=') || url.includes('scope='); if (hasInlineParams) { candidates.push({ dedupKey: `par:inline-params-with-request-uri:${event.id}`,