SSJS HTTP POST BASIC AUTH ISSUE - http

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"}

Related

My http request not working with mailgun api

header = ds_map_create();
header[? "Content-Type"] = "application/json";
header[? "api"] = "My api key that im censoring :)";
json = ds_map_create()
{
json[? "from"] = "mailgun#lapaihui.org";
json[? "to"] = "My email that im also censoring :]";
json[? "subject"] = "Dear user";
json[? "text"] = "This is your game talking";
}
http_request("https://api.mailgun.net/v3/lapaihui.org/messages","POST",header,json);
basically im trying to send an email using mailgun api but something is just not working
if any netcode gods can help i will greatly apreciete it and credit it!
Without any information on the error you getting or why it's not working, I can't be certain what you need, but I have two working solutions that will hopefully help.
If you're sending from the browser:
function sendMgEmail(pFrom, pTo, pSubject, pText, mgApiKey){
const formData = new FormData();
formData.append('from', pFrom);
formData.append('to', pTo);
formData.append('subject', pSubject);
formData.append('text', pText);
const qXhr = new XMLHttpRequest;
const qMethod = 'POST';
const qUrl = 'https://api.mailgun.net/v3/{{YOUR_DOMAIN}}/messages';
qXhr.open(qMethod, qUrl);
qXhr.setRequestHeader('Authorization', 'Basic ' + window.btoa('api:' + mgApiKey));
qXhr.send(formData);
qXhr.onload = function() {
if(qXhr.status == '200' || qXhr.status == '201') {
console.log('email queued', qXhr.status, qXhr.responseText);
} else {
console.log('ERROR ', qXhr.status, qXhr.responseText);
}
}
}
If from a Nodejs application, the XMLHttpRequest approach does not seem to work:
First, refer to https://www.npmjs.com/package/mailgun.js?utm_source=recordnotfound.com#messages
Then, install form-data and mailgun.js
npm i form-data
npm i mailgun.js
Lastly, the code...
const FormData = require('form-data');
const Mailgun = require('mailgun.js');
exports.sendMgEmail(pFrom, pTo, pSubject, pText, mgApiKey) {
const mailgun = new Mailgun(FormData);
const mg = mailgun.client({username: 'api', key: mgApiKey})
mg.messages.create('{{YOUR_DOMAIN}}', {
from: pFrom,
to: pTo,
subject: pSubject,
text: pText
})
.then(msg => console.log(msg))
.catch(err => console.error(err));
}

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

Any API to add an authorized domain to Firebase Auth?

Just want to check, is there any API to add the authorized domain in a programmatical way instead of adding it manually by going to Firebase console?
Also, is there any limit on how many domains can be added as the authorized domains?
JavaScript in Cloud Functions solution
import { google } from "googleapis";
(async () => {
/**
* ! START - Update Firebase allowed domains
*/
// Change this to whatever you want
const URL_TO_ADD = "engineering.acme-corp.net";
// Acquire an auth client, and bind it to all future calls
const auth = new google.auth.GoogleAuth({
scopes: ["https://www.googleapis.com/auth/cloud-platform"],
});
const authClient = await auth.getClient();
google.options({ auth: authClient });
// Get the Identity Toolkit API client
const idToolkit = google.identitytoolkit("v3").relyingparty;
/**
* When calling the methods from the Identity Toolkit API, we are
* overriding the default target URLs and payloads (that interact
* with the v3 endpoint) so we can talk to the v2 endpoint, which is
* what Firebase Console uses.
*/
// Generate the request URL
const projectId = await auth.getProjectId();
const idToolkitConfigUrl = `https://identitytoolkit.googleapis.com/admin/v2/projects/${projectId}/config`;
// Get current config so we can use it when we later update it
const currentConfig = await idToolkit.getProjectConfig(undefined, {
url: idToolkitConfigUrl,
method: "GET",
});
// Update the config based on the values that already exist
await idToolkit.setProjectConfig(undefined, {
url: idToolkitConfigUrl,
method: "PATCH",
params: { updateMask: "authorizedDomains" },
body: JSON.stringify({
authorizedDomains: [
...(currentConfig.data.authorizedDomains || []),
URL_TO_ADD,
],
}),
});
})();
A quick note on other languages
The principles should be the same:
Find a way to interact with Google's identify toolkit API (maybe Google offers an SDK to your language)
Get current config
Set new config
If you can't find an SDK, you can also work with raw http requests: https://cloud.google.com/identity-platform/docs/reference/rest/v2/projects/getConfig (it's just a bit trickier to do authentication when doing everything manually)
There is no API for this - you must do it through the console. You can also file a feature request with Firebase support if you want.
There doesn't appear to be any documentation stating limits of number of domains. Again, reach out to Firebase support if the documentation is unclear.
Thanks #Jean Costa
Totally working for me.
Here is C# implementation
using Google.Apis.Auth.OAuth2;
using Newtonsoft.Json;
var serviceAccountJsonFile = "path to service account json";
var projectId = "your project ids";
var authorizedDomains = new
{
authorizedDomains = new string[] {
"localhost",
"******.firebaseapp.com",
"*********.web.app",
"abc.def.com"
}
}; // your desire authorized domain
List<string> scopes = new()
{
"https://www.googleapis.com/auth/identitytoolkit",
"https://www.googleapis.com/auth/firebase",
"https://www.googleapis.com/auth/cloud-platform"
};
var url = "https://identitytoolkit.googleapis.com/admin/v2/projects/" + projectId + "/config";
using var stream = new FileStream(serviceAccountJsonFile, FileMode.Open, FileAccess.Read);
var accessToken = GoogleCredential
.FromStream(stream) // Loads key file
.CreateScoped(scopes) // Gathers scopes requested
.UnderlyingCredential // Gets the credentials
.GetAccessTokenForRequestAsync().Result; // Gets the Access Token
var body = JsonConvert.SerializeObject(authorizedDomains);
using (var client = new HttpClient())
{
var request = new HttpRequestMessage(HttpMethod.Patch, url) {
Content = new StringContent(body,System.Text.Encoding.UTF8)
};
request.Headers.Add("Accept", "application/json");
request.Headers.Add("Authorization", "Bearer " + accessToken);
try
{
var response = client.SendAsync(request).Result;
Console.WriteLine(response.Content.ReadAsStringAsync().Result);
}
catch (HttpRequestException ex)
{
// Failed
}
}
Thanks #Jean Costa and #Yan Naing
here is my php implemetation
use GuzzleHttp\Client as GuzzleClient;
use GuzzleHttp\Exception\TransferException;
use Google\Service\IdentityToolkit;
use Google\Service\IAMCredentials;
$KEY_FILE_LOCATION = storage_path('/app/credentials/service-account-1.json') ;
if (!file_exists($KEY_FILE_LOCATION)) {
throw new Exception(sprintf('file "%s" does not exist', $KEY_FILE_LOCATION));
}
$json= file_get_contents($KEY_FILE_LOCATION);
if (!$config = json_decode($json, true)) {
throw new Exception('invalid json for auth config');
}
$client = new \Google\Client();
$client->setAuthConfig($config );
$client->setScopes([ "https://www.googleapis.com/auth/identitytoolkit",
"https://www.googleapis.com/auth/firebase",
"https://www.googleapis.com/auth/cloud-platform"]);
$service = new IdentityToolkit($client);
// Get the Identity Toolkit API client
$idToolkit = $service->relyingparty;
//Get current config
$current_config= $idToolkit->getProjectConfig();
//Get service account access token
$access_token_req = new IAMCredentials\GenerateAccessTokenRequest();
$access_token_req->setScope( "https://www.googleapis.com/auth/firebase");
$credentials = new IAMCredentials($client);
$access_token = $credentials->projects_serviceAccounts->generateAccessToken("projects/-/serviceAccounts/{$config["client_email"]}" , $access_token_req )->getAccessToken();
// Generate the request URL (https://cloud.google.com/identity-platform/docs/reference/rest/v2/projects/updateConfig)
$idToolkitConfigUrl = "https://identitytoolkit.googleapis.com/admin/v2/projects/{$config["project_id"]}/config";
$authorized_domains = [ 'authorizedDomains' => array_merge( ['twomore.com'],$current_config->authorizedDomains)];
$client = new GuzzleClient( );
$response = null;
try {
$response = $client->request('PATCH', $idToolkitConfigUrl, [
'verify' => Helpers::isProduction() ? true : false ,
'http_errors'=> false, //off 4xx and 5xx exceptioins
'json' => $authorized_domains ,
'headers' => [
"Authorization" => "Bearer " . $access_token ,
"Accept" => "application/json",
]
]);
} catch (TransferException $e) {
throw new Exception( $e->getMessage());
}
$data = json_decode($response->getBody()->getContents(),true);
if($response->getStatusCode()!==200){
throw new Exception($response->getReasonPhrase() . ( isset($data['exception']['message']) ? " - " . $data['exception']['message'] : ""));
}
return response()->json(['data' => [
'authorized_domains' => $data['authorizedDomains']
]]);

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!

Resources