aws.signature icon indicating copy to clipboard operation
aws.signature copied to clipboard

Generate a AWS predesigned URL with signature_v4_auth ?

Open homer3018 opened this issue 2 years ago • 0 comments

Please specify whether your issue is about:

  • [ ] a possible bug
  • [x] a question about package functionality
  • [ ] a suggested code or documentation change, improvement to the code, or feature request

Hello. I'm trying to generate a resigned URL using signature_v4_auth. This was inspired by this. I add to make some changes according to the documentation, and use URLencode a few times to make sure it matches what AWS is providing me with when I manually generate a presigned URL for the same test object.

Regardless of the change I do, I always get a signature mismatch. I've tried signing the body, or not. I'm a little confused when I look at this documentation page. I'm not seeing the UNSIGNED-PAYLOAD anywhere in the code, and so as a result using their parameter I do not end up with the same signature. In my signature$CanonicalRequest I have the body hash instead.

My code so far:

get_aws_signed_url <- function(file,
                               bucket = "my_bucket_name",
                               timeout_seconds = 30,
                               key = "my_key",
                               secret = "my_secret",
                               token = "my_token",
                               region = "us-east-1") {
  # API Implmented according to https://docs.aws.amazon.com/AmazonS3/latest/API/sigv4-query-string-auth.html#query-string-auth-v4-signing-example
  algorithm <- "AWS4-HMAC-SHA256"
  time <- Sys.time()
  date_time <- format(time, "%Y%m%dT%H%M%SZ", tz = "UTC")

  # Build query parameters
  date <- glue::glue("/{format(time,'%Y%m%d', tz = 'UTC')}/") %>%
    URLencode(reserved = TRUE)
  region_encoded <- glue::glue("{region}/") %>% URLencode(reserved = TRUE)
  amzn <- "s3/aws4_request" %>% URLencode(reserved = TRUE)

  # Query parameters, this portion is implemented with the help of https://github.com/cloudyr/aws.s3/blob/master/R/s3HTTP.R
  request_body <- ""
  body_hash <- tolower(digest::digest(request_body,
                                      file = is.character(request_body) &&
                                        file.exists(request_body),
                                      algo = "sha256", serialize = FALSE))

  signature <- aws.signature::signature_v4_auth(datetime = date_time,
                                                region = region,
                                                service = "s3",
                                                verb = "GET",
                                                action = glue::glue("/{file}"),
                                                key = key,
                                                secret = secret,
                                                session_token = URLencode(token, reserved = TRUE),
                                                request_body = "",
                                                signed_body = TRUE,
                                                query_args = list(`X-Amz-Algorithm` = algorithm,
                                                                  `X-Amz-Credential` = glue::glue("{key}{date}{region_encoded}{amzn}"),
                                                                  `X-Amz-Date` = date_time,
                                                                  `X-Amz-Expires` = timeout_seconds,
                                                                  `X-Amz-SignedHeaders` = "host",
                                                                  `x-amz-content-sha256` = body_hash
                                                                  ),
                                                algorithm = algorithm,
                                                canonical_headers = list(host = glue("{bucket}.s3.amazonaws.com")),
                                                force_credentials = TRUE)
  
  return(glue::glue("https://{bucket}.s3.{region}.amazonaws.com/{file}?X-Amz-Algorithm={signature$Query$`X-Amz-Algorithm`}&X-Amz-Credential={signature$Query$`X-Amz-Credential`}&X-Amz-Date={signature$Query$`X-Amz-Date`}&X-Amz-Expires={signature$Query$`X-Amz-Expires`}&X-Amz-SignedHeaders={signature$Query$`X-Amz-SignedHeaders`}&X-Amz-Signature={signature$Signature}&X-Amz-Security-Token={signature$SessionToken}&x-amz-content-sha256={signature$Query$`x-amz-content-sha256`}"))
}


## session info
> sessionInfo()
R version 4.2.1 (2022-06-23)
Platform: aarch64-apple-darwin20 (64-bit)
Running under: macOS Ventura 13.1

Matrix products: default
LAPACK: /Library/Frameworks/R.framework/Versions/4.2-arm64/Resources/lib/libRlapack.dylib

locale:
[1] en_US.UTF-8/en_US.UTF-8/en_US.UTF-8/C/en_US.UTF-8/en_US.UTF-8

attached base packages:
[1] stats     graphics  grDevices utils     datasets  methods   base     

other attached packages:
 [1] tictoc_1.0.1             glue_1.6.2               aws.signature_0.6.0      aws.s3_0.3.21            viridis_0.6.2            viridisLite_0.4.0       
 [7] DataExplorer_0.8.2       gt_0.6.0                 DT_0.23                  reactlog_1.1.0           shinydashboardPlus_2.0.3 shinycssloaders_1.0.0   
[13] vroom_1.6.0              patchwork_1.1.1          rebus_0.1-3              forcats_0.5.1            stringr_1.4.0            dplyr_1.0.9             
[19] purrr_0.3.4              readr_2.1.2              tidyr_1.2.0              tibble_3.1.8             tidyverse_1.3.1          shinyWidgets_0.7.5      
[25] plotly_4.10.0            ggplot2_3.4.0            shinyjs_2.1.0            shinydashboard_0.7.2     shiny_1.7.1             

loaded via a namespace (and not attached):
 [1] colorspace_2.0-3      ellipsis_0.3.2        rprojroot_2.0.3       base64enc_0.1-3       fs_1.5.2              rstudioapi_0.14       farver_2.1.0         
 [8] bit64_4.0.5           fansi_1.0.3           lubridate_1.8.0       xml2_1.3.3            cachem_1.0.6          knitr_1.39            jsonlite_1.8.2       
[15] broom_1.0.1           dbplyr_2.1.1          png_0.1-8             RAthena_2.6.0         compiler_4.2.1        httr_1.4.2            backports_1.4.1      
[22] assertthat_0.2.1      Matrix_1.4-1          fastmap_1.1.0         lazyeval_0.2.2        cli_3.4.1             later_1.3.0           htmltools_0.5.3      
[29] tools_4.2.1           igraph_1.3.2          gtable_0.3.0          rebus.base_0.0-3      Rcpp_1.0.9            cellranger_1.1.0      jquerylib_0.1.3      
[36] vctrs_0.5.1           crosstalk_1.1.1       xfun_0.31             networkD3_0.4         rebus.datetimes_0.0-1 rvest_1.0.1           mime_0.12            
[43] lifecycle_1.0.3       rebus.numbers_0.0-1   scales_1.2.1          hms_1.0.0             promises_1.2.0.1      parallel_4.2.1        yaml_2.3.5           
[50] curl_4.3.2            reticulate_1.26       gridExtra_2.3         sass_0.4.1            stringi_1.7.6         checkmate_2.1.0       rlang_1.0.6          
[57] pkgconfig_2.0.3       evaluate_0.15         lattice_0.20-45       fontawesome_0.4.0     labeling_0.4.2        htmlwidgets_1.5.4     bit_4.0.4            
[64] tidyselect_1.1.2      here_1.0.1            magrittr_2.0.3        R6_2.5.1              generics_0.1.0        DBI_1.1.3             pillar_1.8.1         
[71] haven_2.5.0           withr_2.5.0           rebus.unicode_0.0-2   modelr_0.1.8          crayon_1.4.1          utf8_1.2.2            tzdb_0.3.0           
[78] rmarkdown_2.14        grid_4.2.1            readxl_1.4.0          data.table_1.14.2     reprex_2.0.1          digest_0.6.29         xtable_1.8-4         
[85] httpuv_1.6.5          arrow_10.0.0          munsell_0.5.0         bslib_0.3.1  

I somehow feel that some things in there could/should be simplified - like I have to manually generate the credentials, because signature$Credential is not encoded properly, or I'm getting this completely backward. Apologies if that's the case.

Thanks ahead of time for the clarifications.

homer3018 avatar Dec 16 '22 10:12 homer3018