Skip to content

req_auth_aws_v4() signature mismatch when URL path contains encoded slashes (%2F) #842

@thisisnic

Description

@thisisnic

Note from me: I created the below issue body using Claude, and while I'm not 100% certain about all of the underlying assumptions, it was after a long session of testing out code which appeared to fix the linked ellmer issue, so while I'm not familiar with httr2 code or AWS API stuff, it seems feasible, though may need stress testing!

Surfaced in tidyverse/ellmer#792

Problem

req_auth_aws_v4() produces an incorrect signature when the URL path contains percent-encoded slashes (%2F) within a path segment. This affects AWS APIs that accept ARNs in the URL path, such as Amazon Bedrock's Converse API with application inference profile ARNs.

Reprex

library(httr2)

# Application inference profile ARN (contains a / that must be encoded as %2F)
arn <- "arn:aws:bedrock:<region>:<account-id>:application-inference-profile/<profile-id>"

req <- request("https://bedrock-runtime.<region>.amazonaws.com")
req <- req_url_path_append(req, paste0("model/", curl::curl_escape(arn), "/converse"))
req <- req_auth_aws_v4(
  req,
  aws_access_key_id = "<access-key>",
  aws_secret_access_key = "<secret-key>"
)
req <- req_body_json(req, list())

# The request will fail with HTTP 403: signature mismatch

Cause

In aws_v4_signature(), the canonical URI is built like this:

CanonicalURI <- curl::curl_escape(url$path %||% "/")
CanonicalURI <- gsub("%2F", "/", CanonicalURI, fixed = TRUE)

There are two problems:

  1. url_parse() (called earlier in auth_aws_sign) decodes %2F back to / in the path, so by the time the signer sees it, there's no way to distinguish between a real path separator / and an encoded slash %2F that's part of a path segment.

  2. Even if %2F survived parsing, the gsub("%2F", "/") would restore ALL %2F to /, including ones that are genuinely encoded slashes within a segment.

For comparison, the paws.common R package handles this correctly by preserving the raw (un-decoded) path alongside the decoded path, and using the raw path when building the canonical URI.

Expected behavior

The canonical URI should preserve %2F within path segments. For the ARN above, the canonical URI (before the required double-encoding) should be:

/model/arn%3Aaws%3Abedrock%3A<region>%3A<account-id>%3Aapplication-inference-profile%2F<profile-id>/converse

Not:

/model/arn%3Aaws%3Abedrock%3A<region>%3A<account-id>%3Aapplication-inference-profile/<profile-id>/converse

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions