I am trying to implement Direct Batch send API to send notification. Using Postman to simulate the request. And it returns the Invalid authorization token error.
Post Request :
https://mynamespace.servicebus.windows.net/myHub/messages/$batch?direct&api-version=2015-08
With below Headers:
Content-Type:application/json
Authorization:SharedAccessSignature sr=https://mynamespace.servicebus.windows.net/myHub/messages/$batch?direct%3fapi-version%3d2015-01&sig=xxxx&se=xxxx&skn=DefaultFullSharedAccessSignature
So how to resolved this error?
Also there is a APNS Example :
POST https://{Namespace}.servicebus.windows.net/{Notification Hub}/messages/$batch?direct&api-version=2015-08 HTTP/1.1
Content-Type: multipart/mixed; boundary="simple-boundary"
Authorization: SharedAccessSignature sr=https%3a%2f%2f{Namespace}.servicebus.windows.net%2f{Notification Hub}%2fmessages%2f%24batch%3fdirect%26api-version%3d2015-08&sig={Signature}&skn=DefaultFullSharedAccessSignature
ServiceBusNotification-Format: apple
Host: {Namespace}.servicebus.windows.net
Content-Length: 511
Expect: 100-continue
Connection: Keep-Alive
--simple-boundary
Content-Type: application/json
Content-Disposition: inline; name=notification
{"aps":{"alert":"Hello using APNS via Direct Batch Send!!!"}}
--simple-boundary
Content-Type: application/json
Content-Disposition: inline; name=devices
['Device Token1','Device Token2','Device Token3']
--simple-boundary--
--simple-boundary
Content-Type: application/json
Content-Disposition: inline; name=notification
{"aps":{"alert":"Hello using APNS via Direct Batch Send!!!"}}
--simple-boundary
Content-Type: application/json
Content-Disposition: inline; name=devices
['Device Token1','Device Token2','Device Token3']
--simple-boundary--
How to test it with Postman?
Have you tried to call Direct Batch Send using NotificationHubs SDK? If yes can you compare the http request from SDK with what Postman is sending?
Also the documentation about filling out the Authorization header contains a few inaccuracies. Below C# code shows how to do it the right way.
string GenerateSasToken(Uri uri, string sasKeyName, string sasKeyValue)
{
var targetUri = HttpUtility.UrlEncode(uri.ToString().ToLower(), Encoding.UTF8).ToLower();
var expiresOnDate = Convert.ToInt64(DateTime.UtcNow.Subtract(new DateTime(1970, 1, 1, 0, 0, 0)).TotalSeconds) + 60*60 /* one hour */;
var toSign = targetUri + "\n" + expiresOnDate;
var keyBytes = Encoding.UTF8.GetBytes(sasKeyValue);
var mac = new HMACSHA256(keyBytes);
mac.Initialize();
var rawHmac = mac.ComputeHash(Encoding.UTF8.GetBytes(toSign));
var signature = HttpUtility.UrlEncode(Convert.ToBase64String(rawHmac), Encoding.UTF8);
var token = "SharedAccessSignature sr=" + targetUri + "&sig=" + signature + "&se=" + expiresOnDate + "&skn=" + sasKeyName;
return token;
}
Related
I am experiencing a problem when working with CAST web receiver. DRM Video does not start on the TV (Android TV). Preloader works endlessly. How to solve this problem?
The title and description in the player is shown.
The logs show errors:
"shakaErrorCode":6007
LICENSE_REQUEST_FAILED - 6007 - The license request failed. This could be a timeout, a network failure, or a rejection by the server. error.data[0] is a shaka.util.Error from the networking engine. (https://shaka-player-demo.appspot.com/docs/api/shaka.util.Error.html)
"detailedErrorCode":905
LOAD_FAILED - 905 - A load command failed. - Verify the load request is set up properly and the media is able to play. (https://developers.google.com/cast/docs/web_receiver/error_codes?hl=en)
Error 6007 indicates problems with the license server. We checked the request parameters on another player and the DRM video starts. This does not work in cast receiver.
The problem occurs on the physical device
Sender: Redmi Note 8 Pro
Device with chromecast: Android Box
Chromecast built-in: 1.56.276477
CAST SDK CAF Version: 3.0.0103
API Request
Request URL: https://widevine-license.vudrm.tech/proxy
Request Method: POST
Status Code: 200 OK
Remote Address: XXX
Referrer Policy: strict-origin-when-cross-origin
Response Headers
access-control-allow-headers: Content-Type,X-Vudrm-Token
access-control-allow-methods: GET,POST,OPTIONS
access-control-allow-origin: *
Connection: keep-alive
Content-Length: 716
content-type: application/octet-stream
date: Mon, 13 Jun 2022 07:59:00 GMT
server: istio-envoy
x-b3-parentspanid: 3d92950cdf3d42ca
x-b3-sampled: 0
x-b3-spanid: 46ff3c9c3e815372
x-b3-traceid: XXX
x-envoy-upstream-healthchecked-cluster: widevine-license.default
x-envoy-upstream-service-time: 18
x-forwarded-for: XXX,XXX
x-request-id: XXX
x-vudrm: app=widevine-proxy; version=2.4.1
Request Headers
Accept: */*
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US
CAST-DEVICE-CAPABILITIES: {"bluetooth_supported":false,"display_supported":true,"hi_res_audio_supported":false,"remote_control_input_supported":false,"touch_input_supported":false}
Connection: keep-alive
Content-Length: 297
content-type: application/json
Host: widevine-license.vudrm.tech
Origin: XXX
Referer: XXX
sec-ch-ua
sec-ch-ua-mobile: ?0
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: cross-site
User-Agent: Mozilla/5.0 (Linux; Android 9.0; Build/PI) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.225 Safari/537.36 CrKey/1.56.500000
Request Payload
{"token":"XXX","drm_info":[8,4],"contentId":"VS-ADULTSWIM"}
receiver.js
const context = cast.framework.CastReceiverContext.getInstance();
const playerManager = context.getPlayerManager();
const playbackConfig = new cast.framework.PlaybackConfig();
const ContentType = {
DASH: 'application/dash+xml',
HLS: 'application/x-mpegurl'
};
const mediaFormatID = {
DASH: 2,
HLS: 4
};
// Debug Logger
const castDebugLogger = cast.debug.CastDebugLogger.getInstance();
const LOG_TAG = 'MyAPP.LOG';
// Enable debug logger and show a 'DEBUG MODE' overlay at top left corner.
castDebugLogger.setEnabled(true);
// Set verbosity level for Core events.
castDebugLogger.loggerLevelByEvents = {
'cast.framework.events.category.CORE': cast.framework.LoggerLevel.INFO,
'cast.framework.events.EventType.MEDIA_STATUS': cast.framework.LoggerLevel.DEBUG
};
// Set verbosity level for custom tags.
castDebugLogger.loggerLevelByTags = {
LOG_TAG: cast.framework.LoggerLevel.DEBUG,
};
playerManager.setMessageInterceptor(
cast.framework.messages.MessageType.LOAD,
request => {
return new Promise((resolve, _reject) => {
// Configure player to parse DASH content
if (request.media.metadata.mediaFormatID == mediaFormatID.DASH) {
request.media.contentUrl = request.media.contentUrl;
request.media.contentType = ContentType.DASH;
// Customize the license url for playback
if (request.media.metadata.licenseUrl) {
playbackConfig.licenseUrl = request.media.metadata.licenseUrl;
playbackConfig.protectionSystem = cast.framework.ContentProtection.WIDEVINE;
let token = request.media.metadata.token;
let contentId = request.media.metadata.contentId;
playbackConfig.licenseRequestHandler = requestInfo => {
requestInfo.withCredentials = false;
let body = {
token: token,
drm_info: Array.apply(null, new Uint8Array(requestInfo.content)),
contentId: contentId
};
body = JSON.stringify(body);
requestInfo.content = body;
requestInfo.headers["Content-Type"] = "application/json";
};
}
mpegDashStreamUrl = request.media.contentUrl;
vudrmToken = request.media.metadata.licenseUrl;
} else {
request.media.contentType = ContentType.HLS;
request.media.contentUrl = request.media.contentUrl;
request.media.hlsSegmentFormat = cast.framework.messages.HlsSegmentFormat.FMP4;
request.media.hlsVideoSegmentFormat = cast.framework.messages.HlsVideoSegmentFormat.FMP4;
}
// Add metadata
let metadata = new cast.framework.messages.GenericMediaMetadata();
metadata.title = request.media.metadata.title;
metadata.subtitle = request.media.metadata.subtitle;
request.media.metadata = metadata;
resolve(request);
});
});
context.start({playbackConfig: playbackConfig});
There are 3 handlers that are used by the Shaka response filters -
PlaybackConfig#manifestHandler
PlaybackConfig#licenseHandler
PlaybackConfig#segmentHandler
I added this code and the error was solved:
playbackConfig.licenseHandler = data => {
return new Promise((resolve, _reject) => {
resolve(new Uint8Array(data));
});
};
I try to use JWT Authentication but the API returns 400 Bad request {"error":"invalid_request"}.
Headers :
POST https://account-d.docusign.com/oauth/token HTTP/1.1
Connection: Keep-Alive
Content-Type: application/x-www-form-urlencoded; charset=utf-8
Body :
grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer&assertion=XXXX
The JWT token is well formatted and contains as body :
iss = _clientIntegratorKey,
sub = "628c4cc3-d36e-4a6b-80bf-b7ccb97d842c",
iat = (long)(now - new DateTime(1970, 1, 1)).TotalSeconds,
exp = (long)(now.AddSeconds(3600) - new DateTime(1970, 1, 1)).TotalSeconds,
aud = "account-d.docusign.com",
scope = "signature"
What am i missing ?
I used my own RSA function that not encrypt with the right cypher algorithm.
Now I use a library (c# dotnet core : RSACryptoServiceProvider) to do it and it works like a charm.
Using Apache Http 4.5 MultipartEntityBuilder and can't seem to figure out why the StringBody(String, ContentType) constructor doesn't actually output the Content-Type in the request form body.
public HttpRequestBase build() throws UnsupportedEncodingException {
HttpPost httpPost = new HttpPost("https://{server}/restapi/{apiVersion}/accounts/{accountId}/envelopes");
MultipartEntityBuilder builder = MultipartEntityBuilder.create();
builder.setContentType(ContentType.MULTIPART_FORM_DATA);
builder.setMode(HttpMultipartMode.BROWSER_COMPATIBLE);
builder.setBoundary("AAA");
//add form body
builder.addPart(generateJsonFormBodyPart());
//add file body
builder.addPart(generateFileFormBodyPart()); //<--intentionally omitted
HttpEntity multipart = builder.build();
httpPost.setEntity(multipart);
return httpPost;
}
private FormBodyPart generateJsonFormBodyPart() throws UnsupportedEncodingException{
StringBody json = new StringBody(packageJson(), ContentType.APPLICATION_JSON); //<--THIS DOESN'T SEEM TO WORK
StringBuilder buffer = new StringBuilder();
buffer.append("form-data");
String contentDisposition = buffer.toString();
FormBodyPartBuilder partBuilder = FormBodyPartBuilder.create("application/json", json);
partBuilder.setField(MIME.CONTENT_DISPOSITION, contentDisposition);
FormBodyPart fbp = partBuilder.build();
return fbp;
}
The file portion outputs ok but I get a "Bad Request" return from the peer which I assume is because it has very specific request parameters.
Required Request Output
Accept: application/json
Content-Type: multipart/form-data; boundary=AAA
--AAA
Content-Type: application/json
Content-Disposition: form-data
json removed
--AAA
Content-Type: application/pdf
Content-Disposition: file; filename="test1.pdf";documentid=1
document removed
Actual Apache Http 4.5 Output
X-Docusign-Act-As-User: xyz#company.com
Accept-Encoding: gzip,deflate
User-Agent: Apache-HttpClient/4.5 (Java/1.8.0_65)
Connect-Time: 0
Host: requestb.in
Connection: close
Content-Length: 3178
Authorization: bearer xxxxrandomoauthtokenxxxxx
Content-Type: multipart/form-data; boundary=AAA; charset=ISO-8859-1
Via: 1.1 vegur
X-Request-Id: 89cd1cf5-3615-41e8-84ba-cd076a03af67
Total-Route-Time: 0
--AAA
Content-Disposition: form-data //<--the problem. should be application/json no?
{"status":"created","emailBlurb":"Welcome to Confluence","emailSubject":"Welcome to Confluence","documents":{"name":"Welcome to Confluence.html","documentId":"1","order":"1"},"recipients":{}}
--AAA
Content-Disposition: file; filename="Welcome.html";documentid=1
Content-Type: text/html; charset=ISO-8859-1
h t m l string removed
Q: So why does the ContentType in the StringBody constructor get ignored? Is there a workaround or am I doing it wrong?
I'm convinced this is a bug in Apache Http Mime for the FormBodyPart.build() when processing a StringBody. Evidence from release notes of 4.5.2 indicating bug fixes for other "body" types not outputting "Content-Type" supports this. I will be logging a defect.
The Workaround:
private FormBodyPart generateJsonFormBodyPart() throws UnsupportedEncodingException{
StringBody json = new StringBody(getMyJsonStuff(), ContentType.APPLICATION_JSON); //<--THE GOGGLES, THEY DO NOTHING!!
StringBuilder buffer = new StringBuilder();
buffer.append("form-data");
buffer.append("\r\n");
buffer.append("Content-Type: application/json"); //<--tack this on to the
String kludgeForDispositionAndContentType = buffer.toString();
FormBodyPartBuilder partBuilder = FormBodyPartBuilder.create("stuff", json);
partBuilder.setField(MIME.CONTENT_DISPOSITION, kludgeForDispositionAndContentType);
FormBodyPart fbp = partBuilder.build();
return fbp;
}
Works like a charm.
I've developed a fileupload tool (using nervgh fileupload for angularjs) that works fine in development. The file(s) are send to a WebApi upload controller:
public class UploadController : ApiController
{
public async Task<HttpResponseMessage> PostFormData()
{
// Check if the request contains multipart/form-data.
if (!Request.Content.IsMimeMultipartContent())
{
throw new HttpResponseException(HttpStatusCode.UnsupportedMediaType);
}
string root = HttpContext.Current.Server.MapPath(#"~\" + Properties.Settings.Default.uploadDir);
var provider = new MultipartFormDataStreamProvider(root);
var documentNewDir = "";
var documentRelative = "";
var fileName = "";
try
{
// Read the form data.
await Request.Content.ReadAsMultipartAsync(provider);
// This illustrates how to get the file names.
foreach (MultipartFileData file in provider.FileData)
{
var vergaderDate = Convert.ToDateTime(provider.FormData["vergaderdatum"]);
var jaar = vergaderDate.Year;
var maand= vergaderDate.Month.ToString(CultureInfo.CurrentCulture).PadLeft(2, '0');
var dag =vergaderDate.Day.ToString(CultureInfo.CurrentCulture).PadLeft(2, '0');
//absolute path to documents new dir
documentNewDir = HttpContext.Current.Server.MapPath("/") + Properties.Settings.Default.documentsBaseDir + #"\" + jaar + #"\" + maand + #"\" + dag;
//filename
fileName = file.Headers.ContentDisposition.FileName.Replace("\"", string.Empty);
//relativePath and filename
documentRelative = Properties.Settings.Default.documentsBaseDir + #"/" + jaar + #"/" + maand + #"/" + dag + #"/"+ fileName;
if(!Directory.Exists(documentNewDir))
Directory.CreateDirectory(documentNewDir);
File.Move(file.LocalFileName, documentNewDir + #"\"+fileName);
}
var response = Request.CreateResponse(HttpStatusCode.OK, documentRelative);
return response;
}
catch (System.Exception e)
{
return Request.CreateErrorResponse(HttpStatusCode.InternalServerError, e);
}
}
}
This works fine in a development setting but deployed to a MS 2012 server IIS 8.5 production server i get an 404 on the POST API call to api/upload.
The application support some other WebAPI calls that worke fine in both dev/prod environments.... How can i start to investigate/debug this situation?
Here's the call in Fiddler ( left out the filedata....):
-----------------------------7df2b09e0350
Content-Disposition: form-data; name="vergaderdatum"
2015-01-30T00:00:00
-----------------------------7df2b09e0350
Content-Disposition: form-data; name="file"; filename="5.0. Vooraankondiging themabijeenkomstSamen naar Buiten, 17 sept.pdf"
Content-Type: application/pdf
%PDF-1.5
%âãÏÓ
and the response
HTTP/1.1 404 Not Found
Content-Type: text/html
Server: Microsoft-IIS/8.5
X-Powered-By: ASP.NET
Date: Wed, 14 Jan 2015 15:31:08 GMT
Content-Length: 1245
I'VE FOUND THE ERROR!
in production the website was installed as a subdomain of the default website. In the javascript was a reference to the upload method (/api/upload) after i added the name of the subdomain like /bisbeheer/api/upload in the javascript it uploaded the file
I am having a problem with stringtosign authentication for azure table pagination query.
This is the current stringtosign im using :
GET\n\n\nFri, 05 Sep 2014 03:57:11 GMT\n/mystorageaccount/mytablename\nNextPartitionKey:1!20!UmFjZSBNZW1iZXJfNA--\nNextRowKey:1!12!TmFtZV85ODE-
Is there anything wrong with this stringtosign authentication?
The rest of the Headers are exactly the same as Fiddle.
Example
GET /mytablename?NextPartitionKey=1%2120%21UmFjZSBNZW1iZXJfNA--&NextRowKey=1%2112%21TmFtZV85ODE- HTTP/1.1
Host: mystorageaccount.table.core.windows.net
x-ms-version: 2014-02-14
x-ms-date: Fri, 05 Sep 2014 05:49:19 GMT
Authorization: SharedKey mystorageaccount:GD2w4pqsllzIOixNF/AfFqLkZhYzLpjK67a8OI7j6Go=
Accept: application/atom+xml
Accept-Charset: UTF-8
DataServiceVersion: 3.0;NetFx
MaxDataServiceVersion: 3.0;NetFx
I have read through both
http://msdn.microsoft.com/library/azure/dd179428.aspx
http://msdn.microsoft.com/en-us/library/azure/dd135718.aspx
Hi Gaurav Mantri,
It still did not work. I'll paste the request, my stringtosign and the response below:
GET /mytablename?NextPartitionKey=1%2120%21UmFjZSBNZW1iZXJfNA--&NextRowKey=1%2112%21TmFtZV85ODE- HTTP/1.1
Host: mystorageaccount.table.core.windows.net
x-ms-version: 2014-02-14
x-ms-date: Fri, 05 Sep 2014 07:05:12 GMT
Authorization: SharedKey mystorageaccount:HSYfO1Baadqcd4bQO5Q6uN1hrr2aXtLcQbFPkWgIXuw=
Accept: application/atom+xml
Accept-Charset: UTF-8
DataServiceVersion: 3.0;NetFx
MaxDataServiceVersion: 3.0;NetFx
String to sign:
GET\n\n\nFri, 05 Sep 2014 07:05:12 GMT\n/mystorageaccount/mytablename\nnextpartitionkey:1!20!UmFjZSBNZW1iZXJfNA--\nnextrowkey:1!12!TmFtZV85ODE-
Response:
<?xml version=\"1.0\" encoding=\"utf-8\"?><m:error xmlns:m=\"http://schemas.microsoft.com/ado/2007/08/dataservices/metadata\"><m:code>AuthenticationFailed</m:code><m:message xml:lang=\"en-US\">Server failed to authenticate the request. Make sure the value of Authorization header is formed correctly including the signature.
RequestId:37272f11-0002-0014-5aa7-f7dd1c000000
Time:2014-09-05T07:05:09.5720897Z</m:message></m:error>
I had an opportunity to actually write the code and try it out. Basically when creating CanonicalizedResource string for table resources, you need not include the query string parameters other than comp querystring parameter. Essentially this is what you would need to follow from the documentation (http://msdn.microsoft.com/library/azure/dd179428.aspx):
2009-09-19 Shared Key Lite and Table Service Format
This format supports Shared Key and Shared Key Lite for all versions
of the Table service, and Shared Key Lite for the 2009-09-19 version
of the Blob and Queue services and 2014-02-14 of the File service.
This format is identical to that used with previous versions of the
storage services. Construct the CanonicalizedResource string in this
format as follows:
Beginning with an empty string (""), append a forward slash (/), followed by the name of the account that owns the resource being
accessed.
Append the resource's encoded URI path. If the request URI addresses a component of the resource, append the appropriate query
string. The query string should include the question mark and the comp
parameter (for example, ?comp=metadata). No other parameters should be
included on the query string.
Once you do that, your code should run just fine. Here's the sample code I wrote:
static void QueryTable()
{
var requestMethod = "GET";
var storageServiceVersion = "2014-02-14";
var date = DateTime.UtcNow.ToString("R");
var canonicalizedResource = string.Format("/{0}/{1}", StorageAccount, TableName);
var stringToSign = string.Format("{0}\n\n\n{1}\n{2}", requestMethod, date, canonicalizedResource);
var authorizationHeader = GetAuthorizationHeader(stringToSign);
using (var httpClient = new HttpClient())
{
httpClient.BaseAddress = new Uri(TableEndpoint);
httpClient.DefaultRequestHeaders.Clear();
httpClient.DefaultRequestHeaders.Add("x-ms-date", date);
httpClient.DefaultRequestHeaders.Add("x-ms-version", storageServiceVersion);
httpClient.DefaultRequestHeaders.Add("Authorization", authorizationHeader);
var result = httpClient.GetAsync(TableName + "?NextPartitionKey=1!48!VXwzMzg0MDAzOWYzMjQ0ZDgxOWZjZmM5M2EyMzNkM2IxOA--&NextRowKey=1!0!");
result.Wait();
}
}
static string GetAuthorizationHeader(string canonicalizedString)
{
var signature = string.Empty;
using (var hash = new HMACSHA256(Convert.FromBase64String(StorageAccountKey)))
{
var data = Encoding.UTF8.GetBytes(canonicalizedString);
signature = Convert.ToBase64String(hash.ComputeHash(data));
}
return string.Format(CultureInfo.InvariantCulture, "{0} {1}:{2}", "SharedKey", StorageAccount, signature);
}
Based on the documentation here: http://msdn.microsoft.com/library/azure/dd179428.aspx (2009-09-19 Shared Key Format Section, point #4), you would need to convert all query parameters to lowercase. So your canonicalized resource string should be:
GET\n\n\nFri, 05 Sep 2014 03:57:11 GMT\n/mystorageaccount/mytablename\nnextpartitionkey:1!20!UmFjZSBNZW1iZXJfNA--\nnextrowkey:1!12!TmFtZV85ODE-
Give it a try. That should take care of the problem.