Gabor
Gabor

Reputation: 41

Azure PUT Blob authentication fails in R

I would like to use R and the Azure Storage's Put Blob API to put files into my blob storage account but it fails to authenticate my request. Unfortunately, I couldn't find any documentation or sample code for R. General documentation of Put Blob API: https://learn.microsoft.com/en-us/rest/api/storageservices/put-blob

Here is the code that I tried to use:

library(httr)  

account <- "myAccount"  
container <- "myContainer"  
filename <- "test.txt"  
key <- "primaryKey"  
object <- "Hello World" 

url <- paste0("https://", account, ".blob.core.windows.net/", container, "/", filename)
requestdate <- format(Sys.time(),"%a, %d %b %Y %H:%M:%S %Z", tz="GMT")  
content_length <- nchar(object, type = "bytes")  

signature_string <- paste0("PUT", "\n", "\n", "\n",
                       content_length, "\n", 
                       "\n",
                       "x-ms-date:",requestdate, "\n", 
                       "x-ms-version:2015-02-21", "\n",
                       "x-ms-blob-type:BlockBlob", "\n",
                       "Content-Type:text/plain",  "\n",
                       "\n",
                       "x-ms-blob-content-dis filename=", filename, "\n",
                       "\n",
                       "/",account, "/",container,"/", filename)

headerstuff <- add_headers(Authorization=paste0("SharedKey ",account,":", 
                       RCurl::base64(digest::hmac(key = 
RCurl::base64Decode(key, mode = "raw"),
                       object = enc2utf8(signature_string),
                       algo = "sha256", raw = TRUE))),
                       `Content-Length` = content_length,
                       `x-ms-date`= requestdate,
                       `x-ms-version`= "2015-02-21",
                       `x-ms-blob-type`="BlockBlob",
                       `Content-Type`="text/plain")

content(PUT(url, config = headerstuff, body = object, verbose()), as = "text")`

Request it sends:

-> PUT /myContainer/test.txt HTTP/1.1
-> Host: myAccount.blob.core.windows.net
-> User-Agent: libcurl/7.49.1 r-curl/2.3 httr/1.2.1
-> Accept-Encoding: gzip, deflate
-> Accept: application/json, text/xml, application/xml, */*
-> Authorization: SharedKey myAccount:hashedSignatureString
-> Content-Length: 11
-> x-ms-date: Tue, 13 Jun 2017 08:50:38 GMT
-> x-ms-version: 2015-02-21
-> x-ms-blob-type: BlockBlob
-> Content-Type: text/plain
-> 
>> Hello World

Response:

<- HTTP/1.1 403 Server failed to authenticate the request. Make sure the 
value of Authorization header is formed correctly including the signature.
<- Content-Length: 693
<- Content-Type: application/xml
<- Server: Microsoft-HTTPAPI/2.0
<- x-ms-request-id: efc2c8de-0001-00a9-3d21-e41b06000000
<- Date: Tue, 13 Jun 2017 08:48:56 GMT

I tried the same with the List Blobs API (with some minor changes in the formatting of the headers) and it works well, but I can't make it work with Put Blob. List Blob solution from here: https://stackoverflow.com/a/29286040/8085694

Could you please provide some sample R code for Authentication header creation at Put Blob or help me resolve this issue?

Also, if I go further, is it possible somehow to upload R objects as blobs to the storage?

Thanks in advance,

Gábor

Upvotes: 1

Views: 1196

Answers (2)

Peter Pan
Peter Pan

Reputation: 24138

There is an Azure offical R package Microsoft/AzureSMR on GitHub, which can help you easier using R & Azure Blob Storage, you can refer to its tutorial to know more details.

If you just want to use some Azure services like Blob Storage, not else, I think some source codes of this project are very valuable for rebuilding your code better, such as createAzureStorageSignature method which can directly help building the signature to resolve your issue.

Upvotes: 0

Gabor
Gabor

Reputation: 41

I managed to resolve this issue by putting the "\n" characters and everything in the right place. Based on Gaurav Mantri's help, I used:
https://learn.microsoft.com/en-us/rest/api/storageservices/authentication-for-the-azure-storage-services

The following changes in the 'signature_string' worked:

signature_string <- paste0("PUT", "\n",            # HTTP Verb
                           "\n",                   # Content-Encoding  
                           "\n",                   # Content-Language
                           content_length, "\n",   # Content-Length
                           "\n",                   # Content-MD5
                           "text/plain", "\n",     # Content-Type
                           "\n",                   # Date
                           "\n",                   # If-Modified-Since
                           "\n",                   # If-Match  
                           "\n",                   # If-None-Match
                           "\n",                   # If-Unmodified-Since
                           "\n",                   # Range
                           # Here comes the Canonicalized Headers
                           "x-ms-blob-type:BlockBlob","\n",
                           "x-ms-date:",requestdate,"\n",
                           "x-ms-version:2015-02-21","\n",
                           # Here comes the Canonicalized Resource
                           "/",account, "/",container,"/", filename)

Upvotes: 3

Related Questions