18

On the backend a custom token is generated via firebase's admin SDK thusly:

router.use('/get-token', (req, res) => {
    var uid = "big-secret";
    admin.auth().createCustomToken(uid)
      .then(function(customToken) {
        res.json({
          instanceID: customToken
        });
      })
      .catch(function(error) {
        console.log("Error creating custom token:", error);
    });
});

The client frontend app then picks up the customToken and with it makes a request back to the backend to verify:

const fbPrivateKey = serviceAccount.private_key;
const key = new NodeRSA(fbPrivateKey).exportKey('pkcs8-public-pem');
router.get('/verifyIdToken', cors(), (req, res) => {
  jwt.verify(req.headers.authorization.split('Bearer ')[1], key, { algorithms: ['RS256'] }, function(err, decoded) {
    console.log('err', err);
    console.log('decoded', decoded);
  });

This always errors with the message: JsonWebTokenError: invalid signature

Does this need signing? If anyone could explain this or has any pointers?

UPDATE: When running req.headers.authorization.split('Bearer ')[1] through jwt.io is says that the signature is invalid, but then I enter my private key (key) and it validates.

Am I getting the method calls incorrect or passing the wrong arguments into jwt.verify() ?

Harry Lincoln
  • 604
  • 2
  • 9
  • 28

2 Answers2

23

It looks like you're calling verifyIdToken with a custom token. That's not going to work. verifyIdToken only accepts "ID tokens". To obtain an ID token from a custom token first call signInWithCustomToken(). Then call getToken() on the signed in user instance.

Hiranya Jayathilaka
  • 6,545
  • 1
  • 20
  • 32
  • 2
    Thanks so much :) – Michael Oct 23 '18 at 21:18
  • This answer is confusing, what you are talking about is admin.auth().verifyIdToken() which yes it can only verify ID tokens and not custom tokens. But in the post this is not used anywhere, the /verifyIdToken is just a route that Harry has configured for verifying ids. I think @Harry was just exporting the public-key wrong, check my answer below. – Flavio Rajta Sep 15 '19 at 14:48
9

If you dont want to use signInWithCustomToken() this is the correct way to do it

const publicKey = new NodeRSA().importKey(serviceAccount.private_key, "pkcs8-private-pem").exportKey("pkcs8-public-pem")

jwt.verify(token, publicKey, {
        algorithms: ["RS256"]
    }, (err, decoded) => {
        if (err) {
            # send some error response
            res.status(400).json({
                status: 0,
                message: "Token is invalid!"
            })
        } else {
            # send some valid response
            res.status(200).json({
                status: 1,
                message: "Token is valid for uid " + decoded.uid
            })
        }
    })
Flavio Rajta
  • 251
  • 3
  • 2
  • You might not need the public key. With the jsonwebtoken package (repo: https://github.com/auth0/node-jsonwebtoken) you can just send the serviceAccount.private_key as second argument, so there wouldn't be need for NodeRSA. – Daniel Reina Sep 17 '21 at 08:55