How do I implement JWT with pub sub push

Viewed 688

I followed the documentation on pub/sub notifications with the push method here

And I want to have authentication on my call with JWT. I looked at their GitHub example here

app.post('/pubsub/authenticated-push', jsonBodyParser, async (req, res) => {
  // Verify that the request originates from the application.
  if (req.query.token !== PUBSUB_VERIFICATION_TOKEN) {
    res.status(400).send('Invalid request');
    return;
  }

  // Verify that the push request originates from Cloud Pub/Sub.
  try {
    // Get the Cloud Pub/Sub-generated JWT in the "Authorization" header.
    const bearer = req.header('Authorization');
    const [, token] = bearer.match(/Bearer (.*)/);
    tokens.push(token);

    // Verify and decode the JWT.
    // Note: For high volume push requests, it would save some network
    // overhead if you verify the tokens offline by decoding them using
    // Google's Public Cert; caching already seen tokens works best when
    // a large volume of messages have prompted a single push server to
    // handle them, in which case they would all share the same token for
    // a limited time window.
    const ticket = await authClient.verifyIdToken({
      idToken: token,
      audience: 'example.com',
    });

    const claim = ticket.getPayload();
    claims.push(claim);
  } catch (e) {
    res.status(400).send('Invalid token');
    return;
  }

  // The message is a unicode string encoded in base64.
  const message = Buffer.from(req.body.message.data, 'base64').toString(
    'utf-8'
  );

  messages.push(message);

  res.status(200).send();
});

But I have some questions.

  1. What is the PUBSUB_VERIFICATION_TOKEN and how do I get it and store it in my environment?

  2. const [, token] = bearer?.match(/Bearer (.*)/); throws the following error

    Type 'RegExpMatchArray | null | undefined' must have a 'Symbol.iterator' method that returns an iterator.ts(2488)

  3. Why do they push the claims and tokens in an array if they never check that array in this function for already existing tokens / claims?

I am trying to implement this with a Firebase Cloud Function and this is what I have. Is it even possible to cache the tokens / claims?

//Service account auth client
const authClient = new google.auth.JWT({
    email: android_key.client_email,
    key: android_key.private_key,
    scopes: ["https://www.googleapis.com/auth/androidpublisher"]
});

export const handlePubSub = functions.region('europe-west1').https.onRequest(async (req, res) => {

    // What is PUBSUB_VERIFICATION_TOKEN???
    if (req.query.token !== PUBSUB_VERIFICATION_TOKEN) {
        res.status(400).send('Invalid request');
        return;
    }

    try {
        const bearer = req.header('Authorization');
        const [, token] = bearer?.match(/Bearer (.*)/); //Error Type 'RegExpMatchArray | null | undefined' must have a 'Symbol.iterator' method that returns an iterator.ts(2488)
        tokens.push(token); // Why do this? Can I do this in firebase cloud functions

        const ticket = await authClient.verifyIdToken({
            idToken: token,
        });

        const claim = ticket.getPayload();
        claims.push(claim);  // Why do this? Can I do this in firebase cloud functions
    } catch (e) {
        res.status(400).send('Invalid token');
        return;
    }

    const message = Buffer.from(req.body.message.data, 'base64').toString(
        'utf-8'
    );

    console.log(message);

    return res.status(200).json({
        statusCode: 200,
        method: req.method,
        message: 'Recieved successfully'
    });
});
1 Answers

What is the PUBSUB_VERIFICATION_TOKEN and how do I get it and store it in my environment?

PUBSUB_VERIFICATION_TOKEN can be any value you want. Easiest way to set an environment variable is on the command line when running node:

PUBSUB_VERIFICATION_TOKEN=whatevertoken node app.js

The req.query.token that is compared too comes from the URL query string.

GET /whatever?token=whatevertoken

Type 'RegExpMatchArray | null | undefined' must have a 'Symbol.iterator' method that returns an iterator.ts(2488)

That's a bug in their code. bearer.match can return undefined/null which can't be spread into the array [, token]. The example will only work when there is a successful regex match. This will parse in plain javascript but typescript highlights this issue at compile time.

const bearer = req.header('Authorization');
const m = /Bearer (.*)/.exec(bearer)
if (m) tokens.push(m[1])

Why do they push the claims and tokens in an array if they never check that array in this function for already existing tokens / claims?

The example comments // List of all messages received by this instance. So more a debug store than something functional.

Related