I use https://github.com/jwtk/jjwt to encode and sign a token as follow:
Jwts.builder()
.setSubject(authentication.getName())
.claim(AUTHORITIES_KEY, authorities)
.signWith(SignatureAlgorithm.HS512, "my-secret-token-to-change-in-production")
.setExpiration(validity)
.compact();
then decode in node.js as follow:
var decoded = jwt.verify(req.get('Authorization'), 'my-secret-token-to-change-in-production', { algorithms: ['HS512'] });
and get the error:
JsonWebTokenError: invalid signature
Using jwt.decode I get the token content without problem.
Am I doing something wrong?
I'd love to give you a hand with this. Would you be able to provide an example token and the secret you used to sign it so I can take a look. With what you've provided, hard to say - from looking at _jjwt_ your example should be throwing since "my-secret-token-to-change-in-production" is not base64.
hum, the base64 issue sounds like a good lead, but I couldn't verify the signature with the secret encoded in base64 like this:
var decoded = jwt.verify(req.get('Authorization'), new Buffer('my-secret-token-to-change-in-production').toString('base64'), { algorithms: ['HS512'] });
Here's a token generated by jjwt with my favorite secret my-secret-token-to-change-in-production:
eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJhZG1pbiIsImF1dGgiOiJST0xFX0FETUlOLFJPTEVfVVNFUiIsImV4cCI6MTQ2Nzc3MzQ5Nn0.v0Gfc9fKDQfkDningjmObkD5-EcbfWy5vuvuOimTV032iCoOaaQtCsZxQC78JbLbeQNLUA3UaQnuLgvwwqLmIg
I'm having the same problem with these libraries.
I used the debugger at jwt.io to decode it, which gave the option to provide the secret as base64 - which worked on the debugger, but not in my program.
However, I couldn't get the debugger to work with your values, @nodje
Did anyone get a solution to this yet? I am signing the JWT in Java (io.jsonwebtoken) and trying to "unsign" using this npm library in a separate node.js app. As already mentioned by others, the token decodes fine, so obviously the data has not been corrupted - it just seems that the sign/unsign procedures do not match up.
@DaleWebb @michaelcbarr would either of you be able to provide some actual code you're trying to do this with?
no problem - this was borrowed form a tutorial - can't find the link but will credit if I find it :D
var jwt = require('jsonwebtoken');
jwt.verify(token, superSecret, function(err, decoded) {
if (err) {
return res.json({ success: false, message: 'Failed to authenticate token.' });
} else {
// if everything is good, save to request for use in other routes
req.decoded = decoded;
next();
}
});
The token is created in Java with code similar to this:
private String generateToken(Map<String, Object> claims) {
return Jwts.builder()
.setClaims(claims)
.setExpiration(this.generateExpirationDate())
.signWith(SignatureAlgorithm.HS512, secret)
.compact();
}
@michaelcbarr Might you be able to provide the java code where you're signing?
added above!
NB: I have tried SignatureAlgorithm.HS256 but makes no difference
@michaelcbarr No, I haven't found a solution to this
@omsmith Can you send me a test token which works for you? I tried the one above with that secret but no luck!
Me neither @DaleWebb - have you tried any other workarounds? I had thought about manually encrypting the token and use the decode function rather than verify but it seems hacky :(
private static String generateToken(Account account, App app) {
return Jwts.builder()
.setSubject(account.getId())
.claim("app_id", app.getSlug())
.setExpiration(new Date(new Date().getTime() + TOKEN_LIFETIME))
.signWith(SignatureAlgorithm.HS512, PersonaApplication.getSecret())//the secret is a String, not base64 encoded.
.compact();
}
@michaelcbarr If I cannot get a solution before we have to ship the feature, I'm going to create a route on the gateway that creates the token to give the information back.
@nodje @DaleWebb @michaelcbarr
I'm not going to take the time to dig into exactly what the java library is doing by default (I don't work with Java usually, needed to install Eclipse and figure and Maven and things :)). However, the issue is the string secret. By calling getBytes("UTF-8") on your secret and providing signWith() with the byte[], everything will work out.
package jwt_test.foo;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import java.io.UnsupportedEncodingException;
import java.util.Date;
/**
* Hello world!
*
*/
public class App
{
private static String SECRET = "HelloWorld!";
public static void main( String[] args ) throws UnsupportedEncodingException
{
System.out.println( makeToken() );
}
private static String makeToken() throws UnsupportedEncodingException {
return Jwts.builder()
.setSubject("foo")
.claim("bar", "baz")
.signWith(SignatureAlgorithm.HS512, SECRET.getBytes("UTF-8"))
.compact();
}
}
'use strict';
const JWT = require('jsonwebtoken');
const SECRET = 'HelloWorld!';
const JAVA_JWT = 'eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJmb28iLCJiYXIiOiJiYXoifQ.1MOXGiwGTFLU7-YMvOe2_q2ZRUHAMCVS7pbnOkRKCFV1HIvY8odBaqWVCQRuT2RUbKtGgA2elFRsuka4K1KP7A';
JWT.verify(JAVA_JWT, SECRET, { algorithms: ['HS512'] })
@DaleWebb @omsmith
AWESOME WORK!
I can confirm that this works perfectly for me - just need to remember to convert the SECRET string to bytes on Java verify method.
claims = Jwts.parser()
.setSigningKey(secret.getBytes("UTF-8"))
.parseClaimsJws(token)
.getBody();
(Also need to catch the UnsupportedEncodingException!)
Think I would still be scratching my head next month without your help - thanks again @omsmith !
Mb
Great! I can confirm that this solution works too.
An author of JJWT here...
FWIW, cryptographic signatures are _always_ computed with byte array keys - never strings. You can get the UTF-8 bytes of a String as demonstrated above, but that only masks what could be a very problematic cryptographic weakness (I'm not saying those in this thread are experiencing that weakness - I'm just raising that this could happen to anyone that might not understand what is going on).
Digital signature keys (again, byte arrays), should ideally never be based on simple strings like 'my secret' or 'my password'. Or, at the very least, if a simple password should be used as a signing key, it is almost always better to send it through a key-derivation algorithm (like PBKDF2) and then use _that_ resulting output as the signature key. This ensures sufficient cryptographic entropy (randomness) that short, human-readable strings don't (and which are therefore risky).
Signing keys should ideally always be:
Number 2 is why JJWT provides the MacProvider.generateKey method - to ensure you always have keys of sufficient strength for the algorithm chosen. You can then easily base64 the result:
SecretKey key = MacProvider.generateKey(SignatureAlgorithm.HS256);
String base64Encoded = TextCodec.BASE64.encode(key.getEncoded());
This is why JJWT expects Base64 by default - because if you do these best practices, you'll always end up with a byte array key (e.g. key.getEncoded()). And if you have a byte array key, the most common way to turn that into a string (e.g. for configuration) is to Base64-encode that byte array.
Finally, note that TextCodec.BASE64.decode(myKey) does NOT produce the same byte array (key) as myKey.getBytes('UTF-8'). The latter is usually incorrect in cryptographic contexts.
That means that my-secret-token-to-change-in-production.getBytes("UTF-8") might represent a weakened signing key, and as a result, shouldn't be used. I recommend dumping that current key and generating a new one with strong cryptographic guarantees as shown above (e.g. using JJWT) and ensuring your Node library base64-decodes your string correctly.
HTH!
I'm having a similar issue. I'm generating a key elswhere, and trying to validate it using this library. It validates fine in other services using libraries from different languages.
Looking at this issue and the answers to it, am i right in assuming that the node library generates a byte array for validating by UTF-8 decoding the supplied secret string? Is there any way to get it to base64url-decode the key (as i believe would be the "proper" way for the reasons described by the previous comment)? I've tried having a dig through the code but haven't come up with anything.
EDIT: Just figured it out. Passing new Buffer(secret, 'base64') instead of the string in did the trick!
The solution @mgkeen provided in his edit worked for me.
If this is the appropriate thing to do can someone update the documentation? i.e.
var tokenDecoded = jwt.verify(token, new Buffer(MYSECRET, 'base64'), function(err, decoded) {
Still getting this error when trying the following:
var user = { username: 'batman' };
// Signing the token
var token = jwt.sign( user, 'ThisStringIsASecret' );
// Generated token
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImJhdG1hbiIsImlhdCI6MTQ4NTM3MTc0MywiZXhwIjoxNDg1Mzc1NzQzfQ.nxFDJGNnEvx4wDwcPk0puvcvtJArm2MxpZc4bn0XrSs
// Verifying the token
jwt.verify( token, new Buffer( 'ThisStringIsASecret', 'base64' ), function ( err, decoded ) { /**/ });
@DmanDman are you signing your token just like that? You should use new Buffer( 'ThisStringIsASecret', 'base64' ) to sign your token
@nodje @DaleWebb @michaelcbarr
I'm not going to take the time to dig into exactly what the java library is doing by default (I don't work with Java usually, needed to install Eclipse and figure and Maven and things :)). However, the issue is the string secret. By calling getBytes("UTF-8") on your secret and providing signWith() with the byte[], everything will work out.
package jwt_test.foo;import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;import java.io.UnsupportedEncodingException;
import java.util.Date;/**
- Hello world!
*
*/
public class App
{
private static String SECRET = "HelloWorld!";public static void main( String[] args ) throws UnsupportedEncodingException { System.out.println( makeToken() ); } private static String makeToken() throws UnsupportedEncodingException { return Jwts.builder() .setSubject("foo") .claim("bar", "baz") .signWith(SignatureAlgorithm.HS512, SECRET.getBytes("UTF-8")) .compact(); }}
'use strict';const JWT = require('jsonwebtoken');
const SECRET = 'HelloWorld!';
const JAVA_JWT = 'eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJmb28iLCJiYXIiOiJiYXoifQ.1MOXGiwGTFLU7-YMvOe2_q2ZRUHAMCVS7pbnOkRKCFV1HIvY8odBaqWVCQRuT2RUbKtGgA2elFRsuka4K1KP7A';JWT.verify(JAVA_JWT, SECRET, { algorithms: ['HS512'] })
This is deprecated, is there's an update here? Thanks
@ralphgabrielle assuming the latest stable JJWT version, which is 0.10.7 at the time of writing, you can do:
public class App {
// Note that 'HelloWorld' is not a valid JWT signing key per the JWA RFC's
// key strength requirements. For why, read this for more information:
// https://stackoverflow.com/a/40274325/407170
// Change this to something else or derive a key using PBKDF2
private static String SECRET = "HelloWorld!";
public static void main( String[] args ) throws UnsupportedEncodingException {
System.out.println( makeToken() );
}
private static String makeToken() throws UnsupportedEncodingException {
Key key = Keys.secretKeyFor(SECRET.getBytes(StandardCharsets.UTF_8));
return Jwts.builder()
.setSubject("foo")
.claim("bar", "baz")
.signWith(key)
.compact();
}
}
@ralphgabrielle assuming the latest stable JJWT version, which is 0.10.7 at the time of writing, you can do:
public class App {// Note that 'HelloWorld' is not a valid JWT signing key per the JWA RFC's // key strength requirements. For why, read this for more information: // https://stackoverflow.com/a/40274325/407170 // Change this to something else or derive a key using PBKDF2 private static String SECRET = "HelloWorld!"; public static void main( String[] args ) throws UnsupportedEncodingException { System.out.println( makeToken() ); } private static String makeToken() throws UnsupportedEncodingException { Key key = Keys.secretKeyFor(SECRET.getBytes(StandardCharsets.UTF_8)); return Jwts.builder() .setSubject("foo") .claim("bar", "baz") .signWith(key) .compact(); }}
Thanks, big help. That worked.
Hi,
I have 2 rest API in springboot project
I am able to generate token using /authenticate POST API but when I validate the token on https://jwt.io/ it says Invalid Signature
Generated Token: eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJmb28iLCJpYXQiOjE2MTY1MDk3NzIsImV4cCI6MTYxNjU0NTc3Mn0.DFSrZv9mDVnxWGBLP8KOQa0i8lBDJN2SHNZpGmYSsNL1-EhUXj3X1Jx0YrPuxGxwEfpBvNzs01AYKQ_DZUGkiQ
Also when I try to use this token as bearer and call /test GET API, it says status: 403, Forbidden
Here is the code snippet
// Here I am trying to remove spring security from /authenticate API but should apply on /test
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.authorizeRequests().antMatchers("/authenticate").permitAll()
.anyRequest().authenticated();
}
private String createToken(Map
{
SecretKey key = Keys.secretKeyFor(SignatureAlgorithm.HS512);
return Jwts.builder().setClaims(claims).setSubject(subject).setIssuedAt(new Date(System.currentTimeMillis()))
.setExpiration(new Date(System.currentTimeMillis() + 1000*60*60*10))
.signWith(key).compact();
}
Can anyone suggest what could be wrong here.
@send2harishsharma ,
You should click on the checkbox and it works fine. I know this is a too late reply.

Most helpful comment
An author of JJWT here...
FWIW, cryptographic signatures are _always_ computed with byte array keys - never strings. You can get the UTF-8 bytes of a String as demonstrated above, but that only masks what could be a very problematic cryptographic weakness (I'm not saying those in this thread are experiencing that weakness - I'm just raising that this could happen to anyone that might not understand what is going on).
Digital signature keys (again, byte arrays), should ideally never be based on simple strings like 'my secret' or 'my password'. Or, at the very least, if a simple password should be used as a signing key, it is almost always better to send it through a key-derivation algorithm (like PBKDF2) and then use _that_ resulting output as the signature key. This ensures sufficient cryptographic entropy (randomness) that short, human-readable strings don't (and which are therefore risky).
Signing keys should ideally always be:
Number 2 is why JJWT provides the
MacProvider.generateKeymethod - to ensure you always have keys of sufficient strength for the algorithm chosen. You can then easily base64 the result:This is why JJWT expects Base64 by default - because if you do these best practices, you'll always end up with a byte array key (e.g.
key.getEncoded()). And if you have a byte array key, the most common way to turn that into a string (e.g. for configuration) is to Base64-encode that byte array.Finally, note that
TextCodec.BASE64.decode(myKey)does NOT produce the same byte array (key) asmyKey.getBytes('UTF-8'). The latter is usually incorrect in cryptographic contexts.That means that
my-secret-token-to-change-in-production.getBytes("UTF-8")might represent a weakened signing key, and as a result, shouldn't be used. I recommend dumping that current key and generating a new one with strong cryptographic guarantees as shown above (e.g. using JJWT) and ensuring your Node library base64-decodes your string correctly.HTH!