Using result of one function as a variable in another - node.js - javascript

I'm writing a node.js script to generate a GitHub installation access token. Here's what I've got:
const axios = require("axios");
var fs = require('fs');
var jwt = require("jsonwebtoken");
var gitInstallationAccessToken = {
genJWTToken: function(callback) {
var private_key = fs.readFileSync("/path/to/my/pemfile.pem");
const now = Math.round(Date.now() / 1000);
const payload = {
iat : now,
exp : now + (10 * 60),
iss : 7233
};
const token = jwt.sign(payload, private_key, { algorithm: 'RS256' })
callback(token);
},
genInstallationAccessToken: function(token, callback) {
var jwt = gitInstallationAccessToken.genJWTToken(function(token) {
return token;
});
console.log("JWT: ", jwt)
var instance = axios({
method: "post",
url: "https://api.github.com/installations/:installation_id/access_tokens",
headers: {
"Accept" : "application/vnd.github.machine-man-preview+json",
"Authorization" : `Bearer ${jwt}`
}
})
.then(function(response) {
console.log("Response: ",response.data);
callback(response);
})
.catch(function(error) {
console.warn("Unable to authenticate");
// The request was made and the server responded with a status code
// that falls out of the range of 2xx
if (error.response) {
console.warn(`Status ${error.response.status}`);
console.warn(`${error.response.data.message}`);
}
});
}
}
module.exports = gitInstallationAccessToken;
gitInstallationAccessToken.genInstallationAccessToken(function(response) {
console.log("response: ", response)
});
My JWT token is getting generated by genJWTToken. I can see that if I add a console.log("Token: ", token) before the callback in genJWTToken.
I now need to use that token in genInstallationAccessToken but I'm clearly calling it wrong. As the following returns undefined:
var jwt = gitInstallationAccessToken.genJWTToken(function(token) {
return token;
});
console.log("JWT: ", jwt)
How do I fix this?

I think you should consider refactoring this and use chained promises it will be easier to understand and control..
Something like this:
function getToken() {
return new Promise(function(resolve, reject) {
resolve('token')
})
}
function chainPromise() {
var token
getToken().then((response) => {
token = response
console.log(token)
}).then(() => {
console.log('I am here and also see: ', token)
})
}
chainPromise()
You should then be able to track down the path of your token quite easily

Related

How to improve sequential promises execution and force fulfillment

This code is being used in a Sveltekit web application.
In the first step I get a user jwt token from an api like : dashboard.example.com/auth/local
and in the second step I'm using the response of the first api call to get full information from an api endpoint like this : example.com/api/users/token
This is an endpoint in an Sveltekit application:
import { json as json$1, error } from '#sveltejs/kit';
import axios from 'axios';
import md5 from 'md5';
import { SITE_ADDRESS } from '$lib/Env';
let userToken;
/** #type {import('#sveltejs/kit').RequestHandler} */
export async function POST({ request }) {
const bodyData = await request.json();
let identifier = bodyData.data.identifier;
let password = bodyData.data.password;
let loginToken = bodyData.data.loginToken;
let newLoginToken = md5(identifier + password + process.env.SECURE_HASH_TOKEN);
let dataResult = await axios
.post(`${import.meta.env.VITE_SITE_API}/auth/local`, {
identifier: identifier,
password: password
})
.then((response) => {
return response.data;
})
.then((response) => {
let userSummaryData = response;
userToken = md5(
userSummaryData.user.username + userSummaryData.user.id + process.env.SECURE_HASH_TOKEN
);
let userCompleteData = axios
.post(`${SITE_ADDRESS}/api/users/${userToken}`, {
data: {
userID: userSummaryData.user.id,
username: userSummaryData.user.username
}
})
.then((response) => {
return {
userJWT: userSummaryData.jwt,
userSummary: userSummaryData.user,
userFullSummary: response.data.userFullSummary
};
});
return userCompleteData;
})
.catch((error) => {
// console.log(' ---- Err ----');
});
if (dataResult && newLoginToken == loginToken) {
return json$1(
{
userJWT: dataResult.userJWT,
userSummary: dataResult.userSummary,
userFullSummary: dataResult.userFullSummary
},
{
headers: {
'cache-control': 'private, max-age=0, no-store'
}
}
);
} else if (dataResult && newLoginToken != loginToken) {
throw error(400, 'Something wrong happened');
}
throw error(401, 'Something wrong happened');
}
This code is work perfectly in localhost. But when I test it on host I get error 401.
and the question is :
Why this works on localhost but doesn't work on the server?
How can I improve this kind of promises (I'd like to use the response of the first api call in the second api call and return both
as a result)

Configuring AXIOS with SVELTEKIT for App Token and Access Key generation on client side

I'm trying to implement SumSub, a verification tool with my SvelteKit application. All API requests require authentication in order to generate AccessTokens which are required to initiate their WebSDK on the front end of my application.
I'm following their example (https://github.com/SumSubstance/AppTokenUsageExamples/tree/master/JS) in order to generate an app token server-side which I can then bring into the client side to init the WebSDK.
In "sumsubinit.js" on server side, I have the following code:
import axios from 'axios';
import FormData from 'form-data';
import crypto from "crypto";
let accesstoken123;
let externalUserId;
let levelName;
let response;
// These parameters should be used for all requests
const SUMSUB_APP_TOKEN = "TOKEN"; // Example: sbx:uY0CgwELmgUAEyl4hNWxLngb.0WSeQeiYny4WEqmAALEAiK2qTC96fBad - Please don't forget to change when switching to production
const SUMSUB_SECRET_KEY = "KEY"; // Example: Hej2ch71kG2kTd1iIUDZFNsO5C1lh5Gq - Please don't forget to change when switching to production
const SUMSUB_BASE_URL = 'https://api.sumsub.com';
var config = {};
config.baseURL= SUMSUB_BASE_URL;
axios.interceptors.request.use(createSignature, function (error) {
return Promise.reject(error);
})
// This function creates signature for the request as described here: https://developers.sumsub.com/api-reference/#app-tokens
function createSignature(config) {
console.log('Creating a signature for the request...');
var ts = Math.floor(Date.now() / 1000);
const signature = crypto.createHmac('sha256', SUMSUB_SECRET_KEY);
signature.update(ts + config.method.toUpperCase() + config.url);
if (config.data instanceof FormData) {
signature.update(config.data.getBuffer());
} else if (config.data) {
signature.update(config.data);
}
config.headers['Access-Control-Allow-Origin'] = "*";
config.headers['X-App-Access-Ts'] = ts;
config.headers['X-App-Access-Sig'] = signature.digest('hex');
return config;
}
// These functions configure requests for specified method
// https://developers.sumsub.com/api-reference/#creating-an-applicant
function createApplicant(externalUserId, levelName) {
console.log("Creating an applicant...");
var method = 'post';
var url = '/resources/applicants?levelName=' + levelName;
var ts = Math.floor(Date.now() / 1000);
var body = {
externalUserId: externalUserId
};
var headers = {
'Accept': 'application/json',
'Content-Type': 'application/json',
'X-App-Token': SUMSUB_APP_TOKEN
};
config.method = method;
config.url = url;
config.headers = headers;
config.data = JSON.stringify(body);
return config;
}
// https://developers.sumsub.com/api-reference/#adding-an-id-document
// https://developers.sumsub.com/api-reference/#getting-applicant-status-sdk
function getApplicantStatus(applicantId) {
console.log("Getting the applicant status...");
var method = 'get';
var url = `/resources/applicants/${applicantId}/status`;
var headers = {
'Accept': 'application/json',
'X-App-Token': SUMSUB_APP_TOKEN
};
config.method = method;
config.url = url;
config.headers = headers;
config.data = null;
return config;
}
// https://developers.sumsub.com/api-reference/#access-tokens-for-sdks
function createAccessToken (externalUserId, levelName = 'basic-kyc-level', ttlInSecs = 600) {
console.log("Creating an access token for initializng SDK...");
var method = 'post';
var url = `/resources/accessTokens?userId=${externalUserId}&ttlInSecs=${ttlInSecs}&levelName=${levelName}`;
var headers = {
'Accept': 'application/json',
'X-App-Token': SUMSUB_APP_TOKEN
};
config.method = method;
config.url = url;
config.headers = headers;
config.data = null;
return config;
}
// This section contains requests to server using configuration functions
// The description of the flow can be found here: https://developers.sumsub.com/api-flow/#api-integration-phases
// Such actions are presented below:
// 1) Creating an applicant
// 2) Adding a document to the applicant
// 3) Getting applicant status
// 4) Getting access tokens for SDKs
export async function main() {
externalUserId = "random-JSToken-" + Math.random().toString(36).substr(2, 9);
levelName = 'basic-kyc-level';
console.log("External UserID: ", externalUserId);
response = await axios(createApplicant(externalUserId, levelName))
.then(function (response) {
console.log("Response:\n", response.data);
return response;
})
.catch(function (error) {
// console.log("Error:\n", error.response.data);
});
const applicantId = response.data.id;
console.log("ApplicantID: ", applicantId);
response = await axios(addDocument(applicantId))
.then(function (response) {
console.log("Response:\n", response.data);
return response;
})
.catch(function (error) {
// console.log("Error:\n", error.response.data);
});
response = await axios(getApplicantStatus(applicantId))
.then(function (response) {
console.log("Response:\n", response.data);
return response;
})
.catch(function (error) {
// console.log("Error:\n", error.response.data);
});
response = await axios(createAccessToken(externalUserId, levelName, 1200))
.then(function (response) {
console.log("Response:\n", response.data);
accesstoken123 = response.data.token;
return response;
})
.catch(function (error) {
// console.log("Error:\n", error.response.data);
});
return accesstoken123.toString();
console.log(accesstoken123);
}
main();
Which is just ripped straight from the GitHub tutorial. On my front end, I have an import of the "main" function and also run it in-app. On Server Side, it runs absolutely fine and generates the necessary token. However, in-browser it fails every single time.
I feel this might be incredibly stupid, or naive but I've been at this for several days now and I'm pulling my hair out and feel that it may be something to do with CORS, my limited understanding or even just the way I've tried to configure this for Svelte, but genuinely ANY ANY ANY help, guidance or direction at this point would be incredibly appreciated and I would be super super grateful for anything.

How to mock jsonwebtoken + jwsks-rsa for custom lambda Authroizer

I am using lambda custom authoriser in front of api endpoint.
so whenever a request comes on that endpoint api gateway calls authoriser to validate jwt token and based on validation it generates a policy.
Also jwt tokens are generated by auth0 api, i know there is a library https://www.npmjs.com/package/mock-jwks which mocks auth0 jwt token but it a http request to endpoint which is not possible in my case
the code is pretty self explanatory howver i stuck with mocking jsonwebtoken and jwks-rsa library? what could be the ideal way to test such kind of function?
my authorizer.js
require('dotenv').config();
const jwks = require('jwks-rsa');
const jwt = require('jsonwebtoken');
const createPolicyDocument = (effect) => {
const policy = {
Version: '2012-10-17',
Statement: [
{
Effect: effect,
Action: 'execute-api:Invoke',
Resource:
'xxxxxxxxxxxxxxxxx',
},
],
};
return policy;
};
// Extract the Bearer token from the event sent by lambda and return it to the authorizer
const extractToken = (event) => {
const tokenWithBearer = event.authorizationToken;
if (!tokenWithBearer) {
throw new Error(' "event.authorization" paramters is missing');
}
const bearer = tokenWithBearer.split(' ');
if (!bearer) {
throw new Error('Invalid token');
}
return bearer[1];
};
const jwtVerifyOptions = {
audience: process.env.AUDIENCE,
issuer: process.env.TOKEN_ISSUER,
};
const client = jwks({
cache: true,
rateLimit: true,
jwksRequestsPerMinute: 10,
jwksUri: process.env.JWKS_URI,
});
function verifyToken(token) {
return new Promise((resolve, reject) => {
const tempDecodedToken = jwt.decode(token, { complete: true });
console.log(token);
console.log(tempDecodedToken);
const { kid } = tempDecodedToken.header;
client.getSigningKey(kid, (err, key) => {
if (err) {
console.log('signinin key get error', err);
reject('Deny');
}
const signingKey = key.publicKey || key.rsaPublicKey;
console.log(signingKey);
jwt.verify(token, signingKey, jwtVerifyOptions, (error, decoded) => {
if (error) {
console.log('jwt verify error', error);
reject('Deny');
}
console.log(decoded);
resolve({ response: 'Allow', decoded });
});
});
});
}
module.exports.auth = async (event, context, callback) => {
try {
const token = extractToken(event);
const tokenResponse = await verifyToken(token);
console.log(tokenResponse);
if (tokenResponse.response === 'Allow') {
return {
principalId: tokenResponse.decoded.sub,
policyDocument: createPolicyDocument('Allow'),
context: { scope: tokenResponse.decoded.scope },
};
}
return {
policyDocument: createPolicyDocument('Deny'),
};
} catch (err) {
return {
policyDocument: createPolicyDocument('Deny'),
};
}
};

Session cookie from node-fetch is invalid?

I am writing a javascript program (for a github action) right now but ran into a problem.
I was trying to log into www.overleaf.com and access the page https://www.overleaf.com/project after generating a session cookie by sending a POST request to https://www.overleaf.com/login with my credentials and the csrf token.
The response contained the requested token in the set-cookie header as expected, however, when I tried to access https://www.overleaf.com/project via GET, I get redirected back to https://www.overleaf.com/login
When copying a session cookie saved in my browser, the request works just fine as expected.
I tried doing the same thing in the command line with cURL and it worked there.
I am fairly certain my authentication request is accepted by Overleaf's server, because I have tried intentionally incorrectly sending the password or the csrf token and in both cases, the response does not give me a new session cookie but sends the old one.
If anyone has any clue what is going wrong, I'd be very thankful for your input.
This is what worked in the terminal, which I'm trying to replicate in javascript with node-fetch:
curl -v --header "Content-Type: application/json" --cookie "GCLB=someothercookie;overleaf_session2=firstsessioncookie" --data '{"_csrf":"the_csrf_token", "email": "MYEMAIL", "password":"MYPASSWORD"}' https://www.overleaf.com/login
to get the cookie and csrf token and
curl -v https://www.overleaf.com/project --cookie "overleaf_session2=returnedsessioncookie; GCLB=someothercookie" as the request that returns the html page of my projects.
This is my javascript code, I have double, triple, quadruple checked it but I think I'm missing something.
const fetch = require("node-fetch");
const parser = require("node-html-parser");
const scparser = require("set-cookie-parser");
async function run() {
const email = process.env.EMAIL;
const password = process.env.PASSWORD;
var cookies = await login(email, password);
console.log(await all_projects(cookies));
}
async function login(email, password) {
const login_get = await fetch("https://www.overleaf.com/login");
const get_cookies = login_get.headers.raw()["set-cookie"];
const parsed_get_cookies = scparser.parse(get_cookies, {
decodeValues: false
});
const overleaf_session2_get = parsed_get_cookies.find(
(element) => element.name == "overleaf_session2"
).value;
const gclb = parsed_get_cookies.find(
(element) => element.name == "GCLB"
).value;
console.log("overleaf_session2_get:", overleaf_session2_get, "gclb:", gclb);
const get_responsetext = await login_get.text();
const _csrf = parser
.parse(get_responsetext)
.querySelector("input[name=_csrf]")
.getAttribute("value");
login_json = { _csrf: _csrf, email: email, password: password };
console.log(login_json);
const login_post = await fetch("https://www.overleaf.com/login", {
method: "post",
body: JSON.stringify(login_json),
headers: {
"Content-Type": "application/json",
"Cookie": "GCLB=" + gclb + ";overleaf_session2=" + overleaf_session2_get
}
});
const post_cookies = login_post.headers.raw()["set-cookie"];
const parsed_post_cookies = scparser.parse(post_cookies, {
decodeValues: false
});
const overleaf_session2_post = parsed_post_cookies.find(
(element) => element.name == "overleaf_session2"
).value;
console.log(
"successful:",
overleaf_session2_get != overleaf_session2_post ? "true" : "false"
);
console.log(await fetch("https://www.overleaf.com/project", {
headers: {
"Cookie": "overleaf_session2=" + overleaf_session2_post
}
}))
return "overleaf_session2=" + overleaf_session2_post;
}
async function all_projects(cookies) {
const res = await fetch("https://www.overleaf.com/project", {
headers: {
Cookie: cookies
}
});
return res;
}
run();
Yes your authentication request is probably valid however this is likely to be a security issue which browsers do not allow you to do such thing and freely access another website's cookie.
Browsers do not allow you to access other domain's cookies, If they did then web would be an unsafe place because for example Stackoverflow could access my Facebook account cookie and extract my personal information.
I fixed my issue by not using node-fetch and switching to https.
Here is what worked:
async function login(email, password) {
//GET login page
const get = await get_login();
//get necessary info from response
const csrf = parser
.parse(get.html)
.querySelector(`meta[name="ol-csrfToken"]`)
.getAttribute("content");
const session1 = scparser
.parse(get.headers["set-cookie"], { decodeValues: false })
.find((cookie) => cookie.name == "overleaf_session2").value;
const gclb = scparser
.parse(get.headers["set-cookie"], { decodeValues: false })
.find((cookie) => cookie.name == "GCLB").value;
//POST login data
const post = await post_login(csrf, email, password, session1, gclb);
//get necessary data from response
const session2 = scparser
.parse(post["set-cookie"], { decodeValues: false })
.find((cookie) => cookie.name == "overleaf_session2").value;
//GET new csrf token from project page
const projects = await get_projects(session2, gclb);
const csrf2 = parser
.parse(projects.html)
.querySelector(`meta[name="ol-csrfToken"]`)
.getAttribute("content");
//return data
return {
session: session2,
gclb: gclb,
csrf: csrf2,
projects: projects.html
};
}
async function get_login() {
const url = "https://www.overleaf.com/login";
return new Promise((resolve) => {
https.get(url, (res) => {
var data;
res.on("data", (chunk) => {
data += chunk;
});
res.on("end", () => {
resolve({ html: data, headers: res.headers });
});
});
});
}
async function get_projects(session2, gclb) {
const url = "https://www.overleaf.com/project";
return new Promise((resolve) => {
https.get(
url,
{ headers: { Cookie: `GCLB=${gclb};overleaf_session2=${session2}` } },
(res) => {
var data;
res.on("data", (chunk) => {
data += chunk;
});
res.on("end", () => {
resolve({ html: data, headers: res.headers });
});
}
);
});
}
async function post_login(_csrf, email, password, session1, gclb) {
const url = "https://www.overleaf.com/login";
const options = {
method: "POST",
headers: {
"Content-Type": "application/json",
Cookie: `GCLB=${gclb};overleaf_session2=${session1}`
}
};
const postData = {
_csrf: _csrf,
email: email,
password: password
};
return new Promise((resolve) => {
var req = https.request(url, options, (res) => {
resolve(res.headers);
});
req.on("error", (e) => {
console.error(e);
});
req.write(JSON.stringify(postData));
req.end();
});
}

Trying to sign a jwt returns undefined

I'm learning node and I'm migrating my current API from python. I'm trying to create a jwt token to authenticate in a third party API, however my function returns undefined. The jwt method I'm using to sign the token is async, so I guess my function doesn't wait until jwt returns the token.
This is my function to sign and create the jwt token:
function token() {
const payload = {
iat: Math.floor(new Date() / 1000),
exp: Math.floor(new Date() / 1000) + 30,
sub: "api_key_jwt",
iss: "external",
jti: crypto.randomBytes(6).toString("hex")
};
return jwt.sign(payload, privatekey, { algorithm: "RS256" }, function(
err,
token2
) {
return token2;
});
}
So, when I call it:
exports.genToken = function() {
const header = {
"x-api-key": api
};
const data = {
kid: api,
jwt_token: token()
};
async function authorization(req, res) {
try {
const auth = await rp({
url: authurl,
method: "POST",
headers: header,
body: data
});
res.send(auth.body);
} catch (error) {
res.send(404).send();
}
}
return {
"x-api-key": api,
Authorization: "Bearer " + authorization()
};
};
jwt_token returns undefined. What am I doing wrong, and how can I fix it?
Thanks in advance guys!
Edit: console.log(token2) returns the signed token. So the problem is returning the token from the token() function
You're trying to return from a callback which doesn't work. Change your token function to return Promise then you can use the async/await like:
function token() {
...
return new Promise((resolve, reject) => {
jwt.sign(payload, privatekey, { algorithm: "RS256" }, function(err, token2) {
if (err) reject(err);
else resolve(token2)
});
})
}
// note async
exports.genToken = async function() {
...
const data = {
kid: api,
jwt_token: await token()
};
...
}

Categories

Resources