I am following this link in order to make an HTTP POST to an Eventhub in Azure. the error I am getting is 401 40103: Invalid authorization token signature. According to Azure, the POST should have the following format:
POST https://your-namespace.servicebus.windows.net/your-event-hub/partitions/0/messages?timeout=60&api-version=2014-01 HTTP/1.1
Authorization: SharedAccessSignature sr=your-namespace.servicebus.windows.net&sig=your-sas-key&se=1403736877&skn=RootManageSharedAccessKey
Content-Type: application/atom+xml;type=entry;charset=utf-8
Host: your-namespace.servicebus.windows.net
Regarding the Authorization header, I have several questions:
My secret key (sig) has an equal sign, should I substitute it by
%3d?
I am currently doing also the POST operation with the
example scripts in Python and C from Azure. In these examples, is
only required to introduce the endpoint with all the credentials and
the post/get operations works fine. Is it possible to perform the
put/get operations introducing directly the endpoint in the api
rest, or obtaining the authorization header that is performed bellow
the python or c code?
Thanks.
I'm able to make it work by creating a SAS policy (nssend below) on the namespace, not on the Event Hub.
$ curl -X POST -i "https://breakingnews.servicebus.windows.net/sucheventsmuchwow/messages" \
-H "Authorization: SharedAccessSignature sr=https%3A%2F%2Fbreakingnews.servicebus.windows.net%2Fsucheventsmuchwow%2Fmessages&sig=SAS_SIG_GOES_HERE&se=1536733370&skn=nssend" \
-H "Content-Type: application/json" \
--data-ascii "{ \"message\": \"So many events, so little time.\" }"
HTTP/1.1 201 Created
Server: Microsoft-HTTPAPI/2.0
...
So that works.
However, i'm getting a HTTP 401, just like you are, when using an Event Hub level SAS policy to generate the signature (as opposed to a namespace level policy).
This is what i used to generate the SAS token —
// Make a SAS token
// See https://learn.microsoft.com/en-us/rest/api/eventhub/generate-sas-token
// Appologies for JavaScript
// npm install moment
const moment = require('moment');
const crypto = require('crypto');
function create_sas_token(uri, key_name, key)
{
// Token expires in one hour
var expiry = moment().add(7, 'days').unix();
var string_to_sign = encodeURIComponent(uri) + '\n' + expiry;
var hmac = crypto.createHmac('sha256', key);
hmac.update(string_to_sign);
var signature = hmac.digest('base64');
var token = 'SharedAccessSignature sr=' +
encodeURIComponent(uri) +
'&sig=' + encodeURIComponent(signature) +
'&se=' + expiry + '&skn=' + key_name;
return token;
}
let token = create_sas_token('https://breakingnews.servicebus.windows.net/sucheventsmuchwow/messages', 'MySendPolicy', 'MySendPolicyPrimaryKey=');
console.log(token);
UPDATE
Thanks to Clemens Vasters —
Try omitting "/messages"— Clemens Vasters 📨, Messenger (#clemensv) September 5, 2018
Your string to sign (resource URI) should omit /messages, e.g.
create_sas_token('https://breakingnews.servicebus.windows.net/sucheventsmuchwow',
'MyEventHubLevelPolicy', 'hUbPriMAry/KEy=');
then craft your request as follows —
$ curl -X POST -i "https://breakingnews.servicebus.windows.net/sucheventsmuchwow/messages" \
-H "Authorization: SharedAccessSignature sr=https%3A%2F%2Fbreakingnews.servicebus.windows.net%2Fsucheventsmuchwow&sig=DONT_INCLUDE_/MESSAGES_IN_STRING_TO_SIGN&se=1536757127&skn=MyEventHubLevelPolicy" \
-H "Content-Type: application/json" \
--data-ascii "{ \"message\": \"SAS signed with Event Hub level policy\" }"
HTTP/1.1 201 Created
Server: Microsoft-HTTPAPI/2.0
...
TL;DR:
Your POST URL should include the trailing /messages, but the string to sign (resource URI) should not. Always. Regardless if using Namespace or Hub-scoped policy.
Related
I am trying to write data with the python code you can see in the GUI (IP:8086) for InfluxDB. My influxdb is version 2.5. I have the right influxdb-client for this version. I have tried creating new token, but the result is the same. I have followed all the steps in the tutorial.
The code is:
import influxdb_client, os, time
from influxdb_client import InfluxDBClient, Point, WritePrecision
from influxdb_client.client.write_api import SYNCHRONOUS
token = os.environ.get(“INFLUXDB_TOKEN”)
org = “censured”
url = “https://us-west-2-1.aws.cloud2.influxdata.com/”
client = influxdb_client.InfluxDBClient(url=url, token=token, org=org)
bucket=“censured”
write_api = client.write_api(write_options=SYNCHRONOUS)
for value in range(5):
point = (*
Point(“measurement1”)*
.tag(“tagname1”, “tagvalue1”)*
.field(“field1”, value)*
)*
write_api.write(bucket=bucket, org=“censured”, record=point)*
time.sleep(1) # separate points by 1 second*
And the output is this one:
influxdb_client.rest.ApiException: (401)
Reason: Unauthorized
HTTP response headers: HTTPHeaderDict({‘Date’: ‘Mon, 28 Nov 2022 12:30:02 GMT’, ‘Content-Type’: ‘application/json; charset=utf-8’, ‘Content-Length’: ‘55’, ‘Connection’: ‘keep-alive’, ‘trace-id’: ‘b834ca17b533f0e8’, ‘trace-sampled’: ‘false’, ‘x-platform-error-code’: ‘unauthorized’, ‘Strict-Transport-Security’: ‘max-age=15724800; includeSubDomains’, ‘X-Influxdb-Request-ID’: ‘2ae0c8dfaf88a4d59bc51e69332d3cdc’, ‘X-Influxdb-Build’: ‘Cloud’})
HTTP response body: {“code”:“unauthorized”,“message”:“unauthorized access”}
I am not able of writing or adding any data to the database.
I have tried changing url for localhost:8086, with same result.
I have tried communicating with the API with this code, but same result:
curl --request POST "http://localhost:8086/api/v2/write?org=YOUR_ORG&bucket=YOUR_BUCKET&precision=ns" --header "Authorization: Token YOUR_API_TOKEN" --header "Content-Type: text/plain; charset=utf-8" --header "Accept: application/json" --data-binary '
airSensors,sensor_id=TLM0201 temperature=73.97038159354763,humidity=35.23103248356096,co=0.48445310567793615 1630424257000000000
airSensors,sensor_id=TLM0202 temperature=75.30007505999716,humidity=35.651929918691714,co=0.5141876544505826 1630424257000000000
'
{"code":"unauthorized","message":"unauthorized access"}
I have a swagger generated (python-flask) web-server, which supports a variety of requests, which are fed by requests sent from an instance of QNetworkAccessManager (GET, PUT, POST).
They actually work like a charm, so I assume, that I got the main usage right, more or less.
What doesn't work though, is file upload using POST request. I actually tried several variants:
Providing the file content with QNetworkAccessManager::send,
Providing the file-pointer with QNetworkAccessManager::send (yes, I made sure, that the file is open and valid during the complete operation and cleaned up afterwards),
Using QMultiPart (see example below),
Playing with the request parameters didn't help either
But whatever I try, the request is recorded in the web-server, but connexion.request.files.getlist('file') is empty.
On the other hand, using curl -X POST --header 'Content-Type: multipart/form-data' --header 'Accept: application/json' -F "file=#/path/to/file/image.jpg" {"type":"formData"} '$myURL' makes it [<FileStorage: 'image.jpg' ('image/jpeg')>].
void uploadImage(QUrl const& url, std::filesystem::path const& filepath)
{
QNetworkRequest request {url};
request.setRawHeader("Content-Type", "multipart/form-data");
QHttpMultiPart *multiPart = new QHttpMultiPart(QHttpMultiPart::FormDataType);
QHttpPart imagePart;
imagePart.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name=\"" + QString::fromStdString(filepath.filename().native()) + "\"; filename=\"" + QString::fromStdString(filepath.native()) + "\""));
imagePart.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("image/jpeg"));
QFile *file = new QFile(QString::fromStdString(filepath.native()));
if (!file->open(QIODevice::ReadOnly)) {
std::cout << "could not open file" << std::endl;
}
QByteArray fileContent(file->readAll());
imagePart.setBody(fileContent);
multiPart->append(imagePart);
request.setHeader(QNetworkRequest::ContentLengthHeader, file->size());
auto reply = _manager->post(request, multiPart);
file->setParent(reply);
connect(reply, &QNetworkReply::finished, reply, &QNetworkReply::deleteLater);
}
QProcess using the curl command wasn't that happy and me neither, because, even though I'm running the code currently on a Linux machine, I don't want to restrict to Linux only.
Are there any suggestions on how to use Qt-Network in this context?
Does StackOverflow really autodeletes Hey guys from beginning of text? :D Hello, i have a problem i cant seem to wrap my mind around.
from influxdb_client import InfluxDBClient
from influxdb_client.client.write_api import SYNCHRONOUS
# You can generate a Token from the "Tokens Tab" in the UI
org = "myorg"
bucket = "mybucket"
token = 'valid_token'
client = InfluxDBClient(url="http://localhost:8086", token=token)
write_api = client.write_api(write_options=SYNCHRONOUS)
d='airSensors,sensor_id=TLM0201 temperature=70.97038159354763,humidity=55.23103248356096,co=0.78445310567793615 1637124357000000000'
write_api.write(bucket, org, d)
This runs and returns no error, i tried making a mistake in eg. bucket and it raises, bad token raises unauthorized, etc..
BUT there is no data in database when i check. BUT when i run this exact line through curl:
curl --request POST \
"http://localhost:8086/api/v2/write?org=myorg&bucket=mybucket&precision=ns" \
--header "Authorization: Token valid_token" \
--header "Content-Type: text/plain; charset=utf-8" \
--header "Accept: application/json" \
--data-binary '
airSensors,sensor_id=TLM0201 temperature=73.97038159354763,humidity=35.23103248356096,co=0.48445310567793615 1637024357000000000
airSensors,sensor_id=TLM0202 temperature=75.30007505999716,humidity=35.651929918691714,co=0.5141876544505826 1637024357000000000
'
This runs also with no errors but this time it actually writes into db.
Am i crazy or what? I tried everything, writing through Points, series,... u name it but it refuses to commit or smthn? Anyone had similar problem?
I run influxdb-client=1.23.0 on python=3.8.10 and Influxdb=2.0.7
Thanks for ur time. Q.
I guess you should use write_api.close() in the end of your write or use with:
with client.write_api() as write_api:
write_api.write(bucket, org, d)
https://github.com/influxdata/influxdb-client-python#writes
I'm trying to use Curl to perform a call with the DUO API.
I tried reviewed their docs here: https://duo.com/docs/adminapi#authentication
The docs says to pass the creds as HMAC key for the request but now sure how to get that going.
This is what I got so far:
curl --request GET \
--header 'Authorization: Basic 'Integration key:Secret key'' \
--header "Content-Type: application/x-www-form-urlencoded" \
"https://api-12345678.duosecurity.com/auth/v2/check"
Which returns
{"code": 40101, "message": "Missing request credentials", "stat": "FAIL"}
Can one point me to the right direction for an example in Bash. If not in Python.
First, your request format does not seem correct, because Integration key:Secret key'' is outside the header (look at the way the syntax is highlighted in the question).
Try:
curl --request GET \
--header 'Authorization: Basic' \
--header 'Integration key: Secret key' \
--header 'Date: Tue, 21 Aug 2012 17:29:18 -0000' \
--header "Content-Type: application/x-www-form-urlencoded" \
"https://api-12345678.duosecurity.com/auth/v2/check"
It's somewhat uncommon to have header names with a space and a lowercase like Integration key, so you may need to experiment with variants, like Integration-Key.
Second, the 401xx series errors mean:
401 The “Authorization”, “Date”, and/or “Content-Type” headers were missing or invalid.
You'll need to add the missing the Date header, required by the authenticator.
In case anyone else stumbles on this, here's what I came up with:
#!/bin/bash -u
FORM="Content-Type: application/x-www-form-urlencoded"
NOW=$(date -R)
#get these from the Duo Admin interface
INT="<integration key>"
KEY="<secret passcode>"
API="<api host>.duosecurity.com"
URL="/auth/v2/check"
REQ="$NOW\nGET\n$API\n$URL\n"
#could also use awk here, or the --binary mode as suggested elsewhere
HMAC=$(echo -n "$REQ" | openssl sha1 -hmac "$KEY" | cut -d" " -f 2)
AUTH=$(echo -n "$INT:$HMAC" | base64 -w0)
curl -s -H "Date: $NOW" -H $FORM -H "Authorization: Basic $AUTH" https://$API$URL
Running this yields:
{"response": {"time": 1539726254}, "stat": "OK"}
Reference: Duo Api docs section on authentication
I am trying to make a websocket connection to a URL(python client) which needs to have a jwt token passed in and the server(implemented in GO) listens to this request on and is supposed to authenticate by parsing the token.
I try to use this part of the code to make the request -
def test_auth_token(token)
conn = create_connection("ws://<IP>:port"+ '/'+ container.uuid + '?token='+token)
result = conn.recv()
assert result is not None
This request hits the server which runs this code to validate this request
func ParseFromRequest(req *http.Request, keyFunc Keyfunc) (token *Token, err error) {
// Look for an Authorization header
if ah := req.Header.Get("Authorization"); ah != "" {
// Should be a bearer token
if len(ah) > 6 && strings.ToUpper(ah[0:6]) == "BEARER" {
return Parse(ah[7:], keyFunc)
}
}
// Look for "access_token" parameter
req.ParseMultipartForm(10e6)
if tokStr := req.Form.Get("access_token"); tokStr != "" {
return Parse(tokStr, keyFunc)
}
return nil, ErrNoTokenInRequest
}
Every time, I am getting the "ErrNoTokenInRequest" output despite I am passing the token as a query parameter. The server side token validation is being done by this external library which contains the above GO Routine - https://github.com/dgrijalva/jwt-go/blob/master/jwt.go
I am not sure, what could be the possible reasons that server doesn't find the token sent in my client? Is it supposed to be sent as payload or headers or something else? Could someone point to get this module working?
With "access_token" as query parameter i get this exception -
self = <websocket._core.WebSocket object at 0x10a15a6d0>
host = 'x.x.x.x.', port = 9345
resource = '/v1/stats/fff51e85-f2bb-4ace-8dcc-fde590932cca?access_token=eyJhbGciOiJSUzI1NiJ9.eyJleHAiOjE0MjIxMzEyMzUsInN1YiI6ImNh...vxvBmtZRrUTY5AcvrjbojXqLxFHL_CMsmTZfTXhOiy-7W2V95bqts2Wy4R8oQvsfDylYJWCBTzZNKHvPVFpcl0jQKLm1ms-LOJg1w-k23VfojZucPGtY5A'
options = {}
headers = ['GET /v1/stats/fff51e85-f2bb-4ace-8dcc-fde590932cca?access_token=eyJhbGciOiJSUzI1NiJ9.eyJleHAiOjE0MjIxMzEyMzUsInN1YiI... 'Host: x.x.x.x.:9345', 'Origin: http://x.x.x.x.:9345', 'Sec-WebSocket-Key: BN1n2BcCT/CUGh9MHeyL5g==', ...]
key = 'BN1n2BcCT/CUGh9MHeyL5g=='
header_str = 'GET /v1/stats/fff51e85-f2bb-4ace-8dcc-fde590932cca?access_token=eyJhbGciOiJSUzI1NiJ9.eyJleHAiOjE0MjIxMzEyMzUsInN1YiI6...3:9345
Origin: http://192.168.59.103:9345
Sec-WebSocket-Key: BN1n2BcCT/CUGh9MHeyL5g==
Sec-WebSocket-Version: 13
Quite simply the serve does not expect the token to be handed over as a query parameter but instead expects it to be included in the headers of the request.
Example using the websocket library from https://github.com/liris/websocket-client
def test_auth_token(token)
header="Authorization: BEARER " + str(token)
conn = create_connection("ws://<IP>:port"+ '/'+ container.uuid", header)
result = conn.recv()
assert result is not None