I'm currently trying to connect to the CEX.IO bitcoin exchange's websocket. Websocket connection is OK but at the time of authentication, I have the error: Timestamp is not in 20sec range. I don't know what this error.
Test case 1 & 2 for createSignature is OK (https://cex.io/websocket-api#authentication).
Code for calculating the signature and request params
const WebSocket = require('ws');
const cexioWs = new WebSocket(
'wss://ws.cex.io/ws/',
{
perMessageDeflate: false
}
);
function createAuthRequest(apiKey, apiSecret) {
let curTime = Math.floor(Date.now() / 1000);
let hmac = crypto.createHmac('sha256', apiSecret);
hmac.update(curTime.toString());
hmac.update(apiKey);
let args =
{
e: "auth",
auth: {
key: apiKey,
signature: hmac.digest('hex'), //createSignature(curTime, apiKey, apiSecret),
timestamp: curTime
}
};
let authMessage = JSON.stringify(args);
console.log(args);
return authMessage;
}
cexioWs.on('message', (mess, error) => {
//console.log("connected");
console.log("cexio message");
console.log(mess);
let JSONMess = JSON.parse(mess);
if (JSONMess.e === "connected") {
cexioWs.send(createAuthRequest(key, secret));
cexioWs.send(JSON.stringify({
e: "subscribe",
roomss: [
"tickers"
]
}));
}
if (JSONMess.e === "ping") {
console.log("pong message");
cexioWs.send(JSON.stringify({e: "pong"}));
}
});
Here is working code:
const crypto = require('crypto')
const WebSocket = require('ws')
var apiKey = ''
var apiSecret = ''
const cexioWs = new WebSocket('wss://ws.cex.io/ws/', {perMessageDeflate: false });
function createSignature(timestamp, apiKey, apiSecret){
var hmac = crypto.createHmac('sha256', apiSecret );
hmac.update( timestamp + apiKey );
return hmac.digest('hex');
}
function createAuthRequest(apiKey, apiSecret ){
var timestamp = Math.floor(Date.now() / 1000);
var args = { e: 'auth', auth: { key: apiKey,
signature: createSignature(timestamp, apiKey, apiSecret), timestamp: timestamp } };
var authMessage = JSON.stringify( args );
return authMessage;
}
cexioWs.on('message', (mess, error) => {
console.log("cexio message");
console.log(mess);
let JSONMess = JSON.parse(mess);
if (JSONMess.e === "connected") {
cexioWs.send(createAuthRequest(apiKey, apiSecret));
cexioWs.send(JSON.stringify({
e: "subscribe",
rooms: [
"tickers"
]
}));
}
if (JSONMess.e === "ping") {
console.log("pong message");
cexioWs.send(JSON.stringify({e: "pong"}));
}
});
Don't know if this helps but I had the same problem for two days, checked everything then I checked and the code looked absolutely fine. Later on I checked what the actual time I was getting and compared it to the Internet time. My computer's time was 4 minutes ahead of Internet time and my settings were off for 'update time from Internet'.
After sync'ing my computer's time with Internet I ran the script and it worked perfectly.
Moral of the story, make sure your PC's time and the Internet's time are the same.
Goodluck!
Related
I am new to GTM+GA.I am trying to display google Analytics(GA4) reports on my webpage. I created Oauth Client Id in google cloud console and also done other settings in Google cloud console. Through javascript code i am trying to get access token from google Api and I am getting below exception.
After successful authentication I will integrate GA repots with my web page. Below is my javascript code for getting access token.
function main(propertyId = 'YOUR-GA4-PROPERTY-ID') {
propertyId = '347415282';
const {
OAuth2Client
} = require('google-auth-library');
const {
grpc
} = require('google-gax');
const http = require('http');
const url = require('url');
const open = require('open');
const destroyer = require('server-destroy');
const keys = require('./oauth2.keys.json');
const SCOPES = ['https://www.googleapis.com/auth/analytics.readonly'];
function getAnalyticsDataClient(authClient) {
const sslCreds = grpc.credentials.createSsl();
const credentials = grpc.credentials.combineChannelCredentials(
sslCreds,
grpc.credentials.createFromGoogleCredential(authClient));
return new BetaAnalyticsDataClient({
sslCreds: credentials,
});
}
function getOAuth2Client() {
return new Promise((resolve, reject) => {
const oAuth2Client = new OAuth2Client(
keys.web.client_id,
keys.web.client_secret,
'http://localhost:3000/oauth2callback');
const authorizeUrl = oAuth2Client.generateAuthUrl({
access_type: 'offline',
scope: SCOPES.join(' '),
});
const server = http
.createServer(async(req, res) => {
try {
if (req.url.indexOf('/oauth2callback') > -1) {
const qs = new url.URL(req.url, 'http://localhost:3000')
.searchParams;
const code = qs.get('code');
console.log(`Code is ${code}`);
res.end(
'Authentication successful! Please return to the console.');
server.destroy();
const r = await oAuth2Client.getToken(code);
oAuth2Client.setCredentials(r.tokens);
console.info('Tokens acquired.');
resolve(oAuth2Client);
}
} catch (e) {
reject(e);
}
})
.listen(3000, () => {
console.info(`Opening the browser with URL: ${authorizeUrl}`);
open(authorizeUrl, {
wait: false
}).then(cp => cp.unref());
});
destroyer(server);
});
}
async function runReport() {
const oAuth2Client = await getOAuth2Client();
const analyticsDataClient = getAnalyticsDataClient(oAuth2Client);
const[response] = await analyticsDataClient.runReport({
property: `properties/${propertyId}`,
dateRanges: [{
startDate: '2020-03-31',
endDate: 'today',
},
],
dimensions: [{
name: 'city',
},
],
metrics: [{
name: 'activeUsers',
},
],
});
console.log('Report result:');
response.rows.forEach(row => {
console.log(row.dimensionValues[0], row.metricValues[0]);
});
}
runReport();
process.on('unhandledRejection', err => {
console.error(err.message);
process.exitCode = 1;
});
main(...process.argv.slice(2));
Please let me know how to get rid off this issue.
Regards,
Prabhash
I have set token session expiration time 15 minute on the AAD,
overviews
Login into the web application and getting a token with an expiration time of 15 minutes and storing it in local sessions storage.
After login calling the timmer method which is running every second. in this method getting token expiration time and set logic to call the acquireTokenSilent method and refresh token silently.
version
MSAL : 0.2.4,
Angular : 6
code
setTimer() {
this.interval = setInterval(() => {
const token = sessionStorage.getItem('msal.idtoken');
if (token) {
const base64Url = token.split('.')[1];
const base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/');
const jsonPayload = decodeURIComponent(
atob(base64)
.split('')
.map(function (c) {
return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2);
})
.join('')
);
const sessionPayload = JSON.parse(jsonPayload);
const date = new Date(0);
const sessionTimeOut = date.setUTCSeconds(sessionPayload.exp - 2 * 60);
const currentTime = new Date();
if (currentTime.valueOf() > sessionTimeOut.valueOf()) {
this.stopInterval();
this.getToken();
}
}
}, 1000);
}
getToken() {
return this.app.acquireTokenSilent(this.applicationConfig.b2cScopes).then(
(accessToken) => {
this.accessToken = accessToken;
this.saveAccessTokenToCache(accessToken);
return accessToken;
},
(error) => {
return this.app
.acquireTokenPopup(this.applicationConfig.b2cScopes)
.then(
(accessToken) => {
this.accessToken = accessToken;
this.saveAccessTokenToCache(accessToken);
return accessToken;
},
(err) => {
console.error('error', error);
}
);
}
);
}
stopInterval() {
clearInterval(this.interval);
}
saveAccessTokenToCache(accessToken: string): void {
sessionStorage.setItem('msal.idtoken', accessToken);
this.setTimer();
}
Issue
after login in the 13-minute token is refresh but the second time refresh token call is not happening.
requirement
Need refresh token every 2 minutes before the token expiration time.
Please Help me, friends.
Thanks!
I'm setting up logging in an app with winston and occassionally when I run tests a separate file is created with the date 12/31/1969. Is there something explicit I need to put in the creation of the transport so that it knows what the current date is?
What's very interesting is this seems to be a system wide anomaly as the _log method, which doesn't use the new Date() syntax, but the moment.js library also results in a 12-31-1969 inside the log file:
My logger:
class Logger{
constructor(configs){
if (!configs) configs = {};
this.logDirectory = configs.directory ? path.join(__dirname, configs.directory) : path.join(__dirname, '../logs') ;
this.initialize();
this.date = moment().format("YYYY-MM-DD HH:mm:ss");
}
initialize() {
this._createTransportObj();
this._createLoggerObj();
}
_createTransportObj() {
const DailyRotateFile = winston.transports.DailyRotateFile;
this._transport = new DailyRotateFile({
filename: path.join(this.logDirectory, '/log-%DATE%.log'),
datePattern: 'YYYY-MM-DD',
level: 'info'
});
}
_createLoggerObj() {
this._logger = winston.createLogger({
level: 'info',
format: winston.format.json(),
transports: [this._transport],
exitOnError: true
});
if (nodeEnv !== 'production') {
this._logger.add(new winston.transports.Console({
format: winston.format.simple()
}));
}
}
_log(type, msg, options) {
const logMsg = {};
const timestamp = moment().format("YYYY-MM-DD HH:mm:ss");
logMsg.level = type || 'info';
logMsg.time = timestamp;
logMsg.msg = msg || '';
logMsg.desc = options.description || '';
// get the user that made the request if available
if (options.user) logMsg.user = options.user;
// get the url endpoint that was hit if available
if (options.url) logMsg.url = options.url;
// if an error is sent through, get the stack
// and remove it from msg for readability
if (msg.stack) {
logMsg.stack = msg.stack;
msg = msg.message ? msg.message : msg;
}
// get the ip address of the caller if available
if (options.ip) logMsg.ip = options.ip;
// get the body of the request if available
if (options.body) logMsg.body = options.body;
// get the query string of the request if available
if (options.query) logMsg.query = options.query;
// get the params string of the request if available
if (options.params) logMsg.params = options.params;
const jsonString = JSON.stringify(logMsg);
this._logger.log(type, logMsg);
}
info(msg, options) {
return this._log('info', msg, options);
}
error(msg, options) {
return this._log('error', msg, options);
}
warn(msg, options) {
return this._log('warn', msg, options);
}
verbose(msg, options) {
return this._log('verbose', msg, options);
}
debug(msg, options) {
return this._log('debug', msg, options);
}
silly(msg, options) {
return this._log('silly', msg, options);
}
}
module.exports = { Logger };
I'm currently only testing it in a promise handler that my routes flow through:
const asyncHandler = fn => (req, res, next) => {
const logger = new Logger();
Promise.resolve(fn(req, res, next))
.then(result => {
if (req.body.password) delete req.body.password;
logger.info(result,
{ user: req.user.username,
url: req.originalUrl,
body: req.body,
description: '200:OK Response sent back successfully'
});
return res.status(200).json({ result })
})
.catch(e => {
console.log(e);
return res.status(400).json({ error: e.message })
});
};
module.exports = asyncHandler;
UPDATE*
ok, so it seems to not be the logger itself. I ran a batch of tests and noticed it's always the same route that triggers the date change. What's weird is I can't seem to figure out what's happening.
The route is:
and the app.use() statement is as follows:
finally the admin_access middleware is simple enought:
I've figured out if I break the endpoint in the app.js file before it hits admin_access the date is correct. However if I break in admin_access the date is 12-31-1969. So what could be happening between the two? Is there something I could be setting unintentionally on this route?
Figured it out. Turns out the package sinon-test was changing the system time.
I had my test setup like this:
const sinon = require('sinon');
const sinonTest = require('sinon-test');
const test = sinonTest(sinon);
describe('Test suite for route: /video/admin/create', ()=>{
let agent;
beforeEach(async function() {
// create temporary admin
admin.permissionId = 1;
await new NewUser().createUser(admin);
//login as admin
agent = chai.request.agent(server);
await agent
.post('/auth')
.send({ username: admin.username, password: admin.password });
});
afterEach(async function() {
// destroy temp admin
await new UserManager(admin).hardRemove();
});
it('should fail to create a video for not including video info', test(async function(){
const newVideo = { title: 'Test Video' };
const response = await agent.post('/video/admin/create')
.send(newVideo);
expect(response.status).to.equal(400);
}));
it('should create a video', test(async function(){
const newVideo = {
title: 'Test Video',
description: 'This is a description',
embed: 'https://youtu.be/SKbHjjZXdmc',
source: 'youtube',
type: 'lecture',
category: 'physics'
};
const response = await agent.post('/video/admin/create')
.send(newVideo);
// validations
expect(response.status).to.equal(200);
const { result } = response.body;
expect(result.title).to.equal(newVideo.title);
expect(result.description).to.equal(newVideo.description);
expect(result.embed).to.equal(newVideo.embed);
}));
});
When I unwrapped the test from the test() function everything worked correctly.
I there,
I have setup a SSE connection between a react-native app and a NodeJS server.
I followed some guidelines to set it up in client side, polyfilling EventSource and all, this is pretty straight forward.
But on the server side, I have some issue finding out how to store the connection with the client. I chose store the Response in a global object, but I have the feeling this is not the proper way to do. Can somebody advise ?
Here is my code below
const SSE_RESPONSE_HEADER = {
'Connection': 'keep-alive',
'Content-Type': 'text/event-stream',
'Cache-Control': 'no-cache',
'X-Accel-Buffering': 'no'
};
const getUserId = (req, from) => {
try {
// console.log(from, req.body, req.params)
if (!req) return null;
if (Boolean(req.body) && req.body.userId) return req.body.userId;
if (Boolean(req.params) && req.params.userId) return req.params.userId;
return null
} catch (e) {
console.log('getUserId error', e)
return null;
}
}
global.usersStreams = {}
exports.setupStream = (req, res, next) => {
let userId = getUserId(req);
if (!userId) {
next({ message: 'stream.no-user' })
return;
}
// Stores this connection
global.usersStreams[userId] = {
res,
lastInteraction: null,
}
// Writes response header.
res.writeHead(200, SSE_RESPONSE_HEADER);
// Note: Heatbeat for avoidance of client's request timeout of first time (30 sec)
const heartbeat = {type: 'heartbeat'}
res.write(`data: ${JSON.stringify(heartbeat)}\n\n`);
global.usersStreams[userId].lastInteraction = Date.now()
// Interval loop
const maxInterval = 55000;
const interval = 3000;
let intervalId = setInterval(function() {
if (!global.usersStreams[userId]) return;
if (Date.now() - global.usersStreams[userId].lastInteraction < maxInterval) return;
res.write(`data: ${JSON.stringify(heartbeat)}\n\n`);
global.usersStreams[userId].lastInteraction = Date.now()
}, interval);
req.on("close", function() {
let userId = getUserId(req, 'setupStream on close');
// Breaks the interval loop on client disconnected
clearInterval(intervalId);
// Remove from connections
delete global.usersStreams[userId];
});
req.on("end", function() {
let userId = getUserId(req, 'setupStream on end');
clearInterval(intervalId);
delete global.usersStreams[userId];
});
};
exports.sendStream = async (userId, data) => {
if (!userId) return;
if (!global.usersStreams[userId]) return;
if (!data) return;
const { res } = global.usersStreams[userId];
res.write(`data: ${JSON.stringify({ type: 'event', data })}\n\n`);
global.usersStreams[userId].lastInteraction = Date.now();
};
My first tip is to simply get rid of global; it's ok to have a variable in your module's closure. Your module can encapsulate this "global" state without making it globally accessible to all other modules.
const usersStreams = {};
Second, it's probably not impossible for the same user to have multiple connections established. I'd recommend that if you're keying these connections on userId, you ought to have the values in userStreams for these keys as collections so you can write to multiple. Either that or you'd need a more unique key.
Hi I'm trying to subscribe to AWS AppSync using JS with this example https://docs.aws.amazon.com/appsync/latest/devguide/building-a-client-app-javascript.html
When querying user table, I got the result just fine. But I can't seem to subscribe to new results.
What I got is a 404 error:
https://some-iot-url.iot.eu-west-1.amazonaws.com/mqtt?X-Amz-Algorithm=..... 404 (Not Found)
with the following error:
errorCode:7
errorMessage:"AMQJS0007E Socket error:undefined."
invocationContext:undefined
The strange thing about the IOT url is that it doesn't match with my IOT url in the IoT core dashboard. Is this expected?
More info: I bundled the code with webpack (but I guess this has nothing to do with the error)
Here's my full code
config.js
Object.defineProperty(exports, "__esModule", { value: true });
var config = {
AWS_ACCESS_KEY_ID: '',
AWS_SECRET_ACCESS_KEY: '',
HOST: 'my-host.appsync-api.eu-west-1.amazonaws.com',
REGION: 'eu-west-1',
PATH: '/graphql',
ENDPOINT: '',
};
config.ENDPOINT = "https://" + config.HOST + config.PATH;
exports.default = config;
app.js
/**
* This shows how to use standard Apollo client on Node.js
*/
global.WebSocket = require('ws');
global.window = global.window || {
setTimeout: setTimeout,
clearTimeout: clearTimeout,
WebSocket: global.WebSocket,
ArrayBuffer: global.ArrayBuffer,
addEventListener: function () { },
navigator: { onLine: true }
};
global.localStorage = {
store: {},
getItem: function (key) {
return this.store[key]
},
setItem: function (key, value) {
this.store[key] = value
},
removeItem: function (key) {
delete this.store[key]
}
};
require('es6-promise').polyfill();
require('isomorphic-fetch');
// Require exports file with endpoint and auth info
const aws_exports = require('./aws-exports').default;
// Require AppSync module
const AUTH_TYPE = require('aws-appsync/lib/link/auth-link').AUTH_TYPE;
const AWSAppSyncClient = require('aws-appsync').default;
const url = aws_exports.ENDPOINT;
const region = aws_exports.REGION;
const type = AUTH_TYPE.API_KEY;
// If you want to use API key-based auth
const apiKey = 'my-api-key';
// If you want to use a jwtToken from Amazon Cognito identity:
const jwtToken = 'xxxxxxxx';
// // If you want to use AWS...
// const AWS = require('aws-sdk');
// AWS.config.update({
// region: aws_exports.REGION,
// credentials: new AWS.Credentials({
// accessKeyId: aws_exports.AWS_ACCESS_KEY_ID,
// secretAccessKey: aws_exports.AWS_SECRET_ACCESS_KEY
// })
// });
// const credentials = AWS.config.credentials;
// Import gql helper and craft a GraphQL query
const gql = require('graphql-tag');
const query = gql(`
query AllUser {
listUsers(first: 20) {
__typename
items{
id
userId
username
}
}
}`);
// Set up a subscription query
const subquery = gql(`
subscription NewUser {
subscribeToNewUsers {
__typename
id
userId
username
}
}`);
// Set up Apollo client
const client = new AWSAppSyncClient({
url: url,
region: region,
auth: {
type: type,
apiKey: apiKey,
}
});
client.hydrated().then(function (client) {
//Now run a query
console.log('querying')
client.query({ query: query })
.then(function logData(data) {
console.log('results of query: ', data);
})
.catch(console.error);
//Now subscribe to results
const observable = client.subscribe({ query: subquery });
console.log(observable)
const realtimeResults = function realtimeResults(data) {
console.log('realtime data: ', data);
};
observable.subscribe({
next: realtimeResults,
complete: console.log,
error: console.log,
});
});
Hey sorry for the slow response. If you are running this in a browser context can you try commenting out the following lines of code:
/*
global.WebSocket = require('ws');
global.window = global.window || {
setTimeout: setTimeout,
clearTimeout: clearTimeout,
WebSocket: global.WebSocket,
ArrayBuffer: global.ArrayBuffer,
addEventListener: function () { },
navigator: { onLine: true }
};
global.localStorage = {
store: {},
getItem: function (key) {
return this.store[key]
},
setItem: function (key, value) {
this.store[key] = value
},
removeItem: function (key) {
delete this.store[key]
}
};
require('isomorphic-fetch');
*/
// You should be able to keep this.
require('es6-promise').polyfill();
This sample has some work arounds to make it work nicely with node but it seems like there are a few issues in its current state. Let me know if that works. Thanks!