How to generate oauth_token from twitter request token API - node.js

I'm creating an app in which I need to post tweets in twitter on behalf of different users. So, I using Application User authentication. For me to redirect the user to twitter page to provide access I need oauth_token from twitter.
For that I am using Request_token_api.
But everytime am getting could not authenticate you and the twitter api page says authorization is not required for this API
I tried using many libraries like passport, Twit, OAuth, etc.. But nothing worked and I am pretty sure all my developer account consumer_key, consumer_secret, access_token, access_token_secret are correct.
function getAuthorization(httpMethod, baseUrl, reqParams) {
// Get acces keys
const consumerKey = keysJson.TWITTER_CONSUMER_KEY,
consumerSecret = keysJson.TWITTER_CONSUMER_SECRET,
accessToken = keysJson.TWITTER_ACCESS_TOKEN,
accessTokenSecret = keysJson.TWITTER_ACCESS_TOKEN_SECRET;
// timestamp as unix epoch
let timestamp = Math.round(Date.now() / 1000);
// nonce as base64 encoded unique random string
let nonce = crypto.randomBytes(32).toString('base64').replace(/[^0-9a-zA-Z]/g, '') ;
let cburl = percentEncode(keysJson.cb);
// generate signature from base string & signing key
let baseString = oAuthBaseString(httpMethod, baseUrl, reqParams, consumerKey, accessToken, timestamp, nonce);
console.log(baseString);
let signingKey = oAuthSigningKey(consumerSecret, accessTokenSecret);
// console.log(signingKey);
let signature = oAuthSignature(baseString, signingKey);
console.log(signature);
// return interpolated string
return 'OAuth ' +
'oauth_nonce="' + nonce + '", ' +
'oauth_callback="' + cburl + '", ' +
'oauth_signature_method="HMAC-SHA1", ' +
'oauth_timestamp="' + timestamp + '", ' +
'oauth_consumer_key="' + consumerKey + '", ' +
'oauth_signature="' + signature + '", ' +
'oauth_version="1.0"' ;
}
function oAuthBaseString(method, url, params, key, token, timestamp, nonce) {
return method
+ '&' + percentEncode(url)
+ '&' + percentEncode(genSortedParamStr(params, key, token, timestamp, nonce));
};
function oAuthSigningKey(consumer_secret, token_secret) {
return consumer_secret + '&' + token_secret;
};
function oAuthSignature(base_string, signing_key) {
var signature = hmac_sha1(base_string, signing_key);
return percentEncode(signature);
};
function percentEncode(str) {
return encodeURIComponent(str).replace(/[!*()']/g, (character) => {
return '%' + character.charCodeAt(0).toString(16);
});
};
function hmac_sha1(string, secret) {
let shaObj = new jsSHA("SHA-1", "TEXT");
shaObj.setHMACKey(secret, "TEXT");
shaObj.update(string);
let hmac = shaObj.getHMAC("B64");
return hmac;
};
function mergeObjs(obj1, obj2) {
for (var attr in obj2) {
obj1[attr] = obj2[attr];
}
return obj1;
};
function genSortedParamStr(params, key, token, timestamp, nonce) {
// Merge oauth params & request params to single object
let paramObj = mergeObjs(
{
include_entities: 'true',
oauth_consumer_key : key,
oauth_nonce : nonce,
oauth_signature_method : 'HMAC-SHA1',
oauth_timestamp : timestamp,
// oauth_token : token,
oauth_version : '1.0'
},
);
// Sort alphabetically
let paramObjKeys = Object.keys(paramObj);
let len = paramObjKeys.length;
paramObjKeys.sort();
// Interpolate to string with format as key1=val1&key2=val2&...
let paramStr = paramObjKeys[0] + '=' + paramObj[paramObjKeys[0]];
for (var i = 1; i < len; i++) {
paramStr += '&' + paramObjKeys[i] + '=' + percentEncode(decodeURIComponent(paramObj[paramObjKeys[i]]));
}
return paramStr;
};
var redirect_uri = encodeURIComponent('http://localhost:4200/admin/marketing/loginsuccess');
var auth = getAuthorization('POST','https://api.twitter.com/oauth/request_token',{oauth_callback:redirect_uri});
console.log(auth);
var options = {
"method": "POST",
"hostname": "api.twitter.com",
"port": null,
"path": '/oauth/request_token',
"headers": {
'Authorization' : auth
}
};
try {
console.log(options);
var req = https.request(options, function (res1) {
var chunks = [];
console.log('statusCode:', res1.statusCode);
console.log('headers:', res1.headers);
res1.on("data", function (chunk) {
chunks.push(chunk);
});
res1.on("end", function () {
var body = Buffer.concat(chunks);
var disp = JSON.parse(body.toString());
console.log(disp);
});
});
req.end();
} catch (error) {
console.log(error);
res.json({success:false,message:error});
}
I need the oauth access token from Twitter API but am getting.
{ errors: [ { code: 32, message: 'Could not authenticate you.' } ] }
I am struggling for 3 days. Kindly let me know is there any alternate way to implement twitter API in nodejs or some sample code in github.
Thanks in advance..

Related

How to authorize with AWS signature 4 -> API Gateway -> Lambda

I've googled around a lot with no luck in finding the solution to my problem. I've read through the entire authentication process for AWS Signature 4 and followed their tutorial as well as view other sources. I'm trying to have client side authentication for a desktop application that makes request to API Gateway.
When I use Postman it works properly but I tried generating my own signature in Nodejs but to no avail, I keep getting 403 messages back from the call.
The function below returns the authenticated requestUrl which is then run by axios.get(requestUrl). When I use the Postman generated request it works perfectly fine but, once I use my generated request I have problems.
Am I missing something while authenticating? Here is what my code currently looks like:
function Authorize() {
const host = "EXAMPLE.execute-api.us-east-1.amazonaws.com"
const reg = 'us-east-1'
const meth = 'GET'
const serv = 'execute-api'
const endpoint = '/development/putImage'
// Keys
let access = "EXAMPLE"
let key = "KEY"
// Get Date
let t = new Date();
let amzDate = t.toJSON().replace(/[-:]/g, "").replace(/\.[0-9]*/, "");
let dateStamp = t.toJSON().replace(/-/g, "").replace(/T.*/, "");
// ************* TASK 1: CREATE CANONICAL REQUEST *************
// Create Canonical Request
let canonical_uri=endpoint
let canonical_headers="host: "+host+"\n"
let signedHeaders = 'host'
let algorithm = 'AWS4-HMAC-SHA256'
let credentialScope = dateStamp + "/" + reg + "/" + serv + "/" + "aws4_request"
// Set query string
let canonicalQueryString = ""
canonicalQueryString += "X-Amz-Date=" + amzDate
canonicalQueryString += "&X-Amz-Algorithm=" + algorithm;
canonicalQueryString += "&X-Amz-Credential=" + encodeURIComponent(access + "/" + credentialScope)
canonicalQueryString += "&X-Amz-SignedHeaders=" + signedHeaders
// Empty payload for get request
var payloadHash = crypto.createHash('sha256').update('').digest('hex');
// Set canonical request
var canonicalRequest = meth + "\n" + canonical_uri + "\n" + canonicalQueryString + "\n" + canonical_headers + "\n" + signedHeaders + "\n" + payloadHash
console.log(canonicalRequest)
// ************* TASK 2: CREATE THE STRING TO SIGN*************
let stringToSign = algorithm + '\n' + amzDate + '\n' + credentialScope + '\n' + crypto.createHash('sha256').update(canonicalRequest).digest('hex');
// ************* TASK 3: CALCULATE THE SIGNATURE *************
var signingKey = getSignatureKey(key, dateStamp, reg, serv)
var signature = crypto.createHmac('sha256', signingKey).update(stringToSign).digest('hex');
// ************* TASK 4: ADD SIGNING INFORMATION TO THE REQUEST *************
canonicalQueryString += '&X-Amz-Signature=' + signature
let requestUrl = "https://"+host+ endpoint + "?" + canonicalQueryString
console.log(requestUrl)
return requestUrl
}
The below code worked for me well. For more info, please visit https://docs.aws.amazon.com/opensearch-service/latest/developerguide/request-signing.html#request-signing-node
const { HttpRequest} = require("#aws-sdk/protocol-http");
const { defaultProvider } = require("#aws-sdk/credential-provider-node");
const { SignatureV4 } = require("#aws-sdk/signature-v4");
const { NodeHttpHandler } = require("#aws-sdk/node-http-handler");
const { Sha256 } = require("#aws-crypto/sha256-browser");
...
var request = new HttpRequest({
body: JSON.stringify({"users":["G0000000B","G0000000A"]}),
headers: {
'Content-Type': 'application/json',
'apiKey':'XXXXXXXXXXXX',
'apiSecret': 'XXXXXXXXXXXXXXXXXX',
'host': 'service2.xxx.xxx.xx'
},
hostname: 'service2.xxx.xxx.xx',
method: 'POST',
path: 'API/user/list'
});
var signer = new SignatureV4({
credentials: defaultProvider(),
region: 'ap-southeast-1',
service: 'execute-api',
sha256: Sha256
});
const signedRequest = await signer.sign(request);
// Send the request
var client = new NodeHttpHandler();
var { response } = await client.handle(signedRequest)
console.log(response.statusCode + ' ' + response.body.statusMessage);
var responseBody = '';
await new Promise(() => {
response.body.on('data', (chunk) => {
responseBody += chunk;
});
response.body.on('end', () => {
console.log('Response body: ' + responseBody);
});
}).catch((error) => {
console.log('Error: ' + error);
});

Here Map API request token with NodeJS

I am trying to request a token to the Here Map API with NodeJS in order to obtain OAuth 2.0 Token Credentials.
I am blocked a the request level and constantly having the same error but according to the documentation I don't do anything wrong.
Here is the necessary code in NodeJS to make the request :
const keyID = "MyKeyID";
const keySecret = "MySecretKey";
const httpMethod = "POST";
const httpUrl = "https://account.api.here.com/oauth2/token";
let oauthNonce = Math.random().toString(36).substring(6);
let oauthTimestamp = Date.now();
let oauthSignatureMethod = "HMAC-SHA256";
let oauthVersion = "1.0";
let baseString = httpMethod + "&" + httpUrl;
let oauth1Param = [];
oauth1Param.oauth_consumer_key = keyID;
oauth1Param.oauth_signature_method = oauthSignatureMethod;
oauth1Param.oauth_signature = "";
oauth1Param.oauth_timestamp = oauthTimestamp;
oauth1Param.oauth_nonce = oauthNonce;
oauth1Param.oauth_version = oauthVersion;
let paramString = "grant_type=client_credentials"
+ "&oauth_consumer_key=" + oauth1Param.oauth_consumer_key
+ "&oauth_nonce=" + oauth1Param.oauth_nonce
+ '&oauth_signature_method=' + oauth1Param.oauth_signature_method
+ "&oauth_timestamp=" + oauth1Param.oauth_timestamp
+ "&oauth_version=" + oauth1Param.oauth_version;
baseString += "&" + paramString;
let signingKey = keySecret + "&";
let signature = crypto.createHmac('SHA256', signingKey).update(baseString).digest('base64');
oauth1Param.oauth_signature = signature;
console.log(JSON.stringify(paramString));
console.log(oauth1Param);
let headerOauth = "OAuth ";
for (let key in oauth1Param) {
headerOauth += key + "=" + oauth1Param[key] + ",";
}
headerOauth = headerOauth.slice(0, -1);
console.log(headerOauth);
var request = require('request');
request.post({
url : httpUrl,
headers: {
'content-type' : 'application/x-www-form-urlencoded',
'Authorization' : headerOauth
},
body : JSON.stringify({
"grant_type" : "client_credentials"
})
}, function(error, response, body){
if(error){
console.log("------ ERROR -------");
throw error;
}
//console.log(response);
console.log(body);
});
And this is the error that I keep getting :
{"errorId":"ERROR-51fa3a57-1dfa-4da7-b2e9-5f94574a4f75",
"httpStatus":401,
"errorCode":401205,
"message":"Unsupported signature method in the header. Require HMAC-SHA256",
"error":"invalid_request",
"error_description":"errorCode: '401205'. Unsupported signature method in the header. Require HMAC-SHA256"}
According to the documentation, https://developer.here.com/documentation/authentication/dev_guide/topics/error-messages.html, I have an error with the signature method and that it's required to be HMAC-SHA256 but actually this is what I have put at the beginning of my code : let oauthSignatureMethod = "HMAC-SHA256";
Does anyone know how to fix this issue?
The library you're using, request, supports OAuth signing. Therefore the code can be significantly shorter:
const request = require('request');
const OAUTH_URL = 'https://account.api.here.com/oauth2/token';
const KEY_ID = '<KEY_ID>';
const KEY_SECRET = '<KEY_SECRET>';
request.post({
url: OAUTH_URL,
oauth: {
consumer_key: KEY_ID,
consumer_secret: KEY_SECRET,
signature_method: 'HMAC-SHA256'
},
headers: {
'Content-Type' : 'application/x-www-form-urlencoded',
},
form: {
'grant_type': 'client_credentials'
}
}, function (e, r, body) {
console.log(body);
});
As for your code snippet, JSON.stringify(obj) is not appropriate as it will encode the curly bracket of the object as well.
Additionally, the values in the OAuth header need to be surrounded by double quotes. It's easy to get little things wrong with OAuth :)

How to scheduling push notifications using azure sdk for node

I know it is possible in .net, i can see the reference over here https://learn.microsoft.com/en-us/azure/notification-hubs/notification-hubs-send-push-notifications-scheduled. But I want to know how to do that in node. Can any one guide me on this.
You can send a scheduled notification in Node using the REST API. Use the specification for sending a normal notification and replace /messages with /schedulednotifications. You will also need to add a header specifying the datetime named ServiceBusNotification-ScheduleTime.
For an example using the template schema:
var CryptoJS = require("crypto-js");
var axios = require("axios");
var getSelfSignedToken = function(targetUri, sharedKey, keyName,
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 base64signature = signature.toString(CryptoJS.enc.Base64);
var base64UriEncoded = encodeURIComponent(base64signature);
// construct autorization string
var token = "SharedAccessSignature sr=" + targetUri + "&sig="
+ base64UriEncoded + "&se=" + expires + "&skn=" + keyName;
// console.log("signature:" + token);
return token;
};
var keyName = "<mykeyName>";
var sharedKey = "<myKey>";
var uri = "https://<mybus>.servicebus.windows.net/<myhub>";
var expiration = 10;
var token = getSelfSignedToken(uri, sharedKey, keyName, expiration);
const instance = axios.create({
baseURL: uri,
timeout: 100000,
headers: {
'Content-Type': 'application/octet-stream',
'X-WNS-Type': 'wns/raw',
'ServiceBusNotification-Format' : 'template',
'ServiceBusNotification-ScheduleTime': '2019-07-19T17:13',
'authorization': token}
});
var payload = {
"alert" : " This is my test notification!"
};
instance.post('/schedulednotifications?api-version=2016-07', payload)
.then(function (response) {
console.log(response);
}).catch(function (error) {
// handle error
console.log(error);
});

How to access the variable data outside of a Node.js function?

This is a function in Node.js, which reads data from Analytics:
function getDataFromGA(Dimension, Metric, StartDate, EndDate, MaxResults) {
var fs = require('fs'),
crypto = require('crypto'),
request = require('request'); // This is an external module
var authHeader = {
'alg': 'RS256',
'typ': 'JWT'
},
authClaimSet = {
'iss': '***t#developer.gserviceaccount.com', // Service account email
'scope': 'https://www.googleapis.com/auth/analytics.readonly',
// We MUST tell them we just want to read data
'aud': 'https://accounts.google.com/o/oauth2/token'
},
SIGNATURE_ALGORITHM = '**',
SIGNATURE_ENCODE_METHOD = '**',
GA_KEY_PATH = '**',
//finds current directory then appends private key to the directory
gaKey;
function urlEscape(source) {
return source.replace(/\+/g, '-').replace(/\//g, '_').replace(/\=+$/, '');
}
function base64Encode(obj) {
var encoded = new Buffer(JSON.stringify(obj), 'utf8').toString('base64');
return urlEscape(encoded);
}
function readPrivateKey() {
if (!gaKey) {
gaKey = fs.readFileSync(GA_KEY_PATH, 'utf8');
}
return gaKey;
}
var authorize = function (callback) {
var self = this,
now = parseInt(Date.now() / 1000, 10), // Google wants us to use seconds
cipher,
signatureInput,
signatureKey = readPrivateKey(),
signature,
jwt;
// Setup time values
authClaimSet.iat = now;
authClaimSet.exp = now + 60; // Token valid for one minute
// Setup JWT source
signatureInput = base64Encode(authHeader) + '.' + base64Encode(authClaimSet);
// Generate JWT
cipher = crypto.createSign('RSA-SHA256');
cipher.update(signatureInput);
signature = cipher.sign(signatureKey, 'base64');
jwt = signatureInput + '.' + urlEscape(signature);
// Send request to authorize this application
request({
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
uri: 'https://accounts.google.com/o/oauth2/token',
body: 'grant_type=' + escape('urn:ietf:params:oauth:grant-type:jwt-bearer') +
'&assertion=' + jwt
}, function (error, response, body) {
if (error) {
console.log(error);
callback(new Error(error));
} else {
var gaResult = JSON.parse(body);
if (gaResult.error) {
callback(new Error(gaResult.error));
} else {
callback(null, gaResult.access_token);
// console.log(gaResult);
console.log("Authorized");
}
}
});
};
var request = require('request'),
qs = require('querystring');
authorize(function (err, token) {
if (!err) {
// Query the number of total visits for a month
var requestConfig = {
'ids': 'ga:72333024',
'dimensions': Dimension,
'metrics': Metric,
// 'sort': '-ga:users',
'start-date': StartDate,
'end-date': EndDate,
'max-results': MaxResults
};
request({
method: 'GET',
headers: {
'Authorization': 'Bearer ' + token // Here is where we use the auth token
},
uri: 'https://www.googleapis.com/analytics/v3/data/ga?' + qs.stringify(requestConfig)
}, function (error, resp, body) {
console.log(body);
var data = JSON.parse(body);
console.log(data.totalsForAllResults);
console.log(data.rows);
});
}
});
}
Here I try to access it from outside:
var gaJSON = utils.getDataFromGA("ga:country", "ga:pageviews", "2011-08-04", "2014-09-12", "50");
res.send(gaJSON);
My question is how I can access the variable data in the end of the first method? How can I call it from outside of the function?
You can assign data to a variable declared in the first function. But since the authorize method is asynchronous the variable data will still be undefined at the end of the first function. The best way to do this is handle with callbacks.
I think you wanna return something related to this variable, right? Try to put a callback parameter to the first function and then call this function passing the result.
callback(variable)
Why do you want to access if from outside. ??
Even though you want to desperately then you need to create a function pass the "data" as argument and then invoke the function .
console.log(body);
var data = JSON.parse(body);
myNewFunction(data);
Write all ur logic inside "myNewFunction" that uses data .

How to set Azure Blob Storage CORS properties using Node?

I have been following the blob service and authentication documentation in order to set the CORS properties on my azure blob storage account from my mobile service.
I can't figure out what I am doing wrong.
The server response is:
The MAC signature found in the HTTP request 'JI...Tk=' is not the same as any computed signature. Server used following string to sign: 'PUT
x-ms-date:Wed, 19 Feb 2014 07:24:06 GMT x-ms-version:2013-08-15
/apporotest/?comp=properties'
When I log the string to sign (not passing contentMD5 and content type) on my end, it turns out to be the same string. So I guess my function to build the shared key is wrong.
This should build : Base64(HMAC-SHA256(UTF8(StringToSign))):
function buildSharedKeyLite( verb, contentMD5, contentType, canonicalizedHeaders, canonicalizedResource ) {
var stringToSign = verb + "\n" +
contentMD5 + "\n" +
contentType + "\n" +
"" + "\n" + // date is to be empty because we use x-ms-date
canonicalizedHeaders +
canonicalizedResource;
return crypto.createHmac('sha256', self.accountKey).update(encodeURIComponent(stringToSign)).digest('base64');
}
What confuses me though is that the formerly mentioned documentation for the Shared Key Lite requires MD5 of the content as well as the content type to be set. However, the server response with the string to sign does not seem to expect these.
If the creation of the shared key lite is correct, then I assume I am not handling the creation of the MD-5 content correctly or the canonicalized headers:
function setCors( cors ) {
var url = MY_ACCOUNT_UTL + '/?restype=service&comp=properties';
var canonicalizedResource = '/' + MY_ACCOUNT_NAME + '/?comp=properties';
var corsMD5 = crypto.createHash('md5' ).update(MY_CORS_XML).digest('base64');
var date = (new Date()).toUTCString();
var headers = {
'x-ms-version': '2013-08-15',
'x-ms-date': date,
'Host': MY_ACCOUNT_HOST
};
var canonicalizedHeaders = buildCanonicalizedHeaders( headers );
// THIS
var key = buildSharedKeyLite( 'PUT', corsMD5, 'text/plain; charset=UTF-8', canonicalizedHeaders, canonicalizedResource );
// AND THIS, BOTH YIELD THE SAME SERVER RESPONSE
var key = buildSharedKeyLite( 'PUT', "", "", canonicalizedHeaders, canonicalizedResource );
headers['Authorization'] = 'SharedKeyLite ' + MY_ACCOUNT_NAME + ':' + key;
var options = {
url: url,
headers: headers
};
function onPropertiesSet(error, response, body) {
if (!error && response.statusCode == 202) {
console.log("CORS: OK");
}
else {
console.log("CORS: "+ response.statusCode);
console.log(body);
}
}
request.put(options, onPropertiesSet); // require('request')
}
function buildCanonicalizedHeaders( headers ) {
var xmsHeaders = [];
var canHeaders = "";
for ( var name in headers ) {
if ( name.indexOf('x-ms-') == 0 ) )
xmsHeaders.push( name );
}
}
xmsHeaders.sort();
for ( var i = 0; i < xmsHeaders.length; i++ ) {
name = xmsHeaders[i];
canHeaders = canHeaders + name.toLowerCase().trim() + ':' + headers[name] + '\n';
}
return canHeaders;
}
I am really thankful for any pointers.
I'm not 100% sure but I believe encodeURIComponent is creating problem for you. For example, look at the code below:
var a = "PUT\n\n\nFeb 2014 09:08:18 GMT\nx-ms-version:2013-08-15\n/cynapta/?comp=properties";
var b = encodeURIComponent(a);
console.log(a);
console.log("\n");
console.log(b);
and this is how a and b are displayed on my console:
Can you try by removing encodeURIComponent and just pass stringToSign directly for signature calculation?
Update
Looking at the source code here for signing the request and assuming you're passing storage account key as string, can you try the following in your buildSharedKeyLite function:
return crypto.createHmac('sha256', new Buffer(self.accountKey, 'base64')).update(stringToSign).digest('base64');

Resources