I'm trying to make a GET request to Azure Table REST API with Postman.
I can make a working request with a C# program I found, but when I try to copy the same information into the Postman request it return the followign error:
Server failed to authenticate the request. Make sure the value of Authorization header is formed correctly including the signature.
With the C# program I generate my UTC time and my Authorization code.
The program will give me the following output:
x-ms-date: Fri, 01 Nov 2019 10:13:26 GMT
Authorization: SharedKeyLite username:e4IREMOVEDSOMELETTERST4Ag=
Request URI: https://username.table.core.windows.net/MainTable(PartitionKey='akey',RowKey='130')
The generated output works in the C# program, because when I use:
result = await Client.GetAsync(requestUri);
The result will give me the information of (akey, 130).
When I pass them into postman it will still give me an error.
I do update the date in postman whenever I make a new authorized string.
My postman setup is as follows:
I eventually want to make this request with the ESP32, so it might be a bit unrelated, but the ESP is giving me the same error. Any tips on setting the headers correct either for Postman or the ESP are appreciated.
to make this work first create two variables in your environment :
{{utcDate}}
{{authToken}}
Then create a new Get request and setup your headers like this :
x-ms-version 2015-12-11
x-ms-date {{utcDate}}
Authorization SharedKey resourceName:{{authToken}}
DataServiceVersion 3.0;NetFx
MaxDataServiceVersion 3.0;NetFx
Accept application/json;odata=nometadata
Finally, define a Pre-request Script :
var now = new Date().toUTCString();
pm.environment.set("utcDate", now);
var hcar = "/resourceName/TableName";
var verb = request.method;
var cntMd5 = "";
var cntType = "";
var mKey="<Your service key goes here>";
var text = verb + "\n" + (cntMd5 || "") + "\n" + (cntType || "") + "\n" + now + "\n" + hcar;
var key = CryptoJS.enc.Base64.parse(mKey);
var signature = CryptoJS.HmacSHA256(text, key);
var base64Bits = CryptoJS.enc.Base64.stringify(signature);
pm.environment.set("authToken", base64Bits);
The reason for the variables is, authToken because you need a place holder to store the calculated token, utcDate because the same date in your header must be used to calculate your token.
I found that the problem was within Postman itself.
There has been an ongoing issue with the automatic URL encoding.
When I went directly to the MainTable the code of Mauricio worked.
Related
This is rather a silly question but I can't seem to figure it out. I'm building a flutter app that communicates to an API and in order for the user to be authenticated it sends an token.
Whenever the user logs in I save the token in the Shared Preferences and then when I make an request add the token to the headers or in this case I need it in the url.
However, whenever I concatenate the strings: the server url and the token it adds extra "" to the token. something like:
http://10.0.2.2:64342/chatHub?access_token="token-value"
instead of
http://10.0.2.2:64342/chatHub?access_token=token-value
here's the code:
var preferences = await SharedPreferences.getInstance();
token = preferences.getString(token_key);
var url = '$serverURl?access_token=$token';
As far as I understand your question, I would like to answer it.
That's not possible!
var serverURl = 'http://10.0.2.2:64342/chatHub';
var token = 'token-value';
var url = '$serverURl?access_token=$token';
print(url);
It just prints the correct one!
You can check the string that is stored in the SharedPreferences! That maybe with quotes.
Okay, I figured it out. Since I was sending only the token from the API. I was receiving it with the "" in it.
Instead I now send a json with the token, like: { "token": "token_value"} and then decode it to get the actual value. So when I store it the shared preferences it doesn't keep the "".
So in the backend:
return Ok(new {token = generatedToken});
and in dart
var tokenJson = json.decode(response.body);
var token = tokenJson['token'];
preferences.setString(token_key, token);
Thanks to everyone that helped :)
I have an azure stored procedure, and I need to hit it with a python script that I'm going to upload as a webjob to schedule it to run once per day.
I've been reading the docs on executing a stored procedure, the common request headers for Azure Cosmos DB rest calls, and the page on access control, but the access control page mentions that these keys are for read queries only (so I assume not for hitting stored procedures, which have rights to do any sort of query or else that seems like a huge vulnerability hole).
I need to know specifically how do I get a key from Azure in python to hit my stored procedure endpoint?
Update 1
I was able, finally, to construct the Authorization string and send it, along with some other headers, to the server. But I am still getting an unauthorized response.
The response:
{
"code": "Unauthorized",
"message": "The input authorization token can't serve the request. Please check that the expected payload is built as per the protocol, and check the key being used. Server used the following payload to sign: 'post\nsprocs\ndbs/metrics/colls/LoungeVisits/sprocs/calculateAverage\nfri, 05 oct 2018 19:06:17 gmt\n\n'\r\nActivityId: 41cd36af-ad0e-40c3-84c8-761ebd14bf6d, Microsoft.Azure.Documents.Common/2.1.0.0"
}
The request headers:
{
Authorization: [my-auth-string],
x-ms-version: "2017-02-22", //My DB was created after this, the latest version, so I assume it uses this version; can I verify this somehow?
x-ms-date: "Fri, 05 Oct 2018 19:06:17 GMT", // My js for returning the auth string also returns the date, so I copy both in
Content-Type: application/json
}
Code to generate auth string which is then copy/pasted into Postman:
var crypto = require("crypto");
var inputKey = "my-key-from-azure";
var today = new Date().toUTCString();
console.log(today);
console.log(getAuthorizationTokenUsingMasterKey("POST", "dbs", "dbs/ToDoList", today, inputKey))
function getAuthorizationTokenUsingMasterKey(verb, resourceType, resourceId, date, masterKey)
{
var key = new Buffer(masterKey, "base64");
var text = (verb || "").toLowerCase() + "\n" +
(resourceType || "").toLowerCase() + "\n" +
(resourceId || "") + "\n" +
date.toLowerCase() + "\n" +
"" + "\n";
var body = new Buffer(text, "utf8");
var signature = crypto.createHmac("sha256", key).update(body).digest("base64");
var MasterToken = "master";
var TokenVersion = "1.0";
return encodeURIComponent("type=" + MasterToken + "&ver=" + TokenVersion + "&sig=" + signature);
}
The page about authorization headers is for any Cosmos DB REST request: query, stored procedures, etc.
Azure cosmos DB has python SDK which is the recommended and supported way for such scenarios.
Also python SDK code is open-sourced. Here is the reference to auth header creation code enter link description here
Im tring to user the DocuSign api/sdk to send a document for someone to sign. The examples say something like:
//.NET
APIServiceSoapClient apiService = new APIServiceSoapClient();
apiService.ClientCredentials.UserName.UserName = "Your DocuSign UserName here";
apiService.ClientCredentials.UserName.Password = "Your DocuSign Password here";
Which I of course have tried but its not working.
I get the following error:
Security requirements are not satisfied because the security header is not present in the incoming message.
Ive tried
var username = "myemail";
var pass = "mypass";
var iteratorKey = "iteratorkey";
APIServiceSoapClient apiService = new APIServiceSoapClient();
apiService.ClientCredentials.UserName.UserName = username;
//also tried ...UserName = "[" + iteratorKey + "]" + username;
apiService.ClientCredentials.UserName.Password = pass;
Is this not where all security requirements are met? maybe? Using APIService not DSAPIService if that makes a difference.
I ended up having to use a different way to pass in the credentials. Which I found somewhere else. Im still not sure how to correctly use the other method I tried though so if anyone knows how to use the other method it would be great just because the code is neater and easier to follow.
string auth = #"<DocuSignCredentials>
<Username>email</Username>
<Password>pass</Password>
<IntegratorKey>key</IntegratorKey>
</DocuSignCredentials>";
DSAPIServiceSoapClient apiService = new DSAPIServiceSoapClient();
using (var scope = new System.ServiceModel.OperationContextScope(apiService.InnerChannel))
{
var httpRequestProperty = new System.ServiceModel.Channels.HttpRequestMessageProperty();
httpRequestProperty.Headers.Add("X-DocuSign-Authentication", auth);
System.ServiceModel.OperationContext.Current.OutgoingMessageProperties[System.ServiceModel.Channels.HttpRequestMessageProperty.Name] = httpRequestProperty;
EnvelopeStatus envStatus = apiService.CreateAndSendEnvelope(envelope);
return envStatus.EnvelopeID;
}
There are two ways to pass member credentials through DocuSign's SOAP API (as opposed to the newer REST API):
SOAP Header via WS-Security UsernameToken
HTTP Header via a custom field “X-DocuSign-Authentication”
The Account Management API only supports the HTTP Header authentication method, while all others can support either method.
Additionally, the DocuSign SOAP API has two API end points: API.asmx and DSAPI.asmx. The API.asmx end point requires the WS-Security UsernameToken in the SOAP header authentication. The DSAPI.asmx and AccountManagement.asmx end points require the HTTP Header authentication method.
I have gone past 2 stages in the Embedded Signing API which I use in my WCF web service using C#.
The login credentials & the request envelope API calls work & the envelopeID is generated.
The 3rd step is the "get URL for the Embedded Console Sign View"
string reqBody = "<recipientViewRequest xmlns=\"http://www.docusign.com/restapi\">" +
"<authenticationMethod>" + "email" + "</authenticationMethod>" +
"<email>" + "jay.krishnamoorthy#gmail.com" + "</email>" + // NOTE: Use different email address if username provided in non-email format!
"<returnUrl>" + "http://www.docusign.com" + "</returnUrl>" + // username can be in email format or an actual ID string
"<clientUserId>" + "1001" + "</clientUserId>" +
"<userName>" + "Jay Krishnamoorthy" + "</userName>" +
"</recipientViewRequest>";
// append uri + "/views/recipient" to baseUrl and use in the request
request = (HttpWebRequest)WebRequest.Create(baseURL + uri + "/views/recipient");
request.Headers.Add("X-DocuSign-Authentication", authenticateStr);
request.ContentType = "application/xml";
request.Accept = "application/xml";
request.ContentLength = reqBody.Length;
request.Method = "POST";
// write the body of the request
byte[] body2 = System.Text.Encoding.UTF8.GetBytes(reqBody);
Stream dataStream2 = request.GetRequestStream();
dataStream2.Write(body2, 0, reqBody.Length);
dataStream2.Close();
// read the response
webResponse = (HttpWebResponse)request.GetResponse();-----> comes back with Bad request
Can some one help with the missing info in my request body which causes the BAD request response.
I'm having trouble making sense of the reqBody value in your question, so instead of commenting on that, I'll just provide a simple example of what a proper POST Recipient View request looks like (in XML format):
POST https://{{env}}.docusign.net/restapi/{{version}}/accounts/{{acctId}}/envelopes/{{envelopeId}}/views/recipient
<recipientViewRequest xmlns="http://www.docusign.com/restapi">
<authenticationMethod>Email</authenticationMethod>
<email>RECIPIENT_EMAIL_ADDRESS</email>
<returnUrl>http://www.google.com</returnUrl>
<clientUserId>CLIENT_USER_ID_VALUE_SPECIFIED_IN_THE_REQUEST</clientUserId>
<userName>RECIPIENT_NAME</userName>
</recipientViewRequest>
I'd suggest that you compare the request URI and request body I've included here with what you're sending, and adjust yours as needed to match.
Additionally, I'd recommend that you use a tool like "Fiddler" or something similar to examine the XML Request and Response as they're being sent over the wire -- i.e., identify problems by examining the raw XML using Fiddler, then update your code to fix the problems (i.e., to generate/send a properly formatted request). Being able to produce a trace of the raw XML Request / Response is a requirement of the DocuSign API Certification process, so you might as well figure that out sooner rather than later, as it's a valuable troubleshooting asset during development as well (when you get a "Bad Request" response like you're getting).
Your request body has all the proper elements and looks to be correct on first glance, however I see what you are doing wrong now. In your request body when you set the authentication method I see that you are setting it to:
"<authenticationMethod>" + "email" + "</authenticationMethod>"
This is incorrect. The value here actually needs to be the string email or Email, you don't enter the recipient's actual email address. The point of the authenticationMethod property is to tell the system what level of authentication you are expecting. If set to email then you are telling the system simply that the email address is the only form of authentication you want for that recipient. So what you want instead is:
"<authenticationMethod>email</authenticationMethod>"
azure table query rest api is failing with AuthenticationFailed error.
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<error xmlns="http://schemas.microsoft.com/ado/2007/08/dataservices/metadata">
<code>AuthenticationFailed</code>
<message xml:lang="en-US">Server failed to authenticate the request. Make sure the value of Authorization header is formed correctly including the signature.</message>
</error>
The winjs app code snippet to form and make the rest call is:
var date = new Date().toGMTString().replace('UTC', 'GMT');
var xhrOption = {
type: 'GET',
url: url,
headers: {
'content-type': 'application/atom+xml;charset="utf-8"',
'content-length': 0,
dataserviceversion: '1.0;NetFx',
maxdataserviceversion: '2.0;NetFx',
'x-ms-version': '2011-08-18',
'x-ms-date': date,
accept: 'application/atom+xml,application/xml',
'Accept-Charset': 'UTF-8',
},
};
xhrOption.headers.Authorization = AuthorizationHeader().computeForTableService(options, xhrOption);
The code to compute the authorization header is little long. It is listed below:
_getSignatureStringForTableService: function getSignatureStringForTableService()
{
var headers = this.xhrOptions.headers;
var httpVerb = this.xhrOptions.type.toUpperCase();
var sigItems = [];
sigItems.push(httpVerb);
var contentMD5 = this._getHeaderOrDefault(headers, 'Content-MD5');
sigItems.push(contentMD5);
var contentType = this._getHeaderOrDefault(headers, 'content-type');
sigItems.push(contentType);
var date = this._getHeaderOrDefault(headers, 'x-ms-date');
if (!date)
date = this._getHeaderOrDefault(headers, 'Date');
sigItems.push(date);
var canonicalizedResource = this._getCanonicalizedResource();
sigItems.push(canonicalizedResource);
var result = sigItems.join('\n');
return result;
},
_getCanonicalizedResource: function getCanonicalizedResource()
{
var items = [];
var path;
if (config.storageAccount.isDevStorage)
path = "/" + config.storageAccount.name + '/' + config.storageAccount.name;
else
path = "/" + config.storageAccount.name;
path += "/" + this.options.resourcePath;
items.push(path);
var result = items.join('\n');
return result;
},
computeForTableService: function computeForTableService(options, xhrOptions)
{
this.options = options;
this.xhrOptions = xhrOptions;
var sig = this._computeSignatureForTableService();
var result = 'SharedKey ' + config.storageAccount.name + ':' + sig;
return result;
},
_computeSignatureForTableService: function computeSignatureForTableService()
{
var sigString = this._getSignatureStringForTableService();
// TODO: use crypto from windows api. currently uses, google cryptoJS lib
var key = CryptoJS.enc.Base64.parse(config.storageAccount.primaryKey);
var hmac = CryptoJS.algo.HMAC.create(CryptoJS.algo.SHA256, key);
hmac.update(sigString);
var hash = hmac.finalize();
var result = hash.toString(CryptoJS.enc.Base64);
return result;
},
Interestingly, I have the whole code working fine 2 days before. I have updated service code to use updated azure nodejs sdk. I wonder if the update caused some incompat in the publisher/consumer code?
Other observations
The service code that uses azure nodejs module, is able to query the table storage without error.
I debugged through the azure nodejs module, looked through the stringToSign and matched with what winjs code is producing. both are same afaik.
service was upgrade to use 0.10.x node and respective latest azure nodejs sdk.
Example: stringToSign
GET\n\napplication/atom+xml;charset="utf-8"\nWed, 5 Jun 2013 14:43:30 GMT\n/devstoreaccount1/devstoreaccount1/mytable()
Thanks for going through details.
Finally - the root cause of the bug is out. The issue is value of x-ms-date header.
Expected value - Thu, 06 Jun 2013 08:09:50 GMT
Value computed in the code above - Thu, 6 Jun 2013 08:20:34 GMT
The 0 missing before the date is the root cause of this bug. Because of that, stringToSign used in computing the authorization header is incorrect. Hence, Authorization Header is incorrect leading to AuthenticationFailed error. This also explains the reason why this code worked couple of days back (end of may - date had two digits).
If someone from MS is reading this, it will be so much useful to have right amount of details along with the error code. AuthenticationFailed error code alone does not give any clue to developer.
I had used azure storage blob rest api earlier. It returns better error for the same AuthenticationFailed error code. It sends across the expected stringToSign and found stringToSign along with the AuthenticationFailed error code. It is so much more helpful and bug gets resolved in couple of minutes.
Used Network monitor from Microsoft. Wrote c# code snippet to make the azure table query using azure .net sdk, and compared every header character by character to hit the issue.