Authenticating Firebase Cloud Messaging HTTP v1 API Requests

Firebase Cloud Messaging is a cross-platform messaging solution that lets you send reliable messages at no cost. FCM handles over 400 billion notification and data messages every day! But if you’re trying to set up a server using the FCM v1 Send API, you may have come across a very frustrating error that looks something like this:

string(304) “{ “error”: { “code”: 401, “message”: “Request is missing required authentication credential. Expected OAuth 2 access token, login cookie or other valid authentication credential. See https://developers.google.com/identity/sign-in/web/devconsole-project.", “status”: “UNAUTHENTICATED” } }”

If you’re getting an authentication error, never fear! Let’s do some troubleshooting together.

Verify what version of FCM you’re using

Currently, there are two FCM endpoints you can use to send notifications and data messages: FCM v1 and Legacy FCM. Chances are high that if you have this error, you’re using FCM v1. So how can you tell? Check out the format of your endpoint.

FCM v1:

https://fcm.googleapis.com/v1/projects/[your-project-id]/messages:send HTTP/1.1

Legacy FCM:

https://fcm.googleapis.com/fcm/send

If you’re using FCM v1, you’re in the right place. That’s the troubleshooting I want to show you today. If you’re having trouble authenticating the Legacy FCM API, a good place to start is by checking out the guides. If you’re still stuck after following the directions in the guide, post a question on Stack Overflow with a minimum reproducible code example and comment below. If I notice a trend in auth issues with the Legacy API, I’d be happy to do another blog post! Now, onto the good stuff…

Download the service account key

I want to start by pointing out that the server key found in the Firebase console will NOT authenticate v1 requests.

Instead, a more secure OAuth 2.0 token must be created using a Firebase service account. This account can be used to call the FCM v1 REST API from your app server or trusted environment. To authenticate the service account and authorize it to access Firebase services, you must generate a private key file in JSON format and use this key to retrieve a short-lived OAuth 2.0 token. It is this OAuth 2.0 token, and not the server key, which is used to authenticate sends. You can download the service account credentials in a JSON file here. Go do that now and add it to your project, and then come back here for the next step when you’re done. Also keep in mind that the contents of the file should be kept private. Do not add it to public repos!

Ok, now that you have the service account key JSON file in your project, let’s walk through how to generate the credentials you’ll pass to the POST request. I’m going to show an example using Node.js, but there are also samples using Java and python.

Create a token using the service account key JSON file

First, you need to instantiate a client using the Google API’s `JWT` function. Be sure that the path to your service account is written correctly. In the code below, the client is named `jwtClient`.

var {google} = require('googleapis');
var MESSAGING_SCOPE = 'https://www.googleapis.com/auth/firebase.messaging';
var SCOPES = [MESSAGING_SCOPE];
function getAccessToken() {
return new Promise(function(resolve, reject) {
var key = require('./service-account.json');
var jwtClient = new google.auth.JWT(
key.client_email,
null,
key.private_key,
SCOPES,
null
);
//…
}
}

Then, call authorize on jwtClient to generate the token.

var {google} = require(‘googleapis’);
var MESSAGING_SCOPE = ‘https://www.googleapis.com/auth/firebase.messaging';
var SCOPES = [MESSAGING_SCOPE];
function getAccessToken() {
return new Promise(function(resolve, reject) {
var key = require(‘./service-account.json’);
var jwtClient = new google.auth.JWT(
key.client_email,
null,
key.private_key,
SCOPES,
null
);
jwtClient.authorize(function(err, tokens) {
if (err) {
reject(err);
return;
}
resolve(tokens.access_token);
});
});
}

If authorized successfully, the promise will resolve to be the access token. If there is an error, the promise will be rejected and the error surfaced.

Use the token in the authorization header

Once the access token is created, you can use it in the authorization header in the format 'Authorization': 'Bearer ' + accesstoken. You can use whatever request package you prefer. Here’s what that header looks like using the Node.js request library:

var PROJECT_ID = ‘<YOUR-PROJECT-ID>’;
var HOST = ‘fcm.googleapis.com’;
var PATH = ‘/v1/projects/’ + PROJECT_ID + ‘/messages:send’;
function sendFcmMessage(fcmMessage) {
getAccessToken().then(function(accessToken) {
var options = {
hostname: HOST,
path: PATH,
method: ‘POST’,
headers: {
‘Authorization’: ‘Bearer ‘ + accessToken
}
// … plus the body of your notification or data message
};
var request = https.request(options, function(resp) {
resp.setEncoding(‘utf8’);
resp.on(‘data’, function(data) {
console.log(‘Message sent to Firebase for delivery, response:’);
console.log(data);
});
});
request.on(‘error’, function(err) {
console.log(‘Unable to send message to Firebase’);
console.log(err);
});
request.write(JSON.stringify(fcmMessage));
request.end();
});
}

Hopefully this clears up your authentication issue with FCM. If this helped you out, be sure to let me know on Twitter at @ThatJenPerson. If it didn’t help and you’re still stuck, I want to know about that too! I want to help you get unstuck so you can contribute to the hundreds of billions of messges sent by FCM!