Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Base64 Encoding of Server Key for Firebase Cloud Messaging

I have been stuck in this problem since 3 days and searched Google, but to no success. I followed the instruction given in Push Notifications Example. But I get this nasty error when I try to implement it.

Uncaught (in promise) DOMException: Failed to execute 'atob' on 'Window': The string to be decoded is not correctly encoded.

I figured out the colon(:) is not allowed in Base64 String, but the Server Key given in Firebase in Cloud Messenging tab is

AAAA-8MWvk0:APA91bHmU8XL-rzOn9SPbpG98NSqAJcuhYBBh7oze_XhprBpK7Q9PPWm3PwBo6Llxby4zjhVtgvKPIaSAQGp-8RfMV10_d1GekzICrVX9oYO8pi6dOM4VTp52CCAzug6NYIa10tNddsgE2P5QowGAYcnRHxLkrHXsw

which contains a colon (don't worry, its just a test app so no problem with privacy).

When I tried to use the legacy server key, it simply throws an error. I tried to use other keys given in Firebase too, but to no success. Please tell me actually which server key to use and how?

I'm attaching my code snippet which actually performs the push subscription.

 const API_KEY = "AIzaSyByIOl-mW0pu8SEXFeutB8jq59hhiau0wI";
 var GCM_ENDPOINT = 'https://fcm.googleapis.com/fcm/send';
 const legacy  = 'AIzaSyDGF8t125bJ4wBvYn_UdRewkTxHGr7KpH8';
 const applicationServerPublicKey = 'AAAA-8MWvk0APA91bHmU8XL-rzOn9SPbpG98NSqAJcuhYBBh7oze_XhprBpK7Q9PPWm3PwBo6Llxby4zjhVtgvKPIaSAQGp-8RfMV10_d1GekzICrVX9oYO8pi6dOM4VTp52CCAzug6NYIa10tNddsgE2P5QowGAYcnRHxLkrHXsw';

function urlB64ToUint8Array(base64String) {
 const padding = '='.repeat((4 - base64String.length % 4) % 4);
 const base64 = (base64String + padding)
 .replace(/\-/g, '+')
 .replace(/_/g, '/');
 console.log(base64);
 const rawData = window.atob(base64);
 console.log(rawData);
 const outputArray = new Uint8Array(rawData.length);

 for (let i = 0; i < rawData.length; ++i) {
  outputArray[i] = rawData.charCodeAt(i);
 }
 return outputArray;
}

function endpointWorkaround(pushSubscription) {
 // Make sure we only mess with GCM
 if(pushSubscription.endpoint.indexOf('https://fcm.googleapis.com/fcm/send') !== 0) {
  return pushSubscription.endpoint;
 }

 var mergedEndpoint = pushSubscription.endpoint;
 // Chrome 42 + 43 will not have the subscriptionId attached
 // to the endpoint.
 if (pushSubscription.subscriptionId &&
 pushSubscription.endpoint.indexOf(pushSubscription.subscriptionId) === -1)    {
  // Handle version 42 where you have separate subId and Endpoint
  mergedEndpoint = pushSubscription.endpoint + '/' +
  pushSubscription.subscriptionId;
 }
 return mergedEndpoint;
}

function sendSubscriptionToServer(subscription) {
 // TODO: Send the subscription.endpoint
 // to your server and save it to send a
 // push message at a later date
 //
 // For compatibly of Chrome 43, get the endpoint via
 // endpointWorkaround(subscription)
 console.log('TODO: Implement sendSubscriptionToServer()',    JSON.stringify(subscription));

 var mergedEndpoint = endpointWorkaround(subscription);

 // This is just for demo purposes / an easy to test by
 // generating the appropriate cURL command
 var temp = showCurlCommand(mergedEndpoint);
 return temp;
}

// NOTE: This code is only suitable for GCM endpoints,
// When another browser has a working version, alter
// this to send a PUSH request directly to the endpoint
function showCurlCommand(mergedEndpoint) {
 // The curl command to trigger a push message straight from GCM
 if (mergedEndpoint.indexOf(GCM_ENDPOINT) !== 0) {
  console.warn('This browser isn\'t currently ' + 'supported for this demo');
  return;
 }

 var endpointSections = mergedEndpoint.split('/');
 var subscriptionId = endpointSections[endpointSections.length - 1];

 var curlCommand = 'curl --header "Authorization: key=' + API_KEY + '" --header Content-Type:"application/json" ' + GCM_ENDPOINT + ' -d "{\\"registration_ids\\":[\\"' + subscriptionId + '\\"]}"';

 console.log(curlCommand);
 return subscriptionId;
}

function initialiseState() {
 // Are Notifications supported in the service worker?
 if (!('showNotification' in ServiceWorkerRegistration.prototype)) {
  console.warn('Notifications aren\'t supported.');
  return;
 }

 // Check the current Notification permission.
 // If its denied, it's a permanent block until the
 // user changes the permission
 if (Notification.permission === 'denied') {
  console.warn('The user has blocked notifications.');
  return;
 }

 // Check if push messaging is supported
 if (!('PushManager' in window)) {
  console.warn('Push messaging isn\'t supported.');
  return;
 }
 var prom = new Promise(function(resolve, reject) {
  navigator.serviceWorker.ready.then(function(serviceWorkerRegistration) {
    // Do we already have a push message subscription?
serviceWorkerRegistration.pushManager.getSubscription().then(function(subscription) {
            // Enable any UI which subscribes / unsubscribes from
            // push messages.
            // var pushButton = document.querySelector('.js-push-button');
            // pushButton.disabled = false;

            if (!subscription) {
                // We aren’t subscribed to push, so set UI
                // to allow the user to enable push
        subscribe();
                return;
            }

            // Keep your server in sync with the latest subscription
      var temp = sendSubscriptionToServer(subscription);
      if(temp){
        resolve(temp);
      }else{
        reject("Oops!")
      }

            // Set your UI to show they have subscribed for
            // push messages
            // pushButton.textContent = 'Disable Push Messages';
            // isPushEnabled = true;
        })
        .catch(function(err) {
            console.error('Error during getSubscription()', err);
      reject(err);
        });
});
});
return prom;
}

function unsubscribe() {
 // var pushButton = document.querySelector('.js-push-button');
 // pushButton.disabled = true;

 navigator.serviceWorker.ready.then(function(serviceWorkerRegistration) {
  // To unsubscribe from push messaging, you need get the
  // subcription object, which you can call unsubscribe() on.
  serviceWorkerRegistration.pushManager.getSubscription().then(
  function(pushSubscription) {
   // Check we have a subscription to unsubscribe
   if (!pushSubscription) {
    // No subscription object, so set the state
    // to allow the user to subscribe to push
    //  isPushEnabled = false;
    //  pushButton.disabled = false;
    //  pushButton.textContent = 'Enable Push Messages';
    return;
   }

   // TODO: Make a request to your server to remove
   // the users data from your data store so you
   // don't attempt to send them push messages anymore

   // We have a subcription, so call unsubscribe on it
   pushSubscription.unsubscribe().then(function() {
    //  pushButton.disabled = false;
    //  pushButton.textContent = 'Enable Push Messages';
    //  isPushEnabled = false;
   }).catch(function(e) {
     // We failed to unsubscribe, this can lead to
     // an unusual state, so may be best to remove
     // the subscription id from your data store and
     // inform the user that you disabled push

     console.log('Unsubscription error: ', e);
     //  pushButton.disabled = false;
   });
 }).catch(function(e) {
   console.error('Error thrown while unsubscribing from ' + 'push messaging.', e);
 });
});
}

function subscribe() {
 // Disable the button so it can't be changed while
 // we process the permission request
 // var pushButton = document.querySelector('.js-push-button');
 // pushButton.disabled = true;

 navigator.serviceWorker.ready.then(function(serviceWorkerRegistration) {
  const applicationServerKey = urlB64ToUint8Array(applicationServerPublicKey);
  serviceWorkerRegistration.pushManager.subscribe({userVisibleOnly: true,  applicationServerKey: applicationServerKey})
 .then(function(subscription) {
  console.log(subscription);
  // The subscription was successful
  //  isPushEnabled = true;
  //  pushButton.textContent = 'Disable Push Messages';
  //  pushButton.disabled = false;

  // TODO: Send the subscription subscription.endpoint
  // to your server and save it to send a push message
  // at a later date
  return sendSubscriptionToServer(subscription);
 })
 .catch(function(e) {
  if (Notification.permission === 'denied') {
   // The user denied the notification permission which
   // means we failed to subscribe and the user will need
   // to manually change the notification permission to
   // subscribe to push messages
   console.log('Permission for Notifications was denied');
   //  pushButton.disabled = true;
  } else {
   // A problem occurred with the subscription, this can
   // often be down to an issue or lack of the gcm_sender_id
   // and / or gcm_user_visible_only
   console.log('Unable to subscribe to push.', e);
   //  pushButton.disabled = false;
   //  pushButton.textContent = 'Enable Push Messages';
  }
 });
});
}
like image 721
Tamoghna Avatar asked Dec 27 '16 02:12

Tamoghna


People also ask

How do I get my server key for Firebase Cloud Messaging?

To generate a private key file for your service account:In the Firebase console, open Settings > Service Accounts. Click Generate New Private Key, then confirm by clicking Generate Key. Securely store the JSON file containing the key.

What is Firebase Cloud Messaging server key?

What is a Firebase Server Key? A Firebase Server Key and Firebase Sender ID are required in order to send push notifications to Android mobile app devices. The goal of this section is to provision your Firebase Server Key and Firebase Sender ID for use in OneSignal.


1 Answers

This is sadly a confusing thing with Chrome.

At the most basic level: Web Push is completely seperate from Firebase Messaging for the web.

Web Push and Application Server Key

Web push requires an application server key, this is what you pass into the subscribe call on a service worker push manager:

pushManager.subscribe({userVisibleOnly: true, applicationServerKey: MY_KEY});

In web push, the applicationServerKey needs to be a Uint8Array (i.e. an array of 65 bytes). This key can be generated anyway you like, just make sure you keep the private key private and use the public key in your web app.

In the codelab I wrote, I instructed that you get your key from: https://web-push-codelab.appspot.com/

This page generates a set of application server keys on the fly and will store the result in local storage (So you can use the same page to send push messages - which need that application server key).

You can just as easily get your key from tools like web-push's node CLI.

Firebase Server Key

The Firebase Messaging SDK for the web uses service workers to implement push on the web to, but it manages the application server keys for you. The reason for this, is that Firebase Messaging SDK sets you up to use the Firebase Cloud Message API to trigger push messages, instead of the Web Push Protocol that you'd need to use in the codelab you were working through.

The "Server Key" in the Firebase Console, is the key that you'd use when triggering a push message, it's the "Authorization" header.

https://fcm.googleapis.com/fcm/send
Content-Type: application/json
Authorization: key=<YOUR SERVER KEY HERE>

Summary

  • Firebase Messaging has it's own SDK for web push, if you use that, then you should follow it's guide and use it's server side API.
  • Web Push (The open web standard) has no direct dependency with Firebase (or any other push service) so you create keys to a specific format and use the web push protocol to trigger push messages. (Chrome uses Firebase behind the scenes, but as a developer of a web push site, you do not need to do anything for Firebase).
like image 87
Matt Gaunt Avatar answered Oct 06 '22 15:10

Matt Gaunt