Singleton not keeping parity in node - node.js

Having a very very peculiar issue happening:
When i start my node application, i set my access tokens to an instance of a model like so:
index.js
const token = new Tokens();
token.setTokens(access_token, refresh_token);
console.log(token.getTokens()) // WORKS
I then call the getter functions in my instance in different files.
RunSchedular.js
const tokens = Tokens.getInstance();
console.log('sched',tokens.getTokens()) //WORKS
API.js
export const POSTRequest = () => {
const currentTokens = Tokens.getInstance();
const refreshToken = currentTokens.getRefreshToken(); // DOES NOT WORK
const body = {
method: 'POST',
headers:
{
"Content-Type": "application/x-www-form-urlencoded",
"Cache-Control": "no-cache"
},
body: qs.stringify({
client_secret: clientSecret,
client_id: clientId,
refresh_token: refreshToken,
grant_type: 'refresh_token',
redirect_uri: redirectUri
})
};
return body;
}
My model is like so:
let instance = null;
export default class Tokens {
constructor() {
if(!instance) {
instance = this;
}
this.accessToken = '';
this.refreshToken = '';
}
getAccessToken() {
return this.accessToken;
}
setAccessToken(value) {
this.accessToken = value;
}
getRefreshToken() {
return this.refreshToken;
}
setRefreshToken(value) {
this.refreshToken = value;
}
getTokens() {
return {
accessToken: this.accessToken,
refreshToken: this.refreshToken
}
}
setTokens(accessToken,refreshToken) {
this.accessToken = accessToken;
this.refreshToken = refreshToken;
}
static getInstance() {
console.log('instance', instance)
if(!instance) {
instance = new Tokens();
}
return instance;
}
};
Any ideas why this could be happening? The instance in the API.js does not return my access tokens (access token = '' as per the constructor) where as the schedular.js and index.js returns my access token fine?
Is my Model not correct?

How are you importing the Tokens module? If the path is different, 2 different modules will be imported.
E.G.:
import { Tokens } from 'src/singletons/Tokens';
Will be a different object than:
import { Tokens } from './../singletons/Tokens';
More information about singletons in javascript.
More information about module caching in NodeJS.

Related

Implementation of rxjs BehaviourSubject in Next.js for state management not working

Trying to store jwt token on login using rxjs behavioursubject
Then creating a http request with Authorization: Bearer ${user.jwtToken} in the
I believe I need to have
a) initial value,
b) a source that can be turned into an observable
c) a public variable that can be subscribed
On log in the user is correctly added to the user subject here "userSubject.next(user);"
But whenever I try to create the bearer token its always null
// The Accounts Service
// initialise and set initial value
const userSubject = new BehaviorSubject(null);
const authApiUrl = "https:testApi";
export const accountService = {
` user: userSubject.asObservable(), get userValue() { return userSubject.value },
login,
getAllUsers
};
function login(email, password) {
return fetchWrapper.post(process.env.AuthApiUrl + '/accounts/authenticate', { email, password })
.then(user => {
userSubject.next(user);
localStorage.setItem('user', JSON.stringify(user));
return user;
});
}
function getAllUsers() {
return await fetchWrapper.get(process.env.AuthApiUrl + '/accounts/get-all-users');
}
}
// The fetchwrapper
export const fetchWrapper = {
get,
post
};
function post(url, body) {
const requestOptions = {
method: 'POST',
headers: { 'Content-Type': 'application/json', ...authHeader(url) },
credentials: 'include',
body: JSON.stringify(body)
};
return fetch(url, requestOptions).then(handleResponse);
}
function get(url) {
const requestOptions = {
method: 'GET',
headers: authHeader(url)
};
return fetch(url, requestOptions).then(handleResponse);
}
function authHeader(url) {
// return auth header with basic auth credentials if user is logged in and request is to the api url
// THE accountService.userValue IS ALWAYS NULL???
const user = accountService.userValue;
const isLoggedIn = user && user.jwtToken;
const isApiUrl = url.startsWith(process.env.AuthApiUrl);
if (isLoggedIn && isApiUrl) {
return { Authorization: `Bearer ${user.jwtToken}` };
} else {
return {};
}
}
function handleResponse(response) {
return response.text().then(text => {
const data = text && JSON.parse(text);
if (!response.ok) {
if ([401, 403].includes(response.status) && accountService.userValue) {
// auto logout if 401 Unauthorized or 403 Forbidden response returned from api
accountService.logout();
}
const error = (data && data.message) || response.statusText;
return Promise.reject(error);
}
return data;
});
}

Cannot able to pass data from Node to asp.net MVC

On the button click event of React side I am calling a node backend.
click event of react,
// calling node backend
this.uploadApi.command(postData.bin_files, this.dummy);
this.setState({submit_form});
}
dummy = (result)=>{
console.log(result);
}
This is my Node backend code,
import axios from 'axios';
class UploadFile {
constructor() {
this.url = 'http://localhost:56246/microservice/uploaddata'; //This is the local MVC application's URL (microservice is the controller)
}
command(postData, callback, uploadCallback = null) {
let jsonDataString = JSON.stringify(postData).replace(/&/g, '--and--');
jsonDataString = jsonDataString.replace(/\+/g, '--plus--');
const payload = JSON.parse(jsonDataString);
console.log('----------');
console.log(this.url);
console.log(payload);
console.log('----------');
// var data = qs.stringify({'jsondata':payload});
const data = new FormData();
for (var i = 0; i < payload.length; i++) {
console.log('inside for 1');
data.append(`model[${i}].name`, payload[i].name);
data.append(`model[${i}].bin_file`, payload[i].bin_file);
console.log('inside for 2');
}
console.log('=============');
console.log(data);
console.log('=============');
var config = {
method: 'post',
url: this.url,
headers: {
'Content-Type': 'multipart/form-data'
},
data: "jsondata=" + data,
onUploadProgress: (progressEvent) => {
const {
loaded,
total
} = progressEvent;
console.log("loaded:", loaded);
console.log("total:", total);
if (uploadCallback !== null) uploadCallback(progressEvent);
}
};
axios(config)
.then(function(response) {
// console.log(JSON.stringify(response.data));
callback(response.data);
})
.catch(function(error) {
console.log(error);
});
// axios.post(this.url, data)
// .then(res => console.log(res.data))
// .catch((error) => { console.error(error) });
}
}
export default UploadFile;
And this is my respective controller,
public dynamic UploadData(List<MemberInfo> model)
{
using (SqlConnection conn = new SqlConnection())
{
conn.ConnectionString = mstrDBConStringNew;
conn.Open();
SqlCommand command = new SqlCommand("SELECT * from tempstorage", conn);
using (SqlDataReader reader = command.ExecuteReader())
{
while (reader.Read())
{
//port = reader.GetString(0);
}
}
}
return "Hiiiiiiii";
}
public class MemberInfo
{
public string name { get; set; }
public string bin_file { get; set; }
}
Now If I show You while debugging, the controller and its respective action gets called but the value that I am expecting is null.
I have also tried like this way, but no luck
public dynamic UploadData(FormCollection model)
{
using (SqlConnection conn = new SqlConnection())
{
conn.ConnectionString = mstrDBConStringNew;
conn.Open();
SqlCommand command = new SqlCommand("SELECT * from tempstorage", conn);
using (SqlDataReader reader = command.ExecuteReader())
{
while (reader.Read())
{
//port = reader.GetString(0);
}
}
}
return "Hiiiiiiii";
}
This is my network request,
Please ask if anything additional is needed.
Yes I was able to figure out the issue,
var config = {
method: 'post',
url: this.url,
headers: {
'Content-Type': 'multipart/form-data'
},
data: data, // previously it was, "jsondata=" + data
Here I am getting the data as expected..

es6 constructor objects passed from source to target file is undefined

I am trying to access the object values passed from source file to target file.
Here in the code below, I pass an object named auth from ServiceFile.js to ChildClassFile.js
But outside the ChildClassFile.js the properties inside auth object is undefined. Any help on how to access it would be appreciated. Thanks in advance.
Code:
// ServiceFile.js
import ChildClass from 'ChildClass';
export default class Service {
constructor(cookie, token) {
super();
this.auth = {
cookie: 'XXX',
token: 'XXX',
};
this.checkout = new ChildClass(this.auth);
}
async serviceMethod(data) {
return await this.checkout.method1(data);
};
}
// ChildClassFile.js
class ChildClass {
constructor(auth) {
this.auth = auth;
console.log("ChildClass -> constructor -> this.auth", this.auth); // values are present
}
async method1(data) {
return await axios.post({
data: data,
headers: {
token: this.auth.token,
cookie: this.auth.cookie
}, // undefined value for both token and cookie
});
};
}
There are some things wrong in your code. You are calling super in the Service class but it's not extending any other class.
I've noticed that you're not adding an URL to your POST request.
The following code should work:
class Service {
constructor() {
this.auth = {
cookie: 'XXX',
token: 'XXX'
};
this.checkout = new ChildClass(this.auth);
}
async serviceMethod(data) {
return await this.checkout.method1(data);
}
}
class ChildClass {
constructor(auth) {
this.auth = auth;
console.log('ChildClass -> constructor -> this.auth', this.auth); // values are present
}
async method1(data) {
console.log('ChildClass -> this.auth.token,', this.auth.token); // value is present
console.log('ChildClass -> this.auth.cookie', this.auth.cookie); // value is present
return await axios.post('where is the url?', {
data: data,
headers: {
token: this.auth.token,
cookie: this.auth.cookie
}
});
}
}
new Service()
.serviceMethod('dummy data')
.then(res => console.log('res', res))
.catch(err => console.log('err.message', err.message));

How to logout once jwt token is expired

I am working on a web-app using node.js and vue.js, I am doing authentication and maintaining session using jwt and passport.js using passport-jwtstrategy
I have done all the things from creating jwt to protecting routes all the things now my issue is while generating jwt I am passing expiresIn:3600 so I want to auto-logout my user from Ui and remove token from localStorage once it has been one hour
On decoding my jwt I am getting
{
"name": "Dheeraj",
"iat": 1571896207,
"exp": 1571899807
}
So how can I get the real-time when to logout
In my auth.js vue store file my logout code when user clicks on logout is
logout({ commit }) {
return new Promise((resolve, reject) => {
localStorage.removeItem('jwt-token')
localStorage.removeItem('user-name')
commit('setAuthUser', null)
resolve(true)
})
},
In the same file, I have a method getAuthUser which is running whenever a page is loading or reloading to check to protect rout and guestUser
getAuthUser({ commit, getters }) {
const authUser = getters['authUser']
const token = localStorage.getItem('jwt-token')
const isTokenValid = checkTokenValidity(token)
if (authUser && isTokenValid) {
return Promise.resolve(authUser)
}
commit('setAuthUser', token)
commit('setAuthState', true)
debugger
return token
}
So how can I logout once my token is expired
Anyone out here please guide me how can I logout once the token is expired
Edit
In my router.js file
router.beforeEach((to, from, next) => {
store.dispatch('auth/getAuthUser')
.then((authUser) => {
const isAuthenticated = store.getters['auth/isAuthenticated']
if (to.meta.onlyAuthUser) {
if (isAuthenticated) {
next()
} else {
next({ name: 'login' })
}
} else if (to.meta.onlyGuestUser) {
if (isAuthenticated) {
next({ name: 'welcome' })
} else {
next()
}
} else {
next()
}
})
})
from my auth file I am calling get authUser which I have already mention above
for checking token validity I am using this code
function checkTokenValidity(token) {
if (token) {
const decodedToken = jwt.decode(token)
return decodedToken && (decodedToken.exp * 1000) > new Date().getTime()
}
return false
}
but it returns false when I am on login page and there is no token there but once I am loged in it shows null
My global api file
import axios from 'axios';
export default () => {
let headers = {
'cache-control': 'no-cache'
};
let accessToken = localStorage.getItem('jwt-token');
if (accessToken && accessToken !== '') {
headers.Authorization = accessToken;
};
return axios.create({
baseURL: 'http://localhost:8086/',
headers: headers
});
}
Refer to the axios documentataion: https://github.com/axios/axios
import axios from 'axios';
export default () => {
let headers = {
'cache-control': 'no-cache'
};
let accessToken = localStorage.getItem('jwt-token');
if (accessToken && accessToken !== '') {
headers.Authorization = accessToken;
};
const instance = axios.create({
baseURL: 'http://localhost:8086/',
headers: headers
});
instance.interceptors.response.use((response) => {
if(response.status === 401) {
//add your code
alert("You are not authorized");
}
return response;
}, (error) => {
if (error.response && error.response.data) {
//add your code
return Promise.reject(error.response.data);
}
return Promise.reject(error.message);
});
return instance;
}

How to authorize an HTTP POST request to execute dataflow template with REST API

I am trying to execute the Cloud Bigtable to Cloud Storage SequenceFile template via REST API in a NodeJS backend server.
I am using axios 0.17.1 to send the request and I'm getting 401 status.
I tried to follow the google documentation however I couldn't figure out how to authorize an HTTP request to run a dataflow template.
I want to be authenticated as a service account and I successfully generated and dowloaded the json file containing the private key.
Can anyone help me by showing an example of sending HTTP POST request to https://dataflow.googleapis.com/v1b3/projects/[YOUR_PROJECT_ID]/templates:launch?gcsPath=gs://dataflow-templates/latest/
You need to generate a jwt from your service account credentials. The jwt can be exchanged for an access token which can then be used to make the request to execute the Dataflow job. Complete example:
import axios from "axios";
import jwt from "jsonwebtoken";
import mem from "mem";
import fs from "fs";
const loadServiceAccount = mem(function(){
// This is a string containing service account credentials
const serviceAccountJson = process.env.GOOGLE_APPLICATION_CREDENTIALS;
if (!serviceAccountJson) {
throw new Error("Missing GCP Credentials");
}
})
const loadCredentials = mem(function() {
loadServiceAccount();
const credentials = JSON.parse(fs.readFileSync("key.json").toString());
return {
projectId: credentials.project_id,
privateKeyId: credentials.private_key_id,
privateKey: credentials.private_key,
clientEmail: credentials.client_email,
};
});
interface ProjectCredentials {
projectId: string;
privateKeyId: string;
privateKey: string;
clientEmail: string;
}
function generateJWT(params: ProjectCredentials) {
const scope = "https://www.googleapis.com/auth/cloud-platform";
const authUrl = "https://www.googleapis.com/oauth2/v4/token";
const issued = new Date().getTime() / 1000;
const expires = issued + 60;
const payload = {
iss: params.clientEmail,
sub: params.clientEmail,
aud: authUrl,
iat: issued,
exp: expires,
scope: scope,
};
const options = {
keyid: params.privateKeyId,
algorithm: "RS256",
};
return jwt.sign(payload, params.privateKey, options);
}
async function getAccessToken(credentials: ProjectCredentials): Promise<string> {
const jwt = generateJWT(credentials);
const authUrl = "https://www.googleapis.com/oauth2/v4/token";
const params = {
grant_type: "urn:ietf:params:oauth:grant-type:jwt-bearer",
assertion: jwt,
};
try {
const response = await axios.post(authUrl, params);
return response.data.access_token;
} catch (error) {
console.error("Failed to get access token", error);
throw error;
}
}
function buildTemplateParams(projectId: string, table: string) {
return {
jobName: `[job-name]`,
parameters: {
bigtableProjectId: projectId,
bigtableInstanceId: "[table-instance]",
bigtableTableId: table,
outputDirectory: `[gs://your-instance]`,
filenamePrefix: `${table}-`,
},
environment: {
zone: "us-west1-a" // omit or define your own,
tempLocation: `[gs://your-instance/temp]`,
},
};
}
async function backupTable(table: string) {
console.info(`Executing backup template for table=${table}`);
const credentials = loadCredentials();
const { projectId } = credentials;
const accessToken = await getAccessToken(credentials);
const baseUrl = "https://dataflow.googleapis.com/v1b3/projects";
const templatePath = "gs://dataflow-templates/latest/Cloud_Bigtable_to_GCS_Avro";
const url = `${baseUrl}/${projectId}/templates:launch?gcsPath=${templatePath}`;
const template = buildTemplateParams(projectId, table);
try {
const response = await axios.post(url, template, {
headers: { Authorization: `Bearer ${accessToken}` },
});
console.log("GCP Response", response.data);
} catch (error) {
console.error(`Failed to execute template for ${table}`, error.message);
}
}
async function run() {
await backupTable("my-table");
}
try {
run();
} catch (err) {
process.exit(1);
}

Resources