fix: stop mutating token refresh errors into auth errors#329
Conversation
|
Important Review skippedAuto incremental reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
WalkthroughThe changes fix issue Changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes 🚥 Pre-merge checks | ✅ 5 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #329 +/- ##
==========================================
- Coverage 85.59% 85.48% -0.11%
==========================================
Files 26 26
Lines 1270 1309 +39
Branches 250 265 +15
==========================================
+ Hits 1087 1119 +32
- Misses 110 112 +2
- Partials 73 78 +5 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
Pull request overview
This PR fixes token refresh failures being surfaced as mutated FgaApiError objects (breaking instanceof FgaApiAuthenticationError) by constructing and throwing a real FgaApiAuthenticationError, and adds a regression test for the failure mode described in #328.
Changes:
- Add a regression test asserting token refresh failures throw a real
FgaApiAuthenticationError. - Replace error-instance mutation in
Credentials.refreshAccessToken()with explicitFgaApiAuthenticationErrorconstruction, preserving auth context fields.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
| tests/credentials.test.ts | Adds a regression test ensuring token refresh failures reject with FgaApiAuthenticationError. |
| credentials/credentials.ts | Stops mutating FgaApiError instances and instead throws a newly constructed FgaApiAuthenticationError with auth context. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
|
Addressed the review comments and pushed commit What changed:
Validated locally:
|
There was a problem hiding this comment.
🤖 Fix all issues with AI agents
Verify each finding against the current code and only fix it if needed.
In `@errors.ts`:
- Around line 217-221: The constructor for the error class currently ignores the
optional context when err is an AxiosError; update the AxiosError branch in the
constructor to apply context overrides (clientId, audience, grantType) after
extracting values from the parsed request data so that if
context.clientId/audience/grantType is provided it replaces the parsed value;
locate the AxiosError handling block in the constructor and merge values (e.g.,
finalClientId = context?.clientId ?? parsedClientId) for each of the three
fields and use those merged values where the code currently uses the parsed
ones.
In `@tests/credentials.test.ts`:
- Around line 541-569: Add assertions to the existing test that checks thrown
error properties: after awaiting
expect(credentials.getAccessTokenHeader()).rejects.toThrow(FgaApiAuthenticationError)
also capture the rejected error and assert its context fields and status code
are set correctly (e.g., verify error.clientId equals OPENFGA_CLIENT_ID,
error.audience equals OPENFGA_API_AUDIENCE, error.grantType equals
CredentialsMethod.ClientCredentials or the literal grant type string used, and
error.statusCode equals 404). Use the same test-local variables (apiTokenIssuer,
OPENFGA_CLIENT_ID, OPENFGA_API_AUDIENCE) and the
Credentials.getAccessTokenHeader / FgaApiAuthenticationError symbols to locate
where to add these assertions.
🧹 Nitpick comments (2)
🤖 Fix all nitpicks with AI agents
Verify each finding against the current code and only fix it if needed. In `@errors.ts`: - Around line 217-221: The constructor for the error class currently ignores the optional context when err is an AxiosError; update the AxiosError branch in the constructor to apply context overrides (clientId, audience, grantType) after extracting values from the parsed request data so that if context.clientId/audience/grantType is provided it replaces the parsed value; locate the AxiosError handling block in the constructor and merge values (e.g., finalClientId = context?.clientId ?? parsedClientId) for each of the three fields and use those merged values where the code currently uses the parsed ones. In `@tests/credentials.test.ts`: - Around line 541-569: Add assertions to the existing test that checks thrown error properties: after awaiting expect(credentials.getAccessTokenHeader()).rejects.toThrow(FgaApiAuthenticationError) also capture the rejected error and assert its context fields and status code are set correctly (e.g., verify error.clientId equals OPENFGA_CLIENT_ID, error.audience equals OPENFGA_API_AUDIENCE, error.grantType equals CredentialsMethod.ClientCredentials or the literal grant type string used, and error.statusCode equals 404). Use the same test-local variables (apiTokenIssuer, OPENFGA_CLIENT_ID, OPENFGA_API_AUDIENCE) and the Credentials.getAccessTokenHeader / FgaApiAuthenticationError symbols to locate where to add these assertions.errors.ts (1)
217-221:contextparameter is silently ignored whenerris anAxiosError.The AxiosError branch (lines 264–272) extracts
clientId,audience, andgrantTypeonly from parsed request data, ignoring thecontextargument. Today no caller passes context with an AxiosError, but a future caller could hit this silently. Consider applying context overrides in the AxiosError branch as well, or documenting that context is only used forFgaApiErrorinputs.Proposed fix
- this.clientId = data?.client_id; - this.audience = data?.audience; - this.grantType = data?.grant_type; + this.clientId = context?.clientId || data?.client_id; + this.audience = context?.audience || data?.audience; + this.grantType = context?.grantType || data?.grant_type;Also applies to: 253-272
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@errors.ts` around lines 217 - 221, The constructor for the error class currently ignores the optional context when err is an AxiosError; update the AxiosError branch in the constructor to apply context overrides (clientId, audience, grantType) after extracting values from the parsed request data so that if context.clientId/audience/grantType is provided it replaces the parsed value; locate the AxiosError handling block in the constructor and merge values (e.g., finalClientId = context?.clientId ?? parsedClientId) for each of the three fields and use those merged values where the code currently uses the parsed ones.tests/credentials.test.ts (1)
541-569: Good regression test — consider asserting error properties too.The
instanceofcheck directly validates the fix for#328. For stronger coverage, you could also verify that context fields (clientId,audience,grantType) andstatusCodeare correctly propagated on the thrown error.Example: assert error properties
- await expect(credentials.getAccessTokenHeader()).rejects.toThrow(FgaApiAuthenticationError); + await expect(credentials.getAccessTokenHeader()).rejects.toSatisfy((err: FgaApiAuthenticationError) => { + expect(err).toBeInstanceOf(FgaApiAuthenticationError); + expect(err.statusCode).toBe(404); + expect(err.clientId).toBe(OPENFGA_CLIENT_ID); + expect(err.audience).toBe(OPENFGA_API_AUDIENCE); + expect(err.grantType).toBe("client_credentials"); + return true; + });🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@tests/credentials.test.ts` around lines 541 - 569, Add assertions to the existing test that checks thrown error properties: after awaiting expect(credentials.getAccessTokenHeader()).rejects.toThrow(FgaApiAuthenticationError) also capture the rejected error and assert its context fields and status code are set correctly (e.g., verify error.clientId equals OPENFGA_CLIENT_ID, error.audience equals OPENFGA_API_AUDIENCE, error.grantType equals CredentialsMethod.ClientCredentials or the literal grant type string used, and error.statusCode equals 404). Use the same test-local variables (apiTokenIssuer, OPENFGA_CLIENT_ID, OPENFGA_API_AUDIENCE) and the Credentials.getAccessTokenHeader / FgaApiAuthenticationError symbols to locate where to add these assertions.
|
Addressed the remaining CodeRabbit suggestions in commit Changes made:
Validation run:
|
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
|
Followed up on Copilot review #329 (review) in commit Actionable fixes applied:
Validation run:
|
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 3 out of 3 changed files in this pull request and generated 3 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
|
Addressed actionable feedback from #329 (review) in commit Applied changes:
Validation run:
|
Conflicts: * errors.ts — main brought in PR #329's "stop mutating token refresh errors into auth errors" fix. Adapted for the fetch-era branch: FgaApiAuthenticationError now accepts HttpErrorContext | FgaApiError. When given an existing FgaApiError (re-wrap path from credentials/), it COPIES fields and preserves the original stack — the canonical fix for the audit-confirmed `instanceof` mutation P0. The HttpErrorContext path is unchanged. Also added the optional `context?: { clientId, audience, grantType }` second parameter from main so callers can override extracted values. * package.json — took main's bumped @opentelemetry/api ^1.9.1; left axios out (the whole point of this branch). * package-lock.json — regenerated via `npm install` after resolving package.json (don't hand-merge lockfiles). Removed two helper functions (parseRequestData, getAuthenticationErrorMessage) that main introduced for the AxiosError path; the fetch branch doesn't have AxiosError as a type, and the helpers had no other callers.
Pulls the fetch-based HTTP client + the FgaApiAuthenticationError re-wrap path (PR #329) into the WIP ESM/CJS dual-build branch. Conflicts: * package.json — kept ESM build setup (main / module / exports fields, build:cjs / build:esm / build:post scripts) AND took drop-axios's newer dep versions inherited from main: jest 30.3.0, ts-jest 29.4.9, eslint ^10.3.0, typescript ^6.0.3, nock ^14.0.14, @types/node ^25.6.0, @typescript-eslint/* ^8.59.2, @opentelemetry/api ^1.9.1. Removed tiny-async-pool + @types/tiny-async-pool (this branch already replaced tiny-async-pool with a native impl). * package-lock.json — regenerated via \`npm install\`. 0 axios entries, 0 tiny-async-pool entries, 0 vulnerabilities. * tests/apiExecutor.test.ts, tests/credentials.test.ts, tests/headers.test.ts — pure import-style conflicts. Kept this branch's \`.js\` extensions on every relative import (mandatory for ESM resolution) and drop-axios's default \`import nock from "nock"\` shape (nock 14's main entry is callable only via the default export; \`import * as nock\` works for namespace methods but breaks \`nock(url)\` calls). credentials.test.ts also kept the FgaApiAuthenticationError import that PR #329's tests need (it was lost on this branch when feat/esm-modules forked from the pre-#329 base). Files auto-merged: credentials/credentials.ts, errors.ts (which now includes PR #329's re-wrap branch in FgaApiAuthenticationError — typed \`HttpErrorContext | FgaApiError\` with optional context arg), tests/client.test.ts, tests/index.test.ts, tests/jest.config.js, tests/helpers/nocks.ts, tsconfig.json. Build (cjs + esm) clean. Tests: 302 passed across 15 suites.
Summary
Replaces error-instance mutation in credential token refresh with construction of a real
FgaApiAuthenticationError.Changes
toThrow(FgaApiAuthenticationError)when token endpoint returns 500FgaApiAuthenticationErrorclient_id,audience,grant_type) in the generated error payloadPotential Breaking Change
FgaApiAuthenticationErrorinstance rather than a mutated API error object.Fixes #328
Summary by CodeRabbit
Bug Fixes
Tests