How to read Media from Attachment using REST API - azure-cosmosdb

I have created an Attachment with image:
{
"_rid": "xD4vALTE7QBAAwAAAAAAAA==",
"Attachments": [
{
"contentType": "image/jpeg",
"id": "10b91d7d-2e5e-466e-a896-3ee54baff4dc",
"media": "/media/xD4vALTE7QBAAwAAAAAAALobDgYB",
"_rid": "xD4vALTE7QBAAwAAAAAAALobDgY=",
"_self": "dbs/xD4vAA==/colls/xD4vALTE7QA=/docs/xD4vALTE7QBAAwAAAAAAAA==/attachments/xD4vALTE7QBAAwAAAAAAALobDgY=",
"_etag": "\"00000d37-0000-0000-0000-5a09602a0000\"",
"_ts": 1510563882
}
],
"_count": 1
}
What is the url to the Media object?
When I use the Cosmos .NET SDK method DocumentClient.ReadMediaAsync(string mediaLink) where mediaLink is /media/xD4vALTE7QBAAwAAAAAAALobDgY= then I can get the Media stream and display the image. But this only works when connecting to Cosmos Db Emulator, when doing the same on the Azure Cosmos DB instance, then I get this error:
Unknown server error occurred when processing this request. ActivityId: 53508de6-2456-4213-947e-4361a8118574, Microsoft.Azure.Documents.Common/1.17.99.1, documentdb-dotnet-sdk/1.19.0 Host/32-bit MicrosoftWindowsNT/6.2.9200.0
The Attachment creation with Medial upload works.
I was thinking to try to query the CosmosDB using the Postman, but I cannot figure out the url to get the Media object.

The url to the Media object is: https://[YOUR-DOCDB-HOST]/media/xD4vALTE7QBAAwAAAAAAALobDgYB
I call this as a GET request in Postman with the following Pre-Request Script:
// store our master key for documentdb
var mastKey = [YOUR-DOCUMENTDB-KEY-HERE];
console.log("mastKey = " + mastKey);
// store our date as RFC1123 format for the request
var today = new Date();
var UTCstring = today.toUTCString();
postman.setEnvironmentVariable("RFC1123time", UTCstring);
// define resourceId/Type now so we can assign based on the amount of levels
var resourceId = "[YOUR-RID-HERE]"; // _rid in attachment document
var resType = "media";
// assign our verb
var verb = request.method.toLowerCase();
// assign our RFC 1123 date
var date = UTCstring.toLowerCase();
// parse our master key out as base64 encoding
var key = CryptoJS.enc.Base64.parse(mastKey);
console.log("key = " + key);
// build up the request text for the signature so can sign it along with the key
var text = (verb || "").toLowerCase() + "\n" +
(resType || "").toLowerCase() + "\n" +
(resourceId || "").toLowerCase() + "\n" +
(date || "").toLowerCase() + "\n" +
"" + "\n";
console.log("text = " + text);
// create the signature from build up request text
var signature = CryptoJS.HmacSHA256(text, key);
console.log("sig = " + signature);
// back to base 64 bits
var base64Bits = CryptoJS.enc.Base64.stringify(signature);
console.log("base64bits = " + base64Bits);
// format our authentication token and URI encode it.
var MasterToken = "master";
var TokenVersion = "1.0";
auth = encodeURIComponent("type=" + MasterToken + "&ver=" + TokenVersion + "&sig=" + base64Bits);
console.log("auth = " + auth);
// set our auth token enviornmental variable.
postman.setEnvironmentVariable("authToken", auth);
There are two variables: [YOUR-DOCUMENTDB-KEY-HERE] and [YOUR-RID-HERE]. The kicker is, you need to query attachment meta data first to get the _rid value to use in the token when requesting the media.
The authToken and RFC1123time postman variables are used in the Authorization header:

Related

Unable to send notification from an Expo React Native App using Azure Notification Hub REST API

I am trying to receive notifications in an Expo React Native App.
The notifications will be sent using Azure Notification Hub REST API
I followed the steps below :
Added the Android project in Firebase Console
To get the Server Key I followed - Firebase messaging, where to get Server Key?
Configured the FCM ServerKey in Azure Notification Hub
Added the google-services.json at the root in my React Native App and modified app.json as mentioned in - https://docs.expo.dev/push-notifications/using-fcm/
To register in ANH, we first need the SAS Token - https://learn.microsoft.com/en-us/rest/api/notificationhubs/common-concepts I generated the token with the following code
const Crypto = require('crypto-js');
const resourceURI =
'http://myNotifHubNameSpace.servicebus.windows.net/myNotifHubName ';
const sasKeyName = 'DefaultListenSharedAccessSignature';
const sasKeyValue = 'xxxxxxxxxxxx';
const expiresInMins = 200;
let sasToken;
let location;
let registrationID;
let deviceToken;
function getSASToken(targetUri, sharedKey, ruleId, expiresInMins) {
targetUri = encodeURIComponent(targetUri.toLowerCase()).toLowerCase();
// Set expiration in seconds
var expireOnDate = new Date();
expireOnDate.setMinutes(expireOnDate.getMinutes() + expiresInMins);
var expires =
Date.UTC(
expireOnDate.getUTCFullYear(),
expireOnDate.getUTCMonth(),
expireOnDate.getUTCDate(),
expireOnDate.getUTCHours(),
expireOnDate.getUTCMinutes(),
expireOnDate.getUTCSeconds()
) / 1000;
var tosign = targetUri + '\n' + expires;
// using CryptoJS
//var signature = CryptoJS.HmacSHA256(tosign, sharedKey);
var signature = Crypto.HmacSHA256(tosign, sharedKey);
var base64signature = signature.toString(Crypto.enc.Base64);
//var base64signature = signature.toString(CryptoJS.enc.Base64);
var base64UriEncoded = encodeURIComponent(base64signature);
// construct autorization string
var token =
'SharedAccessSignature sr=' +
targetUri +
'&sig=' +
base64UriEncoded +
'&se=' +
expires +
'&skn=' +
ruleId;
console.log('signature:' + token);
return token;
}
I then called the create registration API - https://learn.microsoft.com/en-us/rest/api/notificationhubs/create-registration-id
The registrationID has to be extracted from the response header of the API Call
I used the following code to generate the ANH Regsitration ID
async function createRegistrationId() {
const endpoint =
'https://xxxxxx.servicebus.windows.net/xxxxxxx/registrationIDs/?api-version=2015-01';
sasToken = getSASToken(resourceURI, sasKeyValue, sasKeyName, expiresInMins);
const headers = {
Authorization: sasToken,
};
const options = {
method: 'POST',
headers: headers,
};
const response = await fetch(endpoint, options);
if (response.status !== 201) {
console.log(
'Unbale to create registration ID. Status Code: ' + response.status
);
}
console.log('Response Object : ', response);
for (var pair of response.headers.entries()) {
//console.log(pair[0] + ': ' + pair[1]);
}
location = response.headers.get('Location');
console.log('Location - ' + location);
console.log('Type - ' + response.type);
registrationID = location.substring(
location.lastIndexOf('registrationIDs/') + 'registrationIDs/'.length,
location.lastIndexOf('?api-version=2015-01')
);
console.log('Regsitration ID - ', registrationID);
return location;
}
Next step was to update this registration ID in ANH with the Native Device Token
I used expo-notifications package and the method getDevicePushTokenAsync() method to get the native device token
async function registerForPushNotificationsAsync() {
let token;
if (Device.isDevice) {
const { status: existingStatus } = await Notifications.getPermissionsAsync();
let finalStatus = existingStatus;
if (existingStatus !== 'granted') {
const {
status
} = await Notifications.requestPermissionsAsync();
finalStatus = status;
}
if (finalStatus !== 'granted') {
alert('Failed to get push token for push notification!');
return;
}
token = (await Notifications.getDevicePushTokenAsync()).data;
console.log(token);
} else {
alert('Must use physical device for Push Notifications');
}
if (Platform.OS === 'android') {
Notifications.setNotificationChannelAsync('default', {
name: 'default',
importance: Notifications.AndroidImportance.MAX,
vibrationPattern: [0, 250, 250, 250],
lightColor: '#FF231F7C',
});
}
return token;
}
The native device token was in the following format on Android device
c6RI81R7Rn66kWZ0rar3M2:APA91bEcbLXGwEZF-8hu1yGHfXgWBNuxr_4NY_MR8d7HEzeHAJrjoJnjUlneAIiVglCNIGUr11qkP1G4S76bx_H7NItxfQhZa_bgnQjqSlSaY4-oCoarDYWcY-Mz_ulW8rQZFy_SA6_j
I then called the updateRegistrationId API - https://learn.microsoft.com/en-us/rest/api/notificationhubs/create-update-registration
async function updateRegistraitonId() {
//IF you use registrationIDs as in returned location it was giving 401 error
const endpoint =
'https://xxxxx.servicebus.windows.net/xxxxxxx/registrations/' +
registrationID +
'?api-version=2015-01';
const endpoint1 = location;
const headers = {
Authorization: sasToken,
'Content-Type': 'application/atom+xml;type=entry;charset=utf-8',
};
//Remember to create well-formed XML using back-ticks
//else you may get 400 error
//If you use the tags element it was giving an error
const regDATA = `<entry xmlns="http://www.w3.org/2005/Atom">
<content type="application/xml">
<GcmRegistrationDescription xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.microsoft.com/netservices/2010/10/servicebus/connect">
<GcmRegistrationId>${deviceToken}</GcmRegistrationId>
</GcmRegistrationDescription>
</content>
</entry>`;
const options = {
method: 'PUT',
headers: headers,
body: regDATA,
};
const response = await fetch(endpoint, options);
if (response.status !== 201) {
console.log(
'Looks like there was a problem. Status Code: ' + response.status
);
console.log('Response Object : ', response);
//return;
}
}
According to API documentation, I should get 201 response, I got 200 response code . I am not sure if this is the issue
After this I had the notification handling code to recieve the notification,similar to the code in - https://docs.expo.dev/versions/latest/sdk/notifications/
I then tried to send notification using Test Send from ANH, it failed with the error -
**
"The Token obtained from the Token Provider is wrong"
**
I checked in ANH Metrics, the error was categorized as GCM Authentication error, GCM Result:Mismatch SenderId
I tried to check for documentation to add the SenderId , but I couldnt find anyway to inlcude the SenderId also in the payload of updateRegistration call (in xml atom entry)
I tried to use the device token and send directly from Firebase Console, I did not receive it either.
I used the Direct Send API of Azure notification Hub but still did not receive anything
I am suspecting there could be some issue in the way I am handling notifiations in the client device, I can fix that later , but first I will have to resolve the error I am getting in Test Send in Azure NH
Any help to be able to successfully send using Test Send in ANH or pointers ahead for next steps will be much appreciated

SSJS HTTP POST BASIC AUTH ISSUE

I have seen a few posts about how this can be done using oauth, but I need to use basic auth using SSJS and I can't seem to get it to work. Below is an example of the code I'm using.
<script runat = "server" >
Platform.Load("core", "1.1.5");
try {
var authEndpoint = "https://example.com/api/v2/oauth/token?grant_type=client_credentials";
var contentType = 'application/json';
var user = 'XXX';
var password = 'XXXX';
var headers = 'Authorization: Basic ' + Platform.Function.Base64Encode(user + ':' + password);
var payload = '';
var accessTokenRequest = HTTP.Post(authEndpoint, contentType, payload, headers);
if (accessTokenRequest.StatusCode == 200) {
var tokenResponse = Platform.Function.ParseJSON(accessTokenRequest.Response[0]);
var accessToken = tokenResponse.access_token
Write("Access Token =" + accessToken + "<br>");
};
} catch (error) {
Write(Stringify(error));
}
</script>
I am getting below error
{"message":"Unable to retrieve security descriptor for this frame.","description":"System.InvalidOperationException: Unable to retrieve security descriptor for this frame. - from mscorlib\r\n\r\n"}

WooCommerce REST API with Google UrlFetchApp header authorization not working

CURRENTLY
I am utilising WooCommerce REST API in my Google Scripts with the following working code:
var ck = "ck_longstringlongstringlongstringlongstring";
var cs = "cs_longstringlongstringlongstringlongstring";
var website = "https://www.mywebsite.com.au";
var url = website + "/wp-json/wc/v3/orders?consumer_key=" + ck + "&consumer_secret=" + cs;
var options =
{
"method": "GET",
"Content-Type": "application/x-www-form-urlencoded;charset=UTF-8",
"muteHttpExceptions": true,
};
var result = UrlFetchApp.fetch(url, options);
PROBLEM
To improve security, I want to put consumer key and secret into the header, but I cannot get the following script to work
var url = website + "/wp-json/wc/v3/orders;
let authHeader = 'Basic ' + Utilities.base64Encode(ck + ':' + cs);
var options =
{
"method": "GET",
"Content-Type": "application/x-www-form-urlencoded;charset=UTF-8",
"muteHttpExceptions": true,
"headers": {"Authorization": authHeader},
};
var result = UrlFetchApp.fetch(url, options);
Current result = {"code":"woocommerce_rest_cannot_view","message":"Sorry, you cannot list resources.","data":{"status":401}}
Expected result = JSON of orders
Is it an issue with my code? Or with WooCommerce API or Google Scripts?
There are a few issues with your code.
With UrlFetchApp.fetch() you need to use contentType instead of Content-Type.
However, in this case you don't even need to set it since application/x-www-form-urlencoded is the default. Same goes for the method property; it defaults to GET.
Moreover, if you want to err on the side of caution use Utilities.base64EncodeWebSafe(data, charset) to base64 encode your credentials instead of the non-web-safe version.
let url = website + "/wp-json/wc/v3/orders";
let encoded = Utilities.base64EncodeWebSafe(ck + ':' + cs, Utilities.Charset.UTF_8);
let options = {
"muteHttpExceptions":true,
"headers": {
"Authorization": "Basic " + encoded
}
};
let result = UrlFetchApp.fetch(url, options);
result = JSON.parse(result);

ASP.Net MVC 5 How to encrypt JWT Tokens

I have gone throw few posts about using JWT in ASP.Net MVC, which guides how to issue and consume Signed JSON Web Tokens.
Can anyone please guide how to issue and consume encrypted JWT following the JSON Web Encryption (JWE) specifications in case we need to transmit some sensitive data in the JWT payload.
Understanding JWT
JSON Web Token (JWT) is a compact URL-safe means of representing claims to be transferred between two parties. The claims in a JWT are encoded as a JavaScript Object Notation (JSON) object that is used as the payload of a JSON Web Signature (JWS) structure or as the plaintext of a JSON Web Encryption (JWE) structure, enabling the claims to be digitally signed or MACed and/or encrypted.
What JWT?
https://jwt.io/introduction/
Json Web Token Standards
https://datatracker.ietf.org/doc/html/draft-ietf-oauth-json-web-token-25
Anatomy of JWT
https://scotch.io/tutorials/the-anatomy-of-a-json-web-token
Creating JSON Web Token in JavaScript
https://www.jonathan-petitcolas.com/2014/11/27/creating-json-web-token-in-javascript.html
Now, We understand JWT call and how we can serve it from server side.
Here i have HTML page in which I have button and also set some custom parameters.
<script src="//cdnjs.cloudflare.com/ajax/libs/crypto-js/3.1.2/rollups/hmac-sha256.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/crypto-js/3.1.2/components/enc-base64-min.js"></script>
<script language="JavaScript" type="text/javascript" src="https://kjur.github.io/jsrsasign/jsrsasign-latest-all-min.js"></script>
<script type="text/javascript">
$(function () {
$("#btnJWTApi").click(function () {
// Defining our token parts
// You can use one of these, as alg
// HS256, HS386, HS512
// Always keep type as JWT
var header = {
"alg": "HS256",
"typ": "JWT"
};
var tNow = KJUR.jws.IntDate.getNow();
var tEnd = KJUR.jws.IntDate.getNow() + 60 * 5;
// dynamically pass these data using a function
var data = {
"appId": "yourAppId",
"iat": tNow,
// iat (issued at time) should be set to time when request has been generated
"exp": tEnd,
// exp (expiration) should not be more than 5 minutes from now, this is to prevent Replay Attacks
"method": "TestMethod",
"Q": "test",
"SecretKey": "MySecretKey"
};
// Secret key is used for calculating and verifying the signature.
// The secret signing key MUST only be accessible by the issuer and the User,
// it should not be accessible outside of these two parties.
// Use the Secret you set during user registration from the Plugin
var secret = btoa('MySecret ');
function base64url(source) {
// Encode in classical base64
encodedSource = CryptoJS.enc.Base64.stringify(source);
// Remove padding equal characters
encodedSource = encodedSource.replace(/=+$/, '');
// Replace characters according to base64url specifications
encodedSource = encodedSource.replace(/\+/g, '-');
encodedSource = encodedSource.replace(/\//g, '_');
return encodedSource;
}
var stringifiedHeader = CryptoJS.enc.Utf8.parse(JSON.stringify(header));
var encodedHeader = base64url(stringifiedHeader);
var stringifiedData = CryptoJS.enc.Utf8.parse(JSON.stringify(data));
var encodedData = base64url(stringifiedData);
var signature = encodedHeader + "." + encodedData;
signature = CryptoJS.HmacSHA256(signature, secret);
signature = base64url(signature);
var targetEle = $("#data");
$.ajax(
{
type: "POST",
url: "http://localhost:12345/api/v1/MyController/SecureMethod",
data: '{"token":"' + encodedHeader + "." + encodedData + "." + signature + '"}',
contentType: "application/json; charset=utf-8",
dataType: "json",
success: function (data) {
targetEle.html("<pre>" + JSON.stringify(data, null, '\t') + "</pre>");
},
error: function () {
alert('error');
}
});
});
});
</script>
This call will generate encrypted token which include appId,secret and our payload data with method name.
(Here create one common method, which call first and then according to passing data in a token further method will be call)
This will call your method SecureMethod instead of direct TestMethod.
And decrypt token.
public string SecureMethod(dynamic tokenObject)
{
//save at a time of user registration.
string applicationID = appSecret get from database;
string secretKey = appSecret get from database;
}
var bytes = Encoding.UTF8.GetBytes(secretKey);
var secret = Convert.ToBase64String(bytes);
var jwtDecryption = JsonWebToken.DecodeToObject(token, secret, true, true);
var jsonObj = JObject.FromObject(jwtDecryption);
string appId = jsonObj["appId"].Value<string>();
if (appId.Equals(applicationID)
{
object restService = new MyController();
var method = restService.GetType().GetMethod(jsonObj["method"].ToString(), BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
}
if (method != null)
{
var parameters = method.GetParameters().Select(p => Convert.ChangeType(jsonObj[p.Name].ToString(), p.ParameterType)).ToArray();
object response = method.Invoke(restService, parameters); //your actual method should
return new JavaScriptSerializer().Serialize(response);
}
method.Invoke(restService, parameters); will have method name and parameter so it'll called your method and pass parameters.
public IHttpActionResult TestMethod([FromBody]Response model)
{
// you will get parameters in a model
return Ok();
}
Any suggestion welcome!

How do I properly represent an Event object for Microsoft Graph?

I'm currently trying to create events on a users Outlook Calendar using the Microsoft Graph API with ASP .NET MVC. Unfortunately the documentation for creating the events does not include samples. I've found and tried to modify the sample for sending emails ( here: https://github.com/microsoftgraph/aspnet-connect-rest-sample ). I was able to send an initial events totally blank to my calendar. When attempting to send the event object itself I am met with the status response BAD REQUEST. If anyone could help, it'd be greatly appreciated.
For a reference of how you might construct the Event object, you can take a look how the official Microsoft Graph SDK has constructed this object: see the latest source here on GitHub.
For an example of making this REST call without using the SDK, you can reference UserSnippets#CreateEventAsync() - an excerpt is pasted below. While the below example doesn't use native objects for serialization, it hopefully conveys the essence of how it might work.
HttpClient client = new HttpClient();
var token = await AuthenticationHelper.GetTokenHelperAsync();
client.DefaultRequestHeaders.Add("Authorization", "Bearer " + token);
// Endpoint for the current user's events
Uri eventsEndpoint = new Uri(serviceEndpoint + "me/events");
// Build contents of post body and convert to StringContent object.
// Using line breaks for readability.
// Specifying the round-trip format specifier ("o") to the DateTimeOffset.ToString() method
// so that the datetime string can be converted into an Edm.DateTimeOffset object:
// https://msdn.microsoft.com/en-us/library/az4se3k1(v=vs.110).aspx#Roundtrip
string postBody = "{'Subject':'Weekly Sync'," + "'Location':{'DisplayName':'Water Cooler'}," + "'Attendees':[{'Type':'Required','EmailAddress': {'Address':'mara#fabrikam.com'} }]," + "'Start': {'DateTime': '" + new DateTime(2014, 12, 1, 9, 30, 0).ToString("o") + "', 'TimeZone':'UTC'}," + "'End': {'DateTime': '" + new DateTime(2014, 12, 1, 10, 0, 0).ToString("o") + "', 'TimeZone':'UTC'}," + "'Body':{'Content': 'Status updates, blocking issues, and next steps.', 'ContentType':'Text'}}";
var createBody = new StringContent(postBody, System.Text.Encoding.UTF8, "application/json");
HttpResponseMessage response = await client.PostAsync(eventsEndpoint, createBody);
if (response.IsSuccessStatusCode) {
string responseContent = await response.Content.ReadAsStringAsync();
jResult = JObject.Parse(responseContent);
createdEventId = (string) jResult["id"];
Debug.WriteLine("Created event: " + createdEventId);
} else {
// some appropriate error handling here
}
For an example of what the transmitted JSON might look like:
{
"subject": "Weekly Sync",
"location": {
"displayName": "Water Cooler"
},
"attendees": [
{
"type": "Required",
"emailAddress": {
"address": "mara#fabrikam.com"
}
}
],
"start": {
"dateTime": "2016-02-02T17:45:00.0000000",
"timeZone": "UTC"
},
"end": {
"dateTime": "2016-02-02T18:00:00.0000000",
"timeZone": "UTC"
},
"body": {
"content": "Status updates, blocking issues,and nextsteps.",
"contentType": "Text"
}
}
Additional help:
There's a helpful Graph Explorer Sandbox available for you to test requests in your browser

Resources