I'm new to Node.js, reading Node.js Essentials by Fabian Cook. When trying the code in authentication with JWT, I got an NULL from jwt.decode( token ), but the token can be parsed by the Debugger on jwt.io. What's wrong with the code?
var Passport = require( 'passport' );
var LocalStrategy = require( 'passport-local' ).Strategy;
var Express = require( 'express' );
var BodyParser = require( 'body-parser' );
var jwt = require( 'jsonwebtoken' );
var Crypto = require ( 'crypto' );
var users = {
zack: {
username: 'zack',
password: '1234',
id: 1,
},
node: {
username: 'node',
password: '5678',
id: 2,
},
}
var localStrategy = new LocalStrategy({
usernameField: 'username',
passwordField: 'password',
},
function(username, password, done) {
user = users[ username ];
if ( user == null ) {
return done( null, false, { message: 'Invalid user' } );
};
if ( user.password !== password ) {
return done( null, false, { message: 'Invalid password' } );
};
done( null, user );
}
)
Passport.use( 'local', localStrategy );
var app = Express();
app.use( BodyParser.urlencoded( { extended: false } ) );
app.use( BodyParser.json() );
app.use( Passport.initialize() );
var generateToken = function( req, res ) {
var payload = {
id: user.id,
username: user.username
}
var secret = user.secret || Crypto.randomBytes( 128 ).toString( 'base64' );
var token = jwt.sign( payload, secret );
user.secret = secret;
return token;
};
var generateTokenHandler = function ( req, res ) {
var user = req.user;
var token = generateToken( user );
res.send( token );
};
app.post(
'/login',
Passport.authenticate( 'local', { session: false } ),
generateTokenHandler
);
var BearerStrategy = require( 'passport-http-bearer' ).Strategy;
var verifyToken = function( token, done ) {
var payload = jwt.decode(token);
if ( payload == null ){
return done( null, false );
}
console.log(payload);
var user = users[ payload.username ];
if ( user == null ||
user.id !== payload.id ||
user.username !== payload.username ) {
return done( null, false );
}
jwt.verify( token, user.secret, function ( error, decoded ) {
if ( error || decoded == null ) {
return done( error, false );
}
return done( null, user );
})
}
var bearerStrategy = new BearerStrategy( verifyToken )
Passport.use( 'bearer', bearerStrategy );
app.get(
'/userinfo',
Passport.authenticate( 'bearer', { session: false } ),
function ( req, res ) {
var user = request.user;
res.send( {
id: user.id,
username: user.username
});
}
);
app.listen( 3000, function() {
console.log( 'Listening on 3000' );
});
Here is a token I got from the code F.Y.I.
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpZCI6MSwidXNlcm5hbWUiOiJ6YWNrIiwiaWF0IjoxNDU5MDAzMTYxfQ.rhqOX0ICRvivNCwwLNsu5KizNPLQTKPVEqfCuxtII90~
By design, anyone can decode a JWT and read the contents of the header and payload sections. But we need access to the secret key used to create the signature to verify a token's integrity.
jwt.verify(token, secretOrPublicKey, [options, callback]) (Synchronous) If a callback is not supplied, function acts synchronously. Returns the payload decoded if the signature is valid and optional expiration, audience, or issuer are valid. If not, it will throw the error.
To visually inspect a JWT, visit JWT.io or use the JWT Debugger Chrome Extension). The JWT token signature is generated using a signing algorithm.
If you are using this,
var decoded = jwt.decode(token, {complete: true});
or
var payload = jwt.decode(token);
Check if the token is similar to this -> Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpZCI6MSwidXNlcm5hbWUiOiJ6YWNrIiwiaWF0IjoxNDU5MDAzMTYxfQ.rhqOX0ICRvivNCwwLNsu5KizNPLQTKPVEqfCuxtII90~
Then, before you pass the token token to the jwt.decode, we have to remove the Bearer from the token and then pass it. Bearer is an authorization type and needs to be removed. For example:
var newToken = token.substring(7, bearerString.length); // used to remove the Bearer string and space from the token so that it consists of only header,payload and signature.
var decoded = jwt.decode(newToken, { complete: true });
or
var payload = jwt.decode(newToken);
I believe the issue is that when using jwt.decode while also having a secret key you will need to pass in an option to the decode call with complete set to true:
From the jwt docs:
// get the decoded payload ignoring signature, no secretOrPrivateKey needed
var decoded = jwt.decode(token);
// get the decoded payload and header
var decoded = jwt.decode(token, {complete: true});
console.log(decoded.header);
console.log(decoded.payload)
https://github.com/auth0/node-jsonwebtoken
Apparently it might be best to use jwt.verify here:
Warning: This will not verify whether the signature is valid. You should
not use this for untrusted messages. You most likely want to use jwt.verify instead.
I was having this problem too, ends up I needed to throw a JSON.parse() around the token...
var decoded = jwt.decode(JSON.parse(token))
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With