I'm working on server to server authentication and I want to access one request from serverB.
So that I have created 2 RSA key file in serverA repo.
1.safety-private.key
2.safety-public.key
And I'm sending request to serverB by following code.
ServerA :
import { Injectable } from '#nestjs/common';
const jwt = require("jsonwebtoken");
const fs = require("fs");
#Injectable()
export class RsaTokenAuthService {
loadKey(path) {
if (!path) {
return null;
}
try {
return fs.readFileSync(path, { encoding: 'utf8' });
} catch (err) {
console.error(err);
}
}
exportRSAKey() {
return this.loadKey(process.env.JWT_RSA_PRIVATE_KEY);
}
getSigningConfig(subject) {
const rsaSignOptions = {
algorithm: 'RS256',
expiresIn: 15 * 60, // expires in 15min
issuer: process.env.ISSUER,
audience: `${process.env.FORM_API_URL}`,
};
if (this.exportRSAKey()) {
return {
options: { ...rsaSignOptions, subject },
secret: this.exportRSAKey(),
};
}
}
buildToken(subject, payload = {}) {
const sign = this.getSigningConfig(subject);
return jwt.sign(payload, sign.secret, sign.options);
}
}
ServerB:
Again I have created two RSA files in serverB repo
serverB-private.key
serverB-public.key
When I run both the server on localhost and having different port and try to verify the token it throws the invalid token error.
Here is my verification code on serverB.
const fs = require('fs')
const jwt = require("jsonwebtoken");
const rsaVerifyOptions = {
// only allow RSA + SHA256
algorithms: ["RS256"],
}
function loadKey(path) {
if (!path) {
return null;
}
try {
return fs.readFileSync(path, { encoding: 'utf8' });
} catch (err) {
console.error(err);
}
}
function exportRSAKey() {
return loadKey(process.env.JWT_RSA_PUBLIC_KEY);
}
function getVerifyConfig(subject) {
if (exportRSAKey()) {
return {
options: rsaVerifyOptions,
secret: exportRSAKey(),
};
}
}
function verifyToken(token) {
const { options, secret } = getVerifyConfig();
try {
return jwt.verify(token, secret, options);
} catch (err) {
// in prod, don't use console.log because it is synchronous
console.log(err);
return null;
}
}
function decodeAndVerify(req, res, next) {
// Authorization: Bearer <jwt-token>
// 1. extract Authrorization header
const authorizationHeader = req.get("Authorization");
if (!authorizationHeader) {
return res.status(401).json("missing token");
}
// 2. extract the JWT
// "Bearer: my-jwt-token"
const token = authorizationHeader.substring(7);
// 3. decode token
const decodedToken = verifyToken(token);
if (!decodedToken) {
return res.status(401).json("invalid token");
}
req.token = decodedToken;
next();
}
module.exports = {
verifyToken,
decodeAndVerify
}
Please help me. Am I doing wrong somewhere ? Thanks in advance :)
Related
Good day developers, recently as been working in this node js app, and when implementing jwt library i got an error related to tghe verify() method in this jwt repo.
Kind of :
return secretCallback(null, secretOrPublicKey);
^
TokenExpiredError: jwt expired
The repo is structured in several folders:
controllers
middlewares
helpers
socket controllers
On my middleware folder, sepecifically in a file related to jwt validation i settled this:
file middleware jwt-validation
export {};
const { request, response } = require("express");
const User = require("../models/user-model");
const jwt = require("jsonwebtoken");
const jwtValidator = async (req = request, res = response, next) => {
const token_response = req.header("token-response");
if (!token_response) {
return res.status(401).json({
message: "Not valid token .You don't have authorization",
});
}
try {
const payload = await jwt.verify(token_response, process.env.SECRETKEYJWT)
const userAuth = await User.findById(payload.id);
if (userAuth.userState != true) {
return res.status(401).json({
message: "User inhabilitated",
});
}
if (!userAuth) {
return res.status(404).json({
message: "User not found",
});
}
req.user = userAuth;
next();
} catch (error) {
return res.status(500).json({
message: "Not valid token.Error 500",
});
}
};
module.exports = { jwtValidator };
file middleware jwt-validation-sockets
import { UserSchema } from "../interfaces";
const jwt = require("jsonwebtoken");
const User = require("../models/user-model");
const jwtValidatorRenew = async (
token: string = ""
): Promise<UserSchema | null> => {
if (token == "" || token.length < 10 || token == undefined) {
return null;
}
const payloadToken = await jwt.verify(token, process.env.SECRETKEYJWT)
const userTokenDecoded: UserSchema = User.findById(payloadToken.id);
if (userTokenDecoded?.userState) {
return userTokenDecoded;
} else {
return null;
}
};
module.exports = { jwtValidatorRenew };
file helper jwt-generator
const { request, response } = require("express");
const jwt = require("jsonwebtoken");
const createJWT = async (id = "", nickname = "") =>
return new Promise((resolve, reject) => {
const payloadInJWT = { id, nickname };
jwt.sign(
payloadInJWT,
process.env.SECRETKEYJWT,
{
expiresIn: 3600,
},
//calback
(error:any, token:string) => {
if (error) {
alert(error)
reject("Error creating token ");
} else {
resolve(token);
}
}
);
});
};
module.exports = { createJWT };
file socket-controller
const { jwtValidatorRenew } = require("../middlewares/jwt-validation-socket");
const { userData } = require("../helpers/helper-user-schema-data");
const { Socket } = require("socket.io");
const User = require("../models/user-model");
const {
ChatMessage,
Message,
MessagePrivate,
GroupChat,
} = require("../models/chat-model");
const chatMessage = new ChatMessage()
const socketController = async (socket = new Socket(), io) => {
const user = await jwtValidatorRenew(
socket.handshake.headers["token-response"]
);
try {
...some sockets flags
} catch (error) {
socket.disconnect();
}
};
module.exports = { socketController };
I have the code below for token generation. I follow this tutorial. When I use postman to test the code with a registered username and password, it returned the JsonWebTokenError: jwt malform. On close review, I discovered that the error comes from jwt.verify section.
What do I need to adjust so that the token can be generated?
How can I configure it so that when a users makers a log in request, say he's passing his username and password, along with email and password, the client must pass a client identity ( [payload + clientID] ), for the server to know for whom the token is to be signed.
Is there any special value I need to pass in Postman Header to test the working of the token system?
CODE EDITED
const jwt = require('jsonwebtoken');
const fs = require('fs');
// PRIVATE and PUBLIC key
const publicKey = fs.readFileSync(__dirname + '/public.key', 'utf8');
const privateKey = fs.readFileSync(__dirname + '/private.key', 'utf8');
// Returns secret only known to server at runtime
exports.getSecret = () => {
const secret = require('../config/secret.json').secret;
return secret;
};
// Returns token
exports.getToken = (payload, signOptions) => {
if (!signOptions) {
signOptions = {
issuer: "Myself",
expiresIn: "30d",
algorithm: "RS256"
}
};
const token = jwt.sign(payload, privateKey, options);
return (token);
};
// Returns result of token validation
exports.validateToken = (token, verifyOptions) => {
if (!verifyOptions) {
verifyOptions = {
issuer: "Myself",
expiresIn: "30d",
algorithm: "RS256"
}
};
try {
return jwt.verify(token, publicKey, verifyOptions);
} catch (err) {
return err;
}
};
// Returns validation result of token
exports.token_post = (req, res) => {
res.send(this.validateToken(req.header.Authorization, this.getSecret()));
};
exports.hasPermission = (token, resource) => {
const result = this.validateToken(token, this.getSecret());
console.log(result);
if (result.name === 'JsonWebTokenError') {
return false;
} else if (result.permissions) {
let permissionSet = new Set(result.permissions);
console.log('permissions in token', JSON.stringify(permissionSet));
return permissionSet.has(resource);
} else {
return false;
}
};
You're not feeding the good arguments to this.validateToken.
You should give it the verifyOptions instead of this.getSecret().
By the way, this is not a secret but a public key so I don't recommend using this name. And you are reading the public key twice.
Your hasPermission function should look like that:
exports.hasPermission = (token, resource, $Options) => {
const result = this.validateToken(token, $Options);
console.log(result);
if (result.name === 'JsonWebTokenError') {
return false;
} else if (result.permissions) {
let permissionSet = new Set(result.permissions);
console.log('permissions in token', JSON.stringify(permissionSet));
return permissionSet.has(resource);
} else {
return false;
}
};
Don't forget to change the code that call that function. $Options is the one you use to sign your token, the signOptions in the tutorial.
I want to use JWT to generation token to authenticate users. When I use postman to test the code with a registered username and password, it returned the JsonWebTokenError: jwt malform. On close review, I discovered that the error comes from jwt.verify section.
Do I need to pass a payload value to generate the token?
How can I configure it so that when a users makers a log in request, say he's passing his username and password, along with email and password, the client must pass a client identity ( [payload + clientID] ), for the server to know for whom the token is to be signed.
Is there any special value I need to pass in Postman Header to test the working of the token system?
THE CODE
const jwt = require('jsonwebtoken');
const fs = require('fs');
// PRIVATE and PUBLIC key
const publicKey = fs.readFileSync(__dirname + '/public.key', 'utf8');
const privateKey = fs.readFileSync(__dirname + '/private.key', 'utf8');
// Returns secret only known to server at runtime
exports.getSecret = () => {
const secret = require('../config/secret.json').secret;
return secret;
};
// Returns token
exports.getToken = (payload, signOptions) => {
if (!signOptions) {
signOptions = {
issuer: "Myself",
expiresIn: "30d",
algorithm: "RS256"
}
};
const token = jwt.sign(payload, privateKey, options);
return (token);
};
// Returns result of token validation
exports.validateToken = (token, verifyOptions) => {
if (!verifyOptions) {
verifyOptions = {
issuer: "Myself",
expiresIn: "30d",
algorithm: "RS256"
}
};
try {
return jwt.verify(token, publicKey, verifyOptions);
} catch (err) {
return err;
}
};
// Returns validation result of token
exports.token_post = (req, res) => {
res.send(this.validateToken(req.header.Authorization, this.getSecret()));
};
The remaining part of the code below is linked to a permission file that is used to validate users. The error shows that the argument is not getting to the this.validateToken part.
exports.hasPermission = (token, resource) => {
const result = this.validateToken(token, this.getSecret());
console.log(result);
if (result.name === 'JsonWebTokenError') {
return false;
} else if (result.permissions) {
let permissionSet = new Set(result.permissions);
console.log('permissions in token', JSON.stringify(permissionSet));
return permissionSet.has(resource);
} else {
return false;
}
};
I EDITED JWT.(SIGN) FUNCTION AS FOLLOWS
return jwt.sign(payload, privateKey, signOptions, function (error, token){
if(error) {
return done(new JsonWebTokenError('error in generating token: ' + error.message));
} else {
console.log("Token :" + token);
}
});
};
jwt.sign() return a callback function like this
jwt.sign(payload, expiration, function (error, token) {
if(error) {
// Faild Error
} else {
// Get token and do continue
}
});
I tried to implement jwt token generation in node js.I got jwt token but how to validate token using node js crud operation.but I got token jwt verfiy code using callback function.without call back function used to implement async/awit function implement.
index.js
router.post('/', async (req, res) => {
(async function() {
try {
await client.connect();
console.log("Connected correctly to server");
const db = client.db('olc_prod_db');
//Validation
const { error } = validate.validate(req.body);
if (error)
{
return res.status(400).send(error.details[0].message);
}
else
{
const check_login = req.body
const r = await db.collection('UserRegistration').find().toArray();
r.forEach(element => {
if(element['username'] == check_login['username'])
{
const token = get_token.validate(req.body)
res.send({"token ":token})
}
else
{
return res.send(401,"Un Authorized");
}
});
}
client.close();
} catch(err) {
console.log(err.stack);
}
})();
});
authtoken.js
var jwt = require('jsonwebtoken')
function get_token(userdata)
{
var accessToken = jwt.sign(userdata, 'secretkey', {
//Set the expiration
expiresIn: 3600 //we are setting the expiration time of 1 hr.
});
//send the response to the caller with the accesstoken and data
console.log('Authentication is done successfully.....');
return accessToken
}
exports.validate = get_token;
const jwt = require('jsonwebtoken')
const config = require('../../config/default')
function verifyjwt(req,res,next){
const token = req.headers['authorization']
if(!token) return res.status(401).json('Unauthorize user')
try{
const decoded = jwt.verify(token,config.secret);
req.user = decoded
next()
}catch(e){
res.status(400).json('Token not valid')
}
}
module.exports = verifyjwt
const CONST = require('../../config')
exports.validJWTNeeded = (req, res, next) => {
if (req.headers['authorization']) {
try {
let authorization = req.headers['authorization'].split(' ');
if (authorization[0] !== 'Bearer') {
return res.status(401).send('invalid request'); //invalid request
} else {
req.jwt = jwt.verify(authorization[1], CONST.SECRET);
return next();
}
} catch (err) {
return res.status(403).send(); //invalid token
}
} else {
return res.status(401).send('invalid request');
}
}
I am fairly new to NodeJS and Socket.io. I have my main file as index.js which calls a helper function verifyToken, but it does not wait for the return value from the helper function and continues forward.
I have added console.log to track execution flow as below:
File: index.js
socket.on('authenticate', function(data, ack) {
var employeeID = data.employeeID;
var token = data.Token;
var tokenHelper = require("#helpers/tokenHelper"); //#helpers is an alias by using the module-alias package for helpers folder
//Connect with helper to verify token
var isValid = tokenHelper.verifyToken(employeeID, token);
if(isValid) {
socket.auth = true;
ack("Authenticated");
console.log("Authenticated");
}
else {
ack("Unauthorised");
console.log("Unauthorised");
}
});
File: tokenHelper.js
var mysqlDatabase = require("#config/mysql");
module.exports = {
verifyToken: function(employeeID, token, response) {
var publicKey = fs.readFileSync(path.resolve("SOME_PATH"), "utf8");
var isValid = false;
//Verify options
var verifyOptions = {
issuer: issuer,
subject: subject,
audience: audience,
expiresIn: expiresIn,
algorithm: ["RS256"]
};
//Extract data from received payload
var receivedPayload = jwt.verify(token, publicKey, verifyOptions);
var receivedEmailAddress = receivedPayload.sub;
var receivedEmployeeID = receivedPayload.EmployeeID;
console.log("Received email: " + receivedEmailAddress);
console.log("Received id: " + receivedEmployeeID);
console.log("Employee id: " + employeeID);
//SQL Query to check EmployeeID in the database, verification of token is successful if entry is found in the database
if(results !== null) {
isValid = true;
console.log("Verification successful");
}
return isValid;
}
};
Current console log:
Received email: user#test.com
Received id: 1
Employee id: 1
Unauthorised
Verification successful
Expected console log:
Received email: user#test.com
Received id: 1
Employee id: 1
Verification successful
Unauthorised
File: tokenHelper.js
module.exports = {
verifyToken: async (employeeID, token, response) => {
try {
const publicKey = fs.readFileSync(path.resolve('SOME_PATH'), 'utf8');
let isValid = false;
const verifyOptions = {
issuer: issuer,
subject: subject,
audience: audience,
expiresIn: expiresIn,
algorithm: ['RS256'],
};
const receivedPayload = await jwt.verify(token, publicKey, verifyOptions);
const receivedEmailAddress = receivedPayload.sub;
const receivedEmployeeID = receivedPayload.EmployeeID;
console.log(
`Received email: ${receivedEmailAddress}, Received id: ${receivedEmployeeID} and Employee id: ${employeeID}`
);
if (results !== null) {
isValid = true;
console.log('Verification successful');
}
return isValid;
} catch (error) {
console.error(error);
}
},
};
File: index.js
const tokenHelper = require('#helpers/tokenHelper');
socket.on('authenticate', async (data, ack) => {
try {
const employeeID = data.employeeID;
const token = data.Token;
var isValid = await tokenHelper.verifyToken(employeeID, token);
if (isValid) {
socket.auth = true;
ack('Authenticated');
console.log('Authenticated');
} else {
ack('Unauthorised');
console.log('Unauthorised');
}
} catch (error) {
console.error(error);
}
});
You missed the callback in the code. Please replace your code with the following and let me know if something went wrong.
Token Helper:
module.exports = {
async verifyToken = (employeeID, token, response) => {
const publicKey = fs.readFileSync(path.resolve("SOME_PATH"), "utf8");
let isValid = false;
const verifyOptions = {
issuer: issuer,
subject: subject,
audience: audience,
expiresIn: expiresIn,
algorithm: ['RS256']
};
const receivedPayload = await jwt.verify(token, publicKey, verifyOptions);
const receivedEmailAddress = receivedPayload.sub;
const receivedEmployeeID = receivedPayload.EmployeeID;
console.log(`Received email: ${receivedEmailAddress}, Received id: ${receivedEmployeeID} and Employee id: ${employeeID}`);
if (results !== null) {
isValid = true;
console.log('Verification successful');
}
return isValid;
}
};
File: index.js
const tokenHelper = require("#helpers/tokenHelper");
socket.on('authenticate', async (data, ack) => {
const employeeID = data.employeeID;
const token = data.Token;
var isValid = await tokenHelper.verifyToken(employeeID, token);
if (isValid) {
socket.auth = true;
ack('Authenticated');
console.log('Authenticated');
} else {
ack('Unauthorised');
console.log('Unauthorised');
}
});