Skip to content

AccessRepository.ts omits credentials if using Bearer Token Authorization#438

Open
ekraffmiller wants to merge 4 commits intodevelopfrom
437-access-repository-omit-credentials
Open

AccessRepository.ts omits credentials if using Bearer Token Authorization#438
ekraffmiller wants to merge 4 commits intodevelopfrom
437-access-repository-omit-credentials

Conversation

@ekraffmiller
Copy link
Copy Markdown
Contributor

@ekraffmiller ekraffmiller commented Mar 31, 2026

What this PR does / why we need it:

The Access API for download sometimes uses session cookies for authentication, which can be a problem if the user is running JSF and SPA in the same browser. To work around this, this change omits session cookies for all API calls in AccessRepository.

Which issue(s) this PR closes:

Related Dataverse PRs:

(IQSS/dataverse#12267)

Suggestions on how to test this:

Review tests

@ekraffmiller ekraffmiller marked this pull request as ready for review April 3, 2026 17:29
Copilot AI review requested due to automatic review settings April 3, 2026 17:29
@ekraffmiller ekraffmiller moved this to Ready for Review ⏩ in IQSS Dataverse Project Apr 3, 2026
@ekraffmiller ekraffmiller added GREI Re-arch GREI re-architecture-related Project: HDV SPA Rollout Size: 3 A percentage of a sprint. 2.1 hours. labels Apr 3, 2026
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR updates AccessRepository guestbook-download calls to avoid sending session cookies when Bearer Token authentication is configured, preventing unintended authentication via existing Dataverse session cookies.

Changes:

  • Replaced axios-based doPost usage in AccessRepository guestbook download methods with fetch, selecting credentials: 'omit' for bearer-token auth.
  • Added a unit test asserting fetch is called with credentials: 'omit' under bearer-token auth.
  • Strengthened an integration test to validate the returned signed URL is actually downloadable.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 3 comments.

File Description
src/access/infra/repositories/AccessRepository.ts Implements fetch-based POST for guestbook download endpoints and omits credentials for bearer-token auth.
test/unit/access/AccessRepository.test.ts Adds unit coverage to ensure credentials: 'omit' is used with bearer tokens.
test/integration/access/AccessRepository.test.ts Verifies the signed URL can be fetched and returns expected file content.
Comments suppressed due to low confidence (1)

src/access/infra/repositories/AccessRepository.ts:37

  • submitGuestbookForDatafilesDownload takes fileIds: Array<number> but still builds the endpoint using Array.isArray(fileIds) ? fileIds.join(',') : fileIds. The Array.isArray branch is dead code with the current signature and makes the endpoint construction harder to read. Simplify to always fileIds.join(',') (or widen the parameter type if non-array input is actually supported).
    return await this.submitGuestbookDownload(
      this.buildApiEndpoint(
        this.accessResourceName,
        `datafiles/${Array.isArray(fileIds) ? fileIds.join(',') : fileIds}`
      ),

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +96 to +103
const responseData = await this.parseResponseBody(response)

if (!response.ok) {
throw new WriteError(this.buildFetchErrorMessage(response.status, responseData))
}

return responseData.data.signedUrl as string
}
Copy link

Copilot AI Apr 3, 2026

Choose a reason for hiding this comment

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

After parseResponseBody, the code assumes responseData is an object with data.signedUrl. If the response has a missing/incorrect content-type header (so it’s parsed as text) or the JSON shape differs, this will throw a runtime TypeError instead of a controlled WriteError. Add validation for the expected response shape (and/or a fallback JSON parse attempt) and throw a WriteError with a clear message when signedUrl is absent.

Copilot uses AI. Check for mistakes.
Comment on lines +74 to +92
private async submitGuestbookDownload(
apiEndpoint: string,
guestbookResponse: GuestbookResponseDTO,
queryParams: object
): Promise<string> {
const requestConfig = buildRequestConfig(
true,
queryParams,
ApiConstants.CONTENT_TYPE_APPLICATION_JSON
)
const response = await fetch(
this.buildUrlWithQueryParams(buildRequestUrl(apiEndpoint), queryParams),
{
method: 'POST',
headers: this.buildFetchHeaders(requestConfig.headers),
credentials: this.getFetchCredentials(requestConfig.withCredentials),
body: JSON.stringify(guestbookResponse)
}
).catch((error) => {
Copy link

Copilot AI Apr 3, 2026

Choose a reason for hiding this comment

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

AccessRepository reimplements request building (query param serialization, header conversion, body parsing, error message formatting, credentials selection) using fetch, while the rest of the codebase standardizes HTTP behavior via ApiRepository + axios. To reduce drift and keep error/timeout/auth behavior consistent, consider extracting these helpers into ApiRepository (e.g., a shared doPostFetch/doRequestFetch used by this repository) or a core utility module reused across repositories.

Copilot uses AI. Check for mistakes.
Comment on lines +40 to +52
const fetchMock = jest.fn().mockResolvedValue({
ok: true,
status: 200,
headers: new Headers({ 'content-type': 'application/json' }),
json: jest.fn().mockResolvedValue({
data: {
signedUrl: 'https://signed.dataset'
}
})
} as unknown as Response)

global.fetch = fetchMock as typeof fetch

Copy link

Copilot AI Apr 3, 2026

Choose a reason for hiding this comment

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

This test overwrites global.fetch but never restores it. That can leak into other unit tests running in the same worker and make failures order-dependent. Capture the original global.fetch and restore it in afterEach (or use jest.spyOn(global, 'fetch') and mockRestore()).

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Contributor

@ChengShi-1 ChengShi-1 left a comment

Choose a reason for hiding this comment

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

Looks good

@github-project-automation github-project-automation bot moved this from Ready for Review ⏩ to Ready for QA ⏩ in IQSS Dataverse Project Apr 7, 2026
@ChengShi-1 ChengShi-1 added the FY26 Sprint 20 FY26 Sprint 20 (2026-03-26 - 2026-04-08) label Apr 7, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

FY26 Sprint 20 FY26 Sprint 20 (2026-03-26 - 2026-04-08) GREI Re-arch GREI re-architecture-related Project: HDV SPA Rollout Size: 3 A percentage of a sprint. 2.1 hours.

Projects

Status: Ready for QA ⏩

Development

Successfully merging this pull request may close these issues.

Don't send session cookies when using Bearer Token authentication

3 participants