I am using userfront.js that will create a token on their side of things.
https://userfront.com/test/dashboard/jwt/usage
to verify the token I am using the token creatred from userfront.js and public key from my user front account.
keep getting this error though: JsonWebTokenError: invalid signature
here is my code.
Middlware.js
var jwt = require("jsonwebtoken")
const key = Buffer.from(process.env.PUBLIC_KEY_64, "base64").toString("ascii")
const isAdmin = async (req, res, next) => {
try {
const token = req.cookies["access.pn4qd8qb"]
const payload = jwt.verify(token, key, { algorithm: "RS256" })
console.log({ payload })
next()
} catch (error) {
console.log(error)
}
}
module.exports = { isAdmin }
Note: not the full key not sure if I should post full key online
{
key: '-----BEGIN PUBLIC KEY-----\n' +
'MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAyYWU4VNpO0GecWJhsvTH\n' +
'pXLyOrCvXd9m0gkWCnw67HleZTfRt2xqegwY0d/oMg8zSP9kS4Uf6hRX4uu3E0hF\n' +
'EoqEgAmIohoedBN+HAFNoM3ac1svv30efLeVwg5CHEkX3JquduIdOqowte6tNZU/\n' +
'IVFUk8lyDk4RXIR/ncJ0AG+cqMjyj5JVVimAWNltX8TQKlhpnjh3DCMM2QiG81+r\n' +
'QfCleCF1Ju91CCKKHQnPcT6qIKID/CoXThs7FRFUK5kWpSz/N3V5+KANpNBZUk8E\n' +
'ZJncGqXru7O6xkWqh3uy16C1fQcyVkUdM32vbKiEjhHs/p8M4simHYT1TOZw+WP0\n' +
'6wnWsrlWEBnWCUZX9ekzLwDR+DgOKIPHvIZsXdyQXi+JzhoojgW5D32nK7ebcX1B\n' +
'XXO7+HUsvbIPgFaxlyN+k9UCAwEAAQ==\n' +
'-----END PUBLIC KEY-----'
}
{
token: 'eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6ImE1NTNiNGM1LTAwNDMtNGFkNi05MjgxLTVhOTdhMjJmN2FlOSJ9.eyJtb2RlIjoidGVzdCIsInRlbmFudElkIjoicG40cWQ4cWIiLCJ1c2VySWQiOjIzLCJ1c2VyVXVpZCI6IjI5MDc0MzQ3LWM4MjYtNGY4NS1iMTNmLTgzOTQ4YjRkZjVmMSIsImlzQ29uZmlybWVkIjpmYWxzZSwiYXV0aG9yaXphdGlvbiI6eyJwbjRxZDhxYiI6eyJyb2xlcyI6WyJhZG1pbiIsInN1cHBvcnQiLCJtZW1iZXIiXX19LCJpc3MiOiJ1c2VyZnJvbnQiLCJzZXNzaW9uSWQiOiI4ZDU4ZDhjOC0wN2VmLTQwNzQtYjhjMS1lN2JiYmFkY2RjZjEiLCJpYXQiOjE2NjM0ODgwMDMsImV4cCI6MTY2NDA5MjgwM30.pa1egSWlsdZ7INz282OBgNVl6x1Hv5x7oPgwdPPvpynNjWz6NfgQOrBIRZOJuYCmzuS4wOUo3N7gzGptVayjPH6PUrak4zgWZlbUfABw3Z4gJ9OcgEl28i_pgVMZm-uEsAuDSc_YF79hj4SgJ-hf9Ntww3AFUKq1VvRGyTncepr2GLQUjrDR0AaU4g_K_z809gPXyXfaIvvTQJLWr0WvNtqSE0yda8tyPBiw5RkeYye7RXcstBHSZVNFNeQ9oT4ru9830RGZUqniWbF5KTwGI4v7mrqFVPSJ0xGagk3YkZEKYInJEgHbhXB5CKuJB6QvCz8bixhnzk0Q2TYD5Oa7ZIHW_OQyvUuok2MXBGaBaUjQy24I_TZwl0n8Ob2HbOy5RXULIjBrwUPyjaKkbIx6bg6e25314Cj1vkGdwmacF939hdE6UXIjUMKXa-tDimOu3xuUlVH9ZwGe5UGgh_lPITwc2m4RAW2sEukwQ19zai18mSx1cOny7CUYWwHx8BYPqJbazVrYviQhRkf7BV2U-SI0ABS_hJEtAFQiSMvf8U8uq52LoSfeK0PWn8omUYWZIzBUUDfQy4AMxXWCKY0P9yJsQfxNN1oNs8XNs4wmhy9SDvGQws1yIHpJ8Hb1CrskVdBNEczUeJnRwXqaJGJu83uZl2glBkH29JpsqROPJHI'
}
any idea what i'm doing wrong?
Related
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 :)
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 have some key id that I'll use to verify a token, but it's hardcoded and I don't want it to be so.
I'm using the jwks-rsa library to fetch the key from an API endpoint and crack the token open for verification, however the fetch is done in the jwks-rsa client object's option property and because of that I'm unsure how to go about storing the fetched 'kid' value in a variable. It's hard to explain, so I'll show some code.
'use strict'
require('dotenv').config()
const jwt = require('jsonwebtoken')
const jwks = require('jwks-rsa')
const TOKEN_SECRET = 'someweirdtokenstring'
function (request, response, next) {
const client = jwks({
jwksUri: process.env.TOKEN_KEY_PUBLIC //this is the API endpoint; responds with a json key
})
const kid = 'something' //I don't want to hardcode this
client.getSigningKey(kid, (error, key) => {
if (error) throw error
const signingKey = key.publicKey || key.rsaPublicKey
const token = TOKEN_SECRET
if (!token) return response.status(401).send('Access denied')
try {
const verified = jwt.verify(token, signingKey, { algorithms: ['RS256'] })
request.user = verified
next()
} catch (error) {
response.status(400).send({ message: error.message, stack: error.stack })
}
})
}
EDIT: It's not super-clean code, but that's not the point here.
IMO, you should create the JWT with kid in its header. And simply decode only to read the header.
Here is small snippet,
'use strict'
require('dotenv').config()
const jwt = require('jsonwebtoken')
const jwks = require('jwks-rsa')
const TOKEN_SECRET = 'someweirdtokenstring'
function (request, response, next) {
const client = jwks({
jwksUri: process.env.TOKEN_KEY_PUBLIC //this is the API endpoint; responds with a json key
})
var decoded = jwt.decode(token, {complete: true});
const kid = decoded.kid
client.getSigningKey(kid, (error, key) => {
if (error) throw error
const signingKey = key.publicKey || key.rsaPublicKey
const token = TOKEN_SECRET
if (!token) return response.status(401).send('Access denied')
try {
const verified = jwt.verify(token, signingKey, { algorithms: ['RS256'] })
request.user = verified
next()
} catch (error) {
response.status(400).send({ message: error.message, stack: error.stack })
}
})
}
Im getting a JWT from Auth0. I can decode it with the following middleware function on my Node server (using https://www.npmjs.com/package/jwt-node)
function authoriseToken(req, res, next) {
const token = req.headers.authorization.replace('Bearer ', '');
const decodedToken = jwt.decode(token);
console.log('decodedToken ', decodedToken);
next();
}
How can I verify the token? I keep getting an error JsonWebTokenError: invalid algorithm
function authoriseToken(req, res, next) {
const token = req.headers.authorization.replace('Bearer ', '');
const verifyedToken = jwt.verify(token, "my-secrete");
console.log('verifyedToken ', verifyedToken);
next();
}
Im not sure if I should be using a secrete or a jwksUri or what the difference is
Here is the actual token:
eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6Ik16QkJRa1k0T0RRNE9VWTJORVZGT1VJNFFrSXpNRUZDT0RaQ01VSTBOVGN4TWpVeU1UYzNRdyJ9.eyJpc3MiOiJodHRwczovL25vbWFkZ3JvdXBzLmF1dGgwLmNvbS8iLCJzdWIiOiJhdXRoMHw1YjMxMDhkNjc4NzFkNTBkZTA0Njc2NWEiLCJhdWQiOiJTZkQyMEVPZVdRbHlnWXIwUHNXODdtYjd0OGFBOFI2NiIsImlhdCI6MTUzMDAxMzQwMCwiZXhwIjoxNTMwMDQ5NDAwLCJhdF9oYXNoIjoiUi1mRGc3SVRzUUdqemplX3VUR01RdyIsIm5vbmNlIjoiQnN-VmZxNzdtNERuaTJ1LjlIUVJlSEpzeHA4UjF2aDcifQ.CwZb6j3DshbD5M-OWBQpc10EIpAd3D-TuZTA1p7alePobSRVM7bE9Yzr5DIRyc2YUQZQ_OBwVLfFPq0pEBTWFYq2O43FJZ726xP1zK7Ty4PvAoLe4Cx6E0Ow8V8Ymo87XCIKX8J1ndg47q5glKzsnSMToutEWRZ2lnxJyirD4m4EwFykDF8DalA1sWvqnYXEwWraY3VLroqyZH2nkeLDcpcMdJ0tWwmzldwi7ym9OmegV5GBl7F6BgrZNIJfdoT88Rs4AKzogJyJuVQ1XlD7Up_nYlAKBmRMgkFt3t_4iq7pTkgdrWl1tXuJQsnmkkVH6_yffNYrWDnuirWwTCG4XQ
verify takes algorithms option in third parameter, adjust value with the correct one.
You can find it under applications > advanced settings > oauth > JsonWebToken Signature Algorithm
Expanding on Gabriel Bleu's answer here is my complete code:
const jwt = require('jsonwebtoken');
const pemCert = `-----BEGIN CERTIFICATE-----
// <<CERT CODE HERE>>
-----END CERTIFICATE-----`;
function authoriseToken(req, res, next) {
const token = req.headers.authorization;
// If there is no token user is not logged in
if (!token || token.length === 0) {
next();
return;
}
// If there is a token then add it to the res
const tokenCrop = token.replace('Bearer ', '');
const decodedToken = jwt.verify(tokenCrop, pemCert, { algorithm: 'RS256' });
const userId = decodedToken.sub.replace('auth0|', '');
req.authUserId = userId;
next();
}
module.exports = authoriseToken;