“HTTP 403 Server failed to authenticate the request” When Using Shared Access Signatures
One of the more common Azure Storage shared access signature issues I see is “403 Server failed to authenticate the request. Make sure the value of Authorization header is formed correctly including the signature.” The challenge with this error is that it can often feel very random. When you run your code on some computers it works fine, but on other computers you get a 403. Or if you return a collection of SAS URLs to a client some of those URLs work fine and others get a 403.
The code to generate the SAS is typically very simple:
1: string sas = azureContainer.GetSharedAccessSignature (new SharedAccessPolicy ()
2: {
3: SharedAccessStartTime = DateTime.UtcNow,
4: SharedAccessExpiryTime = DateTime.UtcNow.AddHours(1),
5: Permissions = SharedAccessPermissions.Write | SharedAccessPermissions.Read
6: });
So how can it behave so randomly?
Troubleshooting
As with most Azure Storage problems, we want to start with Fiddler. Download and install Fiddler and let it run and capture traffic while you reproduce the problem. The Raw results will look something like the following:
Request
GET https://teststorage.blob.core.windows.net/Images/TestImage.png?st=2013-08-27T10%3A36%3A43Z\&se=2013-08-27T11%3A31%3A43Z\&sr=b\&sp=r\&sig=l95QElg18CEa55%2BXuhJIQz56gFFs%1FppYz%2E024uj3aYc%3D HTTP/1.1
Accept: image/png, image/svg+xml, image/*;q=0.8, */*;q=0.5
Accept-Language: en-US
User-Agent: Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0)
Accept-Encoding: gzip, deflate, peerdist
Proxy-Connection: Keep-Alive
Host: teststorage.blob.core.windows.net
X-P2P-PeerDist: Version=1.0
Response
HTTP/1.1 403 Server failed to authenticate the request. Make sure the value of Authorization header is formed correctly including the signature.
Proxy-Connection: Keep-Alive
Connection: Keep-Alive
Content-Length: 422
Via: 1.1 APS-PRXY-10
Date: Tue, 27 Aug 2013 10:36:41 GMT
Content-Type: application/xml
Server: Microsoft-HTTPAPI/2.0
x-ms-request-id: bf0d4d25-0110-4719-945c-afae8cbcdf0b
AuthenticationFailedServer failed to authenticate the request. Make sure the value of Authorization header is formed correctly including the signature.
RequestId:bf0d4d25-0110-4719-945c-afae8cbcdf0b
Time:2013-08-27T10:36:42.3077388Z Signature not valid in the specified time frame
I have highlighted the key portions of the Fiddler trace. Notice the error message indicates that the shared access signature’s time frame is not valid.
Looking at the Request URL and comparing it to the SAS documentation we can see that the start time is set for 10:36:43. Given the code above we now that this is the value being returned on the client machine when calling DateTime.UtcNow.
Looking at the Response’s Date header we can see that the server side time is 10:36:41. 2 seconds earlier than the start time in the Request URL that the client created. This means that the client machine’s system time is at least 2 seconds faster than the system time for the front end authentication server handling that particular Azure Storage request. And the Azure Storage authentication server is going to reject this request because the client is attempting to access the storage resource 2 seconds earlier than the shared access signature allows. Clock drift like this is not an uncommon scenario.
So depending on which machine is generating the SAS URL, how recently the system time was synchronized, the time delta between the SAS-generating machine and the storage servers, and the speed of the client issuing the SAS request, you will randomly get HTTP 403 responses.
Resolution
As we know from the SAS documentation the Start Time is optional (from MSDN: “Optional. The time at which the shared access signature becomes valid, in an ISO 8061 format. If omitted, start time for this call is assumed to be the time when the storage service receives the request.”). If you are going to specify DateTime.Now as your start time, then why specify a start time at all? The only reason you would want to specify a start time is if you were intentionally trying to time-delay a client’s access to a storage resource.
To resolve this issue simply remove the StartTime from the code such that it looks like this:
1: string sas = azureContainer.GetSharedAccessSignature (new SharedAccessPolicy ()
2: {
3: SharedAccessExpiryTime = DateTime.UtcNow.AddHours(1),
4: Permissions = SharedAccessPermissions.Write | SharedAccessPermissions.Read
5: });
Comments
Anonymous
April 17, 2014
funny; just had my motherboard replaced and the tech failed to properly set the TimeDate. this was the issue.Anonymous
August 12, 2014
Removed Starttime, But still i'm getting the same error.Anonymous
August 13, 2014
Ravindra, the blog post has instructions for troubleshooting this type of issue using Fiddler. Have you done this yet and do you have more information?Anonymous
September 21, 2014
This 403 happens to me frequently even when NOT using a SAS. Eventually it goes away. Appalling.Anonymous
September 21, 2014
Phil, do you have any more details about your problem? How are you authenticating your requests? What does a Fiddler trace look like?Anonymous
January 31, 2015
The comment has been removedAnonymous
January 31, 2015
senkwe, do you have any additional details about the problem you are seeing? 403 authentication failures can be tricky to troubleshoot, but I have yet to see a single one caused by a problem with the service itself. If you can share some code and the Request/Response details it would be helpful.Anonymous
September 01, 2015
Kevin, I have been struggling with this random 403 error for hours. I have rewritten the SaS generation code a million times and it still randomly generates a 403 on file uploads. Any help would greatly appreciated. Thanks, Patrick This is my SaS code located on Azure:
public string GetSaS(string containerName, string blobName) { var myAccount = CloudStorageAccount.Parse(ConfigurationManager.AppSettings["StorageConnectionString"].ToString()); var myContainer = myAccount.CreateCloudBlobClient().GetContainerReference(containerName); myContainer.CreateIfNotExists(); var myBlob = myContainer.GetBlockBlobReference(blobName); var sharedAccesSignature = myBlob.GetSharedAccessSignature( new Microsoft.WindowsAzure.Storage.Blob.SharedAccessBlobPolicy() { Permissions = Microsoft.WindowsAzure.Storage.Blob.SharedAccessBlobPermissions.Write | Microsoft.WindowsAzure.Storage.Blob.SharedAccessBlobPermissions.Read, SharedAccessStartTime = new DateTimeOffset(DateTime.UtcNow.AddMinutes(-5)), SharedAccessExpiryTime = DateTime.UtcNow.AddMinutes(5), }); return myBlob.Uri.AbsoluteUri + sharedAccesSignature; } This is my calling code:
.controller('AccountDetailCtrl', function ($scope) { $scope.uploadfiles = function () { var files = fileControl.files; for (var i = 0, file; file = files[i]; i++) { var reader = new FileReader(); reader.onloadend = (function (theFile) { return function (e) { $.ajax({ type: 'GET', url: 'mytestingzone.azurewebsites.net/.../GetSaS + $("#ContainerName").val() + '&blobName=' + theFile.name, success: function (res, status, xhr) { upload(e.target.result, theFile.type, res); }, error: function (res, status, xhr) { alert("Can't get the Shared Access Signature"); } }); }; })(file); reader.readAsArrayBuffer(file); } } }); This is the function called from the controller above:
function upload(file, type, url) { var ajaxRequest = new XMLHttpRequest(); ajaxRequest.open('PUT', url, true); ajaxRequest.setRequestHeader('Content-Type', type); ajaxRequest.setRequestHeader('x-ms-blob-type', 'BlockBlob'); ajaxRequest.send(file); }
Anonymous
September 01, 2015
Patrick, what does the response body show? It will probably have a more verbose error that will give you a clue as to the source of the failure. You can use Fiddler to easily get this information.Anonymous
September 02, 2015
The response shows the below message. As an FYI, when testing the upload, I have been selecting the same 7 audio files on each attempt. The error does not occur on the same files each time, but it does occur on a random 3-4 out of the 7. As an FYI, this is an Ionic/Cordova app running on the Ripple emulator for an iPad3. The files which do not generate the 403 upload successfully. <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:b153d38a-0001-001c-78c1-e5c92d000000 Time:2015-09-02T20:52:04.1706687Z </Message> <AuthenticationErrorDetail>Signature fields not well formed.</AuthenticationErrorDetail> </Error>Anonymous
September 02, 2015
Patrick, there isn't enough information here to determine root cause. "Signature fields not well formed" is typically a problem with the URL that you are using, perhaps something like a missing &, a URL encoding problem, etc. Try to compare the URLs of a successful upload to one that is failing. If you can provide the full HTTP Request/Response including URL and Headers then I can take a look.Anonymous
September 02, 2015
The comment has been removedAnonymous
September 02, 2015
The comment has been removedAnonymous
September 17, 2017
Onde irá descobrir, analisar e estruturar os nichos. http://yo.io/curso_formula_negocio_online_245733Anonymous
November 06, 2017
This just saved me from hours of debugging, thank you!