Search code examples
node.jsjwtamazon-kms

NodeJS AWS KMS sign and verify token


I am trying to create a JWT and then verify it using AWS KMS Node API. Below is my code, which represents my understanding of the AWS documentation. However calling KMS verify throws KMSInvalidSignatureException

process.env.AWS_REGION = '...';
process.env.AWS_ACCOUNT_ID = '...';
process.env.AWS_PROFILE = '...';
process.env.KEY_ID = '...id of RSA 2048 bytes key stored in AWS KMS...';

const aws = require('aws-sdk');
const kms = new aws.KMS();
const base64url = require('base64url');

async function createToken() {
  const iat = Math.floor(Date.now() / 1000);
  const tomorrow = new Date()
  tomorrow.setDate(tomorrow.getDate() + 1)
  const exp = Math.floor(tomorrow.getTime() / 1000);
  const header = base64url.encode(JSON.stringify({
    alg: 'RS256',
    typ: 'JWT'
  }));
  const payload = base64url.encode(JSON.stringify({
    iat,
    exp,
    code: '123'
  }));
  const message = Buffer.from(`${header}.${payload}`);
  const signResponse = await kms.sign({
    KeyId: process.env.KEY_ID,
    Message: message,
    MessageType: 'RAW',
    SigningAlgorithm: 'RSASSA_PKCS1_V1_5_SHA_256'
  }).promise();
  const signature = signResponse.Signature.toString('base64')
    .replace(/\+/g, '-')
    .replace(/\//g, '_')
    .replace(/=/g, '');
  return `${header}.${payload}.${signature}`;
}

async function verifyToken(token) {
  const [header, payload, signature] = token.split('.');
  const message = Buffer.from(`${header}.${payload}`);
  return await kms.verify({
    KeyId: process.env.KEY_ID,
    Message: message,
    MessageType: 'RAW',
    Signature: signature,
    SigningAlgorithm: 'RSASSA_PKCS1_V1_5_SHA_256'
  }).promise();
}

async function main() {
  const token = await createToken();
  const verification = await verifyToken(token);
  console.log(verification);
}

main();

What am I doing wrong ?


Solution

  • At least with V3 of the aws-sdk, verify expects Signature to be a Buffer/Uint8Array.

    Possibly fixed verifyToken function (also removed obsolete async/await):

    function verifyToken(token) {
      const [header, payload, signature] = token.split('.');
      const message = Buffer.from(`${header}.${payload}`);
      return kms.verify({
        KeyId: process.env.KEY_ID,
        Message: message,
        MessageType: 'RAW',
        Signature: Buffer.from(signature, 'base64'),
        SigningAlgorithm: 'RSASSA_PKCS1_V1_5_SHA_256'
      }).promise();
    }