Skip to content

feat: Add pre-signed URL support for GET/HEAD requests#424

Closed
lcian wants to merge 4 commits intolcian/feat/presigned-object-authfrom
lcian/feat/presigned-urls
Closed

feat: Add pre-signed URL support for GET/HEAD requests#424
lcian wants to merge 4 commits intolcian/feat/presigned-object-authfrom
lcian/feat/presigned-urls

Conversation

@lcian
Copy link
Copy Markdown
Member

@lcian lcian commented Apr 9, 2026

Implements pre-signed URLs that grant time-limited access to specific objects, as an alternative to providing a JWT header.
We will need this for frontend components that rely on Objectstore to provide files (currently only snapshot images and diffs) and we could also use it for e.g. attachments downloads.

Pre-signed URLs are of the form:

/v1/objects/{usecase}/{scopes}/{key}?X-Os-Expires={unix_ts}&X-Os-KeyId={kid}&X-Os-Signature={base64url_sig}
  • The signature is a Ed25519 signature of the path portion of the URL and query parameters. The signature is computed on a canonicalized combination of path and query params, to ensure that signature computation and verification are consistent.
  • Pre-signed URLs are only supported for GET and HEAD requests for now. The rationale for that is to solve the immediate problem of GET requests coming from browsers, and avoid expanding the scope to PUT/POST which would have a more complex implementation (implementing those correctly would require signing (part of) the request headers).

Clients expose a presign_url utility which takes a private key and creates pre-signed URLs with a customizable TTL, 5 minutes by default.

Introduces a new objectstore-shared crate to house the shared signing logic and constants between the client and server crate. We already have objectstore-types that's similarly shared, but these are not really types, so I think a separate crate is warranted.

@github-actions
Copy link
Copy Markdown

github-actions bot commented Apr 9, 2026

Semver Impact of This PR

🟡 Minor (new features)

📋 Changelog Preview

This is how your changes will appear in the changelog.
Entries from this PR are highlighted with a left border (blockquote style).


New Features ✨

  • Add pre-signed URL support for GET/HEAD requests by lcian in #424

🤖 This preview updates automatically when you update the PR.

(None, Some(jwt)) => AuthContext::from_encoded_jwt(jwt, &state.key_directory),
(None, None) => Err(AuthError::BadRequest("No authorization provided")),
};

Copy link
Copy Markdown
Member Author

@lcian lcian Apr 10, 2026

Choose a reason for hiding this comment

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

We need the pre-signed auth to take precedence, because browsers will make requests to the proxy endpoint (or to Objectstore directly, if it were exposed through an NGINX rule) with both an Authorization header and the pre-signature.

Another approach would be to try all 3 methods (2 headers + pre-signature) every time. Or even build a composite AuthContext that takes into account all authentication methods, if multiple of them are present together.
That's more resilient but also more expensive, I would prefer to avoid it.

@lcian

This comment was marked as outdated.

@lcian

This comment was marked as outdated.

@lcian

This comment was marked as outdated.

Copy link
Copy Markdown

@cursor cursor bot left a comment

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 449747d. Configure here.

@lcian lcian force-pushed the lcian/feat/presigned-urls branch from 944425c to 570c2ee Compare April 13, 2026 10:25
@lcian lcian changed the base branch from main to lcian/feat/presigned-object-auth April 13, 2026 10:26
@lcian lcian force-pushed the lcian/feat/presigned-urls branch from c6ce514 to 0c429ad Compare April 13, 2026 11:30
@lcian lcian force-pushed the lcian/feat/presigned-urls branch from 0c429ad to 45d6baf Compare April 13, 2026 13:30
lcian added 3 commits April 13, 2026 15:40
… tests

PR 428 made AuthContext fields private. Add #[cfg(test)] accessor
methods and update presigned URL tests to use them.
@lcian lcian closed this Apr 13, 2026
@lcian
Copy link
Copy Markdown
Member Author

lcian commented Apr 13, 2026

This is complex:

  • Needs a separate mechanism (other than the JWT) to sign and verify
  • The mechanism is sensitive to encoding, ordering of query params etc.
    The only benefit that this provides is fixed-size URLs.
    The more sensible approach I will now implement is to extend JWTs to support an optional ObjectKey to scope down authorization further.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant