Azure put blob forbidden, but get blob works rest api - c#

I want to put a text file to a blob container using HttResquestMessage, and I get 403 error(Forbidden).
I know that this is a famous problem, and there are a lot of answers about it, but I've searched a lot and couldn't find the problem in my case.
Here is my Signature
PUT\n
\n
\n
11\n
\n
\n
\n
\n
\n
\n
\n
\n
x-ms-blob-type:BlockBlob\n
x-ms-date:Thu, 10 May 2018 16:00:21 GMT\n
x-ms-version:2017-07-29\n
/MyStorage/dbstore/myFolder/test2.txt"
And this is Authorization generated by SHA256 and Base64 encoding
SharedKey MyStorage:Oy8nB79/KVROzxYCYSF71djhIwYEYS36tgRqyxe6sXY=
This Authorization works for GET request I've tried to get all container names and it succeeded.
My HttpRequestMessage looks like
{Method: PUT, RequestUri:
'http://MyStorage.blob.core.windows.net/dbstore/
myFolder/test2.txt', Version: 1.1, Content:
System.Net.Http.ByteArrayContent, Headers:
{
x-ms-date: Fri, 11 May 2018 06:39:35 GMT
x-ms-version: 2017-07-29
Authorization: SharedKey
storagekaren:Oy8nB79/KVROzxYCYSF71djhIwYEYS36tgRqyxe6sXY=
Content-Length: 11
Content-MD5: CAQ66JnJH0H9GhrLTy+b1w==
}}
Content: {System.Net.Http.ByteArrayContent}
Headers: {x-ms-date: Fri, 11 May 2018 06:39:35 GMT
x-ms-version: 2017-07-29
Authorization: SharedKey
storagekaren:Oy8nB79/KVROzxYCYSF71djhIwYEYS36tgRqyxe6sXY=
}
Method: {PUT}
Properties: Count = 0
RequestUri: {http://MyStorage.blob.core.windows.net/dbstore/myFolder/test2.txt}
Version: {1.1}
What I missed in the request?

Two parameters need adding when putting blob
x-ms-blob-type is required to be included in HttpRequestMessage header.
Content-MD5 is optional, but if you add it in your request header, it should also be included in StringToSign, i.e. the Signature you mentioned.
Any further question, just ask.

Related

Is it possible to change the order of the Headers in a HttpWebResponseMessage using the ApiController?

I have a legacy project which uses .NET Framework 4.5.2 and NancyModule.
When I get the result of a GET-request, then the Headers have the following order:
Key
Value
Content-Length
206
Content-Type
application/json; charset=utf-8
Vary
Accept
Server
Microsoft-HTTPAPI/2.0
Link
</Servicename.xml>; rel="application/xml"
x-powered-by
...
Date
Tue, 31 Jan 2023 13:25:07 GMT
I transfer this project to .net 6 and Microsoft.AspNetCore.Mvc.
When I get the result of a GET-request, then the keys of the Headers are arranged alphabetically.
This leads me to the following question:
Is it possible to change the order of the headers?
I tried to remove and add several values of the dictionary in HttpContext.Response.Headers but it has no entries. When I added a custom header then it was also in alphabetical order.
If you are using Kestrel (the default web server in ASP.NET Core) you might want to remove the Server header in order to try to have the Date header last.
But that would be very fragile, you can't really control the order of the headers, see the source code of how it's done!
For simple HTTP responses that don't set any special headers, this might work and you might end up with something like that. Note the many conditionals used in the previous sentences. 😉
HTTP/1.1 200 OK
Content-Length: 4536
Content-Type: text/plain
Date: Tue, 31 Jan 2023 14:58:31 GMT
And here's how to disable the Server header for Kestrel:
var builder = WebApplication.CreateBuilder(args);
builder.WebHost.ConfigureKestrel(serverOptions => serverOptions.AddServerHeader = false);
If you need to use HTTP.sys instead of Kestrel then you'll be out of luck since the Content-Length header is added after the Date header and there's nothing you can do about it.
HTTP/1.1 200 OK
Content-Type: text/plain
Server: Microsoft-HTTPAPI/2.0
Date: Tue, 31 Jan 2023 15:11:23 GMT
Content-Length: 4536

Occasional response headers in C# HTTP Request

Request:
HttpWebResponse response = (HttpWebResponse)request.GetResponse();
String responseString = new StreamReader(response.GetResponseStream()).ReadToEnd();
Console.WriteLine(responseString);
Response:
{"code":"SUCCESS","details":
{"created_time":"","id":"xxxx"},
"message":"uploaded",
"status":"success"}
HTTP/1.1 200 OK
Date: Wed, 18 Dec 2019 11:42:26 IST
Last-Modified: Wed, 18 Dec 2019 11:42:25 IST
Content-Type: application/json
Connection: Keep-Alive
Server: AWServer
Pragma: no-cache
Cache-Control: no-cache
Expires: 1
Whenever the above-mentioned C# request is executed, the response occasionally contains headers(HTTP/1.1 200 OK...), When I'm only trying to get the body part({"code"....} alone(response.GetResponseStream()). Is this the intended behavior?
Take a look at the basic article on http headers
HTTP headers let the client and the server pass additional information with an HTTP request or response. An HTTP header consists of its case-insensitive name followed by a colon (:), then by its value. Whitespace before the value is ignored.
Headers are additional information. I guess that since you left out the url and the whole creation of the Request and the url, this means that some responses have Headers and some not. That depends on the additional non-body information the api or web server wants to respond with.
It's in the control of the responder and not the receiver.
Don't ignore them: Some times interesting metadata come from Headers. It should not be data but information about it, like encoding, CORS info etc.
last modified header link
date header link

Put Block list Forbidden Azure

I'am quiet new in Azure, and I'm trying to implement the Put Block List operation according to the documentation.
Here are the steps, which I do to upload my file
Read a file from a local folder
Upload it using Put block operation. it gets succes.
Then I use
Get Block List to see if my blocks are uploaded and in the response I can see
<BlockList><CommittedBlocks /><UncommittedBlocks><Block><Name>MDAwMDAwMDAwMA==</Name><Size>17</Size></Block></UncommittedBlocks></BlockList>
that I have one uncommited block with ID MDAwMDAwMDAwMA==.
And finally I use Put Block List to commit the blob.
Here I get an error code 403 "Forbidden".
My Signature is the following
"PUT\n
\n
\n
110\n
\n
\n
\n
\n
\n
\n
\n
\n
x-ms-date:Tue, 15 May 2018 10:54:08 GMT\nx-ms-version:2017-07-29\n
/storagekaren/dbstore/ddd.txt\ncomp:blocklist"
Here is the uri
https://storagekaren.blob.core.windows.net/dbstore/ddd.txt?comp=blocklist
request content
"<?xml version=\"1.0\" encoding=\"utf-8\"?
>\r\n<BlockList>\r\n<Uncommitted>MDAwMDAwMDAwMA==</Uncommitted>
</BlockList>\r\n"
authorizationHeader is computed using this method
public static String CreateAuthorizationHeader(String canonicalizedString)
{
string signature;
using (var hmacSha256 = new
HMACSHA256(Convert.FromBase64String(STORAGE_ACCOUNT_KEY)))
{
var dataToHmac = Encoding.UTF8.GetBytes(canonicalizedString);
signature =
Convert.ToBase64String(hmacSha256.ComputeHash(dataToHmac));
}
var authorizationHeader = String.Format(
CultureInfo.InvariantCulture,
"{0} {1}:{2}",
"SharedKey",
ACCOUNT_NAME,
signature
);
return authorizationHeader;
}
"SharedKey storagekaren:eIHacFz/PWypTWg6SN/4BOuqlCLVLctABhi6Ay7TYiA="
And this is my HttpClient object to make a request
{System.Net.Http.HttpClient}
BaseAddress: null
DefaultRequestHeaders: {x-ms-date: Tue, 15 May 2018 11:07:51 GMT
x-ms-version: 2017-07-29
Authorization: SharedKey storagekaren:eIHacFz/PWypTWg6SN/4BOuqlCLVLctABhi6Ay7TYiA=
}
MaxResponseContentBufferSize: 2147483647
Timeout: {01:00:00}
Here is the response with error
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: 686
Content-Type: application/xml
Server: Microsoft-HTTPAPI/2.0
x-ms-request-id: 7a96deee-201e-00fc-78de-ec0ffc000000
x-ms-error-code: AuthenticationFailed
Date: Wed, 16 May 2018 06:22:35 GMT
<?xml version="1.0" encoding="utf-8"?><Error>
<Code>AuthenticationFailed</Code><Message>Server failed to authenticate the
request. Make sure the value of Authorization header is formed correctly
including the signature.
RequestId:7a96deee-201e-00fc-78de-ec0ffc000000
Time:2018-05-16T06:22:36.0842958Z</Message><AuthenticationErrorDetail>The
MAC signature found in the HTTP request
'E9M4w8nHaBbAsgW3Qhf+u5nHipvmxMvLp09AFdaxYZg=' is not the same as any
computed signature. Server used following string to sign: 'PUT
110
text/plain
x-ms-date:Wed, 16 May 2018 06:22:34 GMT
x-ms-version:2017-07-29
/storagekaren/dbstore/ddd.txt
comp:blocklist'.</AuthenticationErrorDetail></Error>
Please help me understand what am I doing wrong?
Essentially the problem is coming because HttpClient is adding text/plain for Content-Type header whereas you're passing an empty string for that. If you change your signature string to include content-type header, I believe you should not get this error. Essentially, your signature string should look like the following:
"PUT\n
\n
\n
110\n
\n
text/plain\n
\n
\n
\n
\n
\n
\n
x-ms-date:Tue, 15 May 2018 10:54:08 GMT\nx-ms-version:2017-07-29\n
/storagekaren/dbstore/ddd.txt\ncomp:blocklist"

Microsoft Owin Response Body Encoding

In my OwinMiddleware I just write out to the body:
await context.Response.Body.WriteStringAsync("Hey There");
The raw response I get in Fiddler is:
HTTP/1.1 200 OK
Transfer-Encoding: chunked
Server: Microsoft-HTTPAPI/2.0
Date: Fri, 13 May 2016 16:00:07 GMT
A
/hey/there
5
there
9
Hey There
26
{ec86d5c7-4ea4-437f-be2a-4e294b655227}
0
And Fiddler is saying this encoded (with a message saying please click here decode)
The single letters and numbers are not mine. I wrote /hey/there and there and Hey There and the Guid with braces.
How do I just get the plain text response?

(Instagram API) Trouble Reaching Tag Endpoints

I'm trying to gather a list of recent posts that contain a certain hashtag. The API Documentation states that I should be using the following GET call:
https://api.instagram.com/v1/tags/{tag-name}/media/recent?access_token=ACCESS-TOKEN
When I load the page where I want this information displayed, I perform the following:
using(HttpClient Client = new HttpClient())
{
var uri = "https://api.instagram.com/v1/tags/" + tagToLookFor + "/media/recent?access_token=" + Session["instagramaccesstoken"].ToString();
var results = Client.GetAsync(uri).Result;
// Result handling below here.
}
For reference, tagToLookFor is a constant string defined at the top of the class (eg. foo), and I store the Access Token returned from the OAuth process in the Session object with a key of 'instagramaccesstoken'.
While debugging this, I checked to make sure the URI was being formed correctly, and it does contain both the tag name and the just-created access_token. Using Apigee with the same URI (Save for a different access_token) returns the valid results I would expect. However, attempting to GET using the URI on my webstie returns:
{
StatusCode: 400,
ReasonPhrase: 'BAD REQUEST',
Version: 1.1,
Content: System.Net.Http.StreamContent,
Headers:{
X-Ratelimit-Remaining: 499
Vary: Cookie
Vary: Accept-Language
X-Ratelimit-Limit: 500
Pragma: no-cache
Connection: keep-alive
Cache-Control: no-store, must-revalidate, no-cache, private
Date: Fri, 27 Nov 2015 21:39:56 GMT
Set-Cookie: csrftoken=97cc443e4aaf11dbc44b6c1fb9113378; expires=Fri, 25-Nov-2016 21:39:56 GMT; Max-Age=31449600; Path=/
Content-Length: 283
Content-Language: en
Content-Type: application/json; charset=utf-8
Expires: Sat, 01 Jan 2000 00:00:00 GMT
}
}
I'm trying to determine what the difference between the two could be; the only thing that I can think of is that access_token is somehow being invalidated when I switch between pages. The last thing I do on the Login/Auth page is store the access_token using Session.Add, then call Server.Transfer to move to the page that I'm calling this on.
Any Ideas on what the issue could be? Thanks.
Attach the token to the header when making the request.
Client.DefaultRequestHeaders.Add("access_token", "Bearer " + token);
The problem ended up being one regarding Sandbox Mode. I had registered an app after the switch, and I was the only user in my sandbox. As a result, it had no problem finding my posts/info, but Sandbox Mode acts as if the Sandbox users are the only users on Instagram, so naturally it would not find anything else.
It turns out there was an existing registered application in my organization (made before the switch date) that does not have any such limitations, so I have been testing using that AppID/secret.
tl;dr: If you're the only user in your app's sandbox, work on getting users into your sandbox. See their article about it for more info.

Categories

Resources