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()
};
...
}
Related
So basically here is the scenario. The user tries to access a protected API link. During the request stage, The Next.js middleware checks if the token is correct, decodes the token and then creates a current user property with response.user and if possible, checks if the exists on the MongoDB database
This is what the middleware.js currently looks like
interface CustomNextResponse extends NextResponse {
request: {
userId: string;
};
}
export async function sign(payload: JwtPayload, secret: string): Promise<string> {
const iat = Math.floor(Date.now() / 1000);
const exp = iat + 60 * 60; // one hour
return new SignJWT({ ...payload })
.setProtectedHeader({ alg: 'HS256', typ: 'JWT' })
.setExpirationTime(exp)
.setIssuedAt(iat)
.setNotBefore(iat)
.sign(new TextEncoder().encode(secret));
}
export async function verify(token: string, secret: string): Promise<JwtPayload> {
const { payload } = await jwtVerify(token, new TextEncoder().encode(secret));
return payload;
}
export async function middleware(request: NextRequest) {
try {
const refreshToken = request.cookies.get('X-Refresh-Token')?.value;
const requestHeaders = new Headers(request.headers);
const bearerToken = requestHeaders.get('Authorization');
if (!refreshToken) {
throw new Error('Unauthorized');
}
if (!bearerToken) {
throw new Error('Unauthorized');
}
const accessToken = bearerToken.split(' ')[1];
if (!accessToken) {
throw new Error('Unauthorized');
}
const decode: JwtPayload = (await verify(accessToken, secretKey)) as JwtPayload;
const response = NextResponse.next() as CustomNextResponse;
response.request = {
userId: decode.id
}
return response;
} catch (error) {
const errorObject = error as Error;
return new NextResponse(JSON.stringify({ success: false, message: errorObject.message }), {
status: 401,
headers: { 'content-type': 'application/json' },
});
}
}
But in the Nextapi at pages/api/me I can neither access the property with req nor res
async function handler(req: CustomNextApiRequest, res: NextApiResponse) {
//req.userId and res.userId give me undefined
Is it case the case then that middleware cant does not store custom properties to be accessed when making a request the same that Express.js does it? plus can you access MongoDB within the middleware?
I have a function to get the data returned as follows:
async function getAccessToken () {
let accessTokenInit = await oauth2Client.getAccessToken()
let accessToken = JSON.stringify(accessTokenInit)
return accessToken
}
It will return the following array:
And this is the code I need to get the accesstoken value in "token": value but I try to return accessToken.data.access_token but it doesn't work...
email: {
provider: env(`${process.env.REACT_APP_EMAIL_PROVIDER}`),
providerOptions: {
host: env("EMAIL_SMTP_HOST", "smtp.gmail.com"),
port: env("EMAIL_SMTP_PORT", 587),
auth: {
type: "OAuth2",
user: "hjhjhj1i2i#gmail.com",
clientId: `"${process.env.CLIENT_ID }"`,
clientSecret: `${process.env.CLIENT_SECRET}`,
refreshToken: `${process.env.REFRESH_TOKEN}`,
accessToken: await getAccessToken(),
},
},
}
So is there a way for me to get the value and assign it to acessToken?
async function getAccessToken() {
const accessTokenInit = await oauth2Client.getAccessToken();
return accessTokenInit.token;
}
Or you can directly return the response if you are not using in this wrapper function:
async function getAccessToken() {
return (await oauth2Client.getAccessToken()).token;
}
The mistake i think you were doing was that you are converting an object to a string and using whole that string in token i.e you have all the object.
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'),
};
}
};
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
I'm building a graphql server and one of the resolvers should return an url that is fetched from the aws api. I have tried in hours with promises, async await but nothing have worked yet.
What happens in the code i the following:
1) i make a call to aws api, and get a signed url in the callback.
2) i want to return that url in the graphql resolver function - getSignedURL
My question is: How can i make a resolver function return a result that i've got in another functions callback?
I will appreciate any help!
IN CLASS S3Store
var s3 = new aws.S3();
newSignedUrl(callback){
var params = {
Bucket: 'testBucket28993746',
Key: uuid.v4(),
Expires: 100,
ContentType: 'image/jpg'
};
s3.getSignedUrl('putObject', params, (err, signedURL)=>{
callback(err,signedURL);
});
}
Graphql resolver
getSignedURL(){//TODO: more secure, require auth first
let newURL = null;
s3store = new S3Store();
s3store.newSignedUrl((err,result)=>{
if(err){
console.log(err)
newURL = {}
} else{
console.log(result);
newURL = result;
}
});
return newURL;
},
When i make a call to the graphql endpoint, i get following:
{
"data": {
"getSignedURL": null
}
}
This woked for me:
IN CLASS S3Store
getSignedUrl(){
var params = {
Bucket: 'testBucket28993746',
Key: uuid.v4(),
Expires: 100,
ContentType: 'image/jpg'
};
return new Promise ((resolve, reject)=> { s3.getSignedUrl('putObject',params, (err, signedURL)=>{
if(err){
reject(err);
} else {
resolve( signedURL);
// console.log(signedURL);
console.log("in else ")
}
})
})
}
Graphql resolver
getSignedURL(){
return new S3Store().getSignedUrl().then((url)=> {return url});
}