Skip to content

Anonymous guestbook download via :persistentId returns a signed URL that immediately fails with 401 Bad signed URL, numeric dataset id works #12245

@ekraffmiller

Description

@ekraffmiller

Summary:
For a public dataset with a guestbook assigned, anonymous guestbook download through the access API behaves differently depending on the dataset identifier used. The :persistentId guestbook-signing path returns a signed URL that immediately fails with 401 Bad signed URL, while the numeric dataset id path returns a signed URL that works. The logged-in :persistentId path also works.

What steps does it take to reproduce the issue?
Create or use a published public dataset with a guestbook assigned.
As an anonymous user, submit a guestbook response to the access API using the :persistentId endpoint with signed=true.
Take the exact signedUrl returned by the API response.
Fetch that exact returned URL.
Repeat the same test using the numeric dataset id endpoint instead of :persistentId.
Repeat the :persistentId test as a logged-in user with a bearer token.

  • When does this issue occur?
    When an anonymous user downloads a public dataset that requires a guestbook response and the signed URL is generated through the :persistentId guestbook access endpoint.

  • Which page(s) does it occurs on?
    It is visible from the SPA dataset page download flow for public datasets with guestbooks assigned. It is also reproducible directly at the API level via the access endpoints.

  • What happens?
    The anonymous POST /api/v1/access/dataset/:persistentId?...&signed=true request returns status: OK and returns a signedUrl, but fetching that exact returned signed URL immediately fails with:

{"status":"ERROR","message":"Bad signed URL"}
and HTTP status 401 Unauthorized.

By contrast:

anonymous POST /api/v1/access/dataset/2?signed=true succeeds and the returned signed URL works
logged-in POST /api/v1/access/dataset/:persistentId?...&signed=true succeeds and the returned signed URL works

  • To whom does it occur (all users, curators, superusers)?

It occurs for anonymous users (guest) downloading a public dataset with a guestbook through the :persistentId guestbook-signing path.

It does not reproduce for:

logged-in users using the :persistentId guestbook-signing path
anonymous users using the numeric dataset id guestbook-signing path

  • What did you expect to happen?
    The exact signed URL returned by the anonymous :persistentId guestbook access POST should be valid and should download the dataset successfully, just like:

the numeric dataset id version for anonymous users
the :persistentId version for logged-in users

Which version of Dataverse are you using?
Latest develop branch version

Any related open or closed issues to this bug report?

Screenshots:

API-level reproduction commands and results:

Anonymous :persistentId path, fails:

PID='doi:10.5072/FK2/I0NJBF'

RESP=$(curl -sS -X POST "http://localhost:8000/api/v1/access/dataset/:persistentId?persistentId=${PID}&signed=true"
-H 'Content-Type: application/json'
-d '{
"guestbookResponse": {
"name": "Guest User",
"email": "guest@example.com",
"institution": "Test Institution",
"position": "Researcher",
"answers": []
}
}')

echo "$RESP" | jq .

SIGNED_URL=$(echo "$RESP" | jq -r '.data.signedUrl // empty')

if [ -n "$SIGNED_URL" ]; then
curl -i "$SIGNED_URL"
fi
Observed:

POST returns status: OK
GET on exact returned signed URL returns 401 Unauthorized
body: {"status":"ERROR","message":"Bad signed URL"}
Anonymous numeric dataset id path, succeeds:

RESP=$(curl -sS -X POST "http://localhost:8000/api/v1/access/dataset/2?signed=true"
-H 'Content-Type: application/json'
-d '{
"guestbookResponse": {
"name": "Guest User",
"email": "guest@example.com",
"institution": "Test Institution",
"position": "Researcher",
"answers": []
}
}')

echo "$RESP" | jq .

SIGNED_URL=$(echo "$RESP" | jq -r '.data.signedUrl // empty')

if [ -n "$SIGNED_URL" ]; then
curl -i "$SIGNED_URL"
fi
Observed:

POST returns status: OK
GET on exact returned signed URL returns 200 OK
Logged-in :persistentId path, succeeds:

TOKEN=''
PID='doi:10.5072/FK2/I0NJBF'

RESP=$(curl -sS -X POST "http://localhost:8000/api/v1/access/dataset/:persistentId?persistentId=${PID}&signed=true"
-H "Authorization: Bearer ${TOKEN}"
-H 'Content-Type: application/json'
-d '{
"guestbookResponse": {
"name": "Dataverse Admin",
"email": "dataverse@mailinator.com",
"institution": "IQSS",
"position": "Admin",
"answers": []
}
}')

echo "$RESP" | jq .

SIGNED_URL=$(echo "$RESP" | jq -r '.data.signedUrl // empty')

if [ -n "$SIGNED_URL" ]; then
curl -i "$SIGNED_URL"
fi
Observed:

POST returns status: OK
GET on exact returned signed URL returns 200 OK

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions