async sendPush()

in packages/fxa-auth-server/lib/push.js [380:489]


    async sendPush(uid, devices, reason, options = {}) {
      devices = filterSupportedDevices(options.data, devices);
      if (!PUSH_REASONS.has(reason)) {
        throw `Unknown push reason: ${reason}`;
      }
      // There's no spec-compliant way to error out as a result of having
      // too many devices to notify.  For now, just log metrics about it.
      if (devices.length > MAX_ACTIVE_DEVICES) {
        log.warn(LOG_OP_TOO_MANY_DEVICES, { uid });
      }
      const sendErrors = {};
      for (const device of devices) {
        const deviceId = device.id;

        // Details that we might want to include in logs.
        const metricsTags = {
          reason,
          uid,
          deviceId,
          uaOS: device.uaOS,
          uaOSVersion: device.uaOSVersion,
          uaBrowser: device.uaBrowser,
          uaBrowserVersion: device.uaBrowserVersion,
          lastSeen: this.getLastSeenTag(device),
        };
        this.reportPushAttempt(device.pushCallback, metricsTags);

        if (!device.pushCallback) {
          sendErrors[deviceId] = this.reportPushFailure(
            ERR_NO_PUSH_CALLBACK,
            metricsTags
          );
          continue;
        }
        if (device.pushEndpointExpired) {
          sendErrors[deviceId] = this.reportPushFailure(
            ERR_PUSH_CALLBACK_EXPIRED,
            metricsTags
          );
          continue;
        }
        const pushSubscription = { endpoint: device.pushCallback };
        let pushPayload = null;
        const pushOptions = { TTL: options.TTL || '0' };
        if (options.data) {
          if (!device.pushPublicKey || !device.pushAuthKey) {
            sendErrors[deviceId] = this.reportPushFailure(
              ERR_DATA_BUT_NO_KEYS,
              metricsTags
            );
            continue;
          }
          pushSubscription.keys = {
            p256dh: device.pushPublicKey,
            auth: device.pushAuthKey,
          };
          pushPayload = Buffer.from(JSON.stringify(options.data));
        }
        if (vapid) {
          pushOptions.vapidDetails = vapid;
        }
        try {
          await webpush.sendNotification(
            pushSubscription,
            pushPayload,
            pushOptions
          );
          this.reportPushSuccess(metricsTags);
        } catch (err) {
          // If we've stored an invalid key in the db for some reason, then we
          // might get an encryption failure here.  Check the key, which also
          // happens to work around bugginess in node's handling of said failures.
          let keyWasInvalid = false;
          if (!err.statusCode && device.pushPublicKey) {
            if (!isValidPublicKey(device.pushPublicKey)) {
              keyWasInvalid = true;
            }
          }
          // 404 or 410 error from the push servers means
          // the push settings need to be reset.
          // the clients will check this and re-register push endpoints
          if (
            err.statusCode === 404 ||
            err.statusCode === 410 ||
            keyWasInvalid
          ) {
            sendErrors[deviceId] = this.reportPushFailure(
              ERR_PUSH_CALLBACK_RESET,
              metricsTags
            );
            // set the push endpoint expired flag
            // Warning: this method is called without any session tokens or auth validation.
            device.pushEndpointExpired = true;
            try {
              await db.updateDevice(uid, device);
            } catch (err) {
              log.warn(LOG_OP_DEVICE_UPDATE_FAILED, { uid, deviceId, err });
            }
          } else {
            log.error(LOG_OP_PUSH_UNEXPECTED_ERROR, {
              err,
              statusCode: err.statusCode,
              body: err.body,
            });
            sendErrors[deviceId] = this.reportPushFailure(err, metricsTags);
          }
        }
      }
      return sendErrors;
    },