I am having trouble with the ‘jose’ library when it comes to verifying the generated token
or JWT
of my API. So far the createAccessToken
of this is working properly and is generating the token. However in my verifyToken and decodeToken functions, I am getting an error. So far here is the code for my ./middleware/Authentication.js
const expressAsyncHandler = require('express-async-handler');
const { v4: uuidv4} = require('uuid');
const fs = require('fs');
const path = require('path');
const { importJWK, CompactSign, CompactEncrypt, compactDecrypt, jwtVerify } = require('jose');
// Import the RSA keys
const privateKey = JSON.parse(fs.readFileSync(path.join(__dirname, '..', 'privateKey.json'), 'utf8'));
const publicKey = JSON.parse(fs.readFileSync(path.join(__dirname, '..', 'publicKey.json'), 'utf8'));
// Helper function to create an access token
const createAccessToken = expressAsyncHandler(async (user, userIp, userAgent) => {
try {
const { password, createdAt, updatedAt, ...userData } = user;
const now = new Date();
const endOfDay = new Date(now);
endOfDay.setHours(23, 59, 59, 999);
const expiresInSeconds = Math.floor((endOfDay - now) / 1000);
// JWT options
const jwtPayload = {
iss: 'havok gaming',
sub: userData._id.toString(),
exp: Math.floor(Date.now() / 1000) + expiresInSeconds,
iat: Math.floor(Date.now() / 1000),
jti: uuidv4(),
user: userData,
ip: userIp || 'unknown',
userAgent: userAgent.source || 'unknown',
role: userData.role
};
// Create the signed JWT
const privateKeyJwk = await importJWK(privateKey, 'RS512');
const sign = new CompactSign(new TextEncoder().encode(JSON.stringify(jwtPayload)))
.setProtectedHeader({ alg: 'RS512', typ: 'JWT' })
.sign(privateKeyJwk);
// Encrypt the signed JWT
const publicKeyJwk = await importJWK(publicKey, 'RSA-OAEP-256');
const encrypt = new CompactEncrypt(new TextEncoder().encode(sign))
.setProtectedHeader({ alg: 'RSA-OAEP-256', enc: 'A256GCM' })
.encrypt(publicKeyJwk);
return encrypt;
}
catch (error) {
throw new Error('Error creating access token');
}
});
// Middleware function to verify the token's validity
const verifyToken = expressAsyncHandler(async (req, res, next) => {
try {
const token = req.cookies['__Secure-auth.session-token'];
if (!token) {
res.status(401);
throw new Error('No token provided');
}
const publicKeyJwk = await importJWK(publicKey, 'RSA-OAEP-256');
const decrypt = await compactDecrypt(token, publicKeyJwk);
console.log('Decrypted token: ', decrypt);
// Verify JWT signature
const jwtPayload = JSON.parse(new TextDecoder().decode(decrypt.plaintext));
const publicKey = await importJWK(publicKey, 'RS512');
const { payload } = jwtVerify(new TextEncoder().encode(JSON.stringify(jwtPayload)), publicKey);
if (payload.exp < Math.floor(Date.now() / 1000)) {
res.status(401);
throw new Error('Token expired');
}
next();
}
catch (error) {
res.status(401);
console.log(error);
throw new Error('Invalid token');
}
});
// Helper function to decode the token
const decodeToken = expressAsyncHandler(async (token) => {
try {
const publicKeyJwk = await importJWK(publicKey, 'RSA-OAEP-256');
const decrypt = await compactDecrypt(token, publicKeyJwk);
// Verify and decode the JWT payload
const jwtPayload = JSON.parse(new TextDecoder().decode(decrypt.plaintext));
const publicKey = await importJWK(publicKey, 'RS512');
const { payload } = jwtVerify(new TextEncoder().encode(JSON.stringify(jwtPayload)), publicKey);
return payload;
}
catch (error) {
console.log(error);
throw new Error('Error decoding token');
}
});
module.exports = {
createAccessToken,
verifyToken,
decodeToken
};
Ideally I am trying to make the verifyToken
as a middleware to simply verify if the token is valid, like if it’s not expired. And the decodeToken
is for decoding the token when necessary. Any ideas on what I did wrong and how do I fix this? Here is the error I am receiving with the console logs and the error I get in the POSTMAN
application.
console
ReferenceError: Cannot access 'publicKey' before initialization
at PROJECT_DIRmiddlewareAuthentication.js:63:46
at asyncUtilWrap (PROJECT_DIRnode_modulesexpress-async-handlerindex.js:3:20)
at Layer.handle [as handle_request] (PROJECT_DIRnode_modulesexpresslibrouterlayer.js:95:5)
at next (PROJECT_DIRnode_modulesexpresslibrouterroute.js:149:13)
at Route.dispatch (PROJECT_DIRnode_modulesexpresslibrouterroute.js:119:3)
at Layer.handle [as handle_request] (PROJECT_DIRnode_modulesexpresslibrouterlayer.js:95:5)
at PROJECT_DIRnode_modulesexpresslibrouterindex.js:284:15
at Function.process_params (PROJECT_DIRnode_modulesexpresslibrouterindex.js:346:12)
at next (PROJECT_DIRnode_modulesexpresslibrouterindex.js:280:10)
at Function.handle (PROJECT_DIRnode_modulesexpresslibrouterindex.js:175:3)
POSTMAN response error
{
"title": "Unauthorized",
"message": "Invalid token",
"stackTrace": "Error: Invalid tokenn at PROJECT_DIR\server\middleware\Authentication.js:83:15n at asyncUtilWrap (PROJECT_DIR\server\node_modules\express-async-handler\index.js:3:20)n at Layer.handle [as handle_request] (PROJECT_DIR\server\node_modules\express\lib\router\layer.js:95:5)n at next (PROJECT_DIR\server\node_modules\express\lib\router\route.js:149:13)n at Route.dispatch (PROJECT_DIR\server\node_modules\express\lib\router\route.js:119:3)n at Layer.handle [as handle_request] (PROJECT_DIR\server\node_modules\express\lib\router\layer.js:95:5)n at PROJECT_DIR\server\node_modules\express\lib\router\index.js:284:15n at Function.process_params (PROJECT_DIR\server\node_modules\express\lib\router\index.js:346:12)n at next (PROJECT_DIR\server\node_modules\express\lib\router\index.js:280:10)n at Function.handle (PROJECT_DIR\server\node_modules\express\lib\router\index.js:175:3)"
}