Storing the googleapi Secret Keys in Firebase Environmental Variables - node.js

Question
I would like to store a Secret API Key for googleapis in a secure location. When I store the Secret API Key from googleapis as a Firebase Environmental Variable, the private_key is not processed the same as when I require("./privatekey.json"); See Issue below:
Context
I have downloaded and decoded a Secret API Key from Google. Most examples show saving the decoded JSON file within your project path and using require to pull the token into to code.
const SERVICE_ACCOUNT_KEY_FILE = require("./privatekey.json"); <----- This is Bad!!
const SERVICE_ACCOUNT_EMAIL = 'email#serviceaccount.com';
const jwt = new googleapis.auth.JWT(
SERVICE_ACCOUNT_EMAIL,
SERVICE_ACCOUNT_KEY_FILE.private_key,
null,
['https://www.googleapis.com/auth/analytics.readonly']);
I have used the firebase-cli to firebase functions:config:set Firebase Environmental Variables. When complete and redeployed, I run firebase functions:config:get and I see:
"googleapi_credentials": {
"private_key": "-----BEGIN PRIVATE KEY-----\\nMIIE ... q0DEg==\\n-----END PRIVATE KEY-----\\n",
Issue
When I configure googleapis.auth.JWT() I need to provide the googleapis Secret API Key. When I use require to pull in the Secret API Key, the requests work.
However, if I try to access the Firebase Environmental Variable to provide the Secret API Key, the requests fail.
var jwt = new googleapis.auth.JWT(
functions.config().googleapi_credentials.client_email,
functions.config().googleapi_credentials.private_key, <----- NOPE!
null,
['https://www.googleapis.com/auth/analytics.readonly']);
Debug
To see what's different I compared the console.log() of the two tokens in the firebase functions log view. The token I stored in the JSON file and in Firebase Environmental Variables looks the same in code, that is, both strings match and they include many \n (line breaks).
Now, when I review what the console.log() returns in the Firebase Functions Logs, I see different tokens.
console.log("JSON Private.Key", privatekey.private_key)
The view in the logs returns a formatted string with all \n replaced by line breaks, and the token is accepted.
console.log("Private.Key", functions.config().googleapi_credentials.private_key)
Logs returns a sting will all \n replaced by \\n., and the token is not accepted.
Final Note
The googleapis.auth.JWT() function can take an object for it arguments? Do I need to take this into consideration if using Firebase Environmental Variables?

Firebase environment details have a problem with add slashes and can break \n strings.
There is an open ticket on GitHub which should be referred to; github.com/firebase/firebase-tools/issues/371

Here is a hack I found posted by YunjorGlez. This worked for me.
You can use .replace(/\n/g, '\n') to remove the extra \ that is being added to the private_key.
const serviceAccount = functions.config().fireenv;
admin.initializeApp({
credential: admin.credential.cert({
"projectId": serviceAccount.project_id,
"private_key": serviceAccount.private_key.replace(/\\n/g, '\n'),
"clientEmail": serviceAccount.client_email
}),
databaseURL: whatever,
...
});

Related

Node.js google cloud storage authentication using credentials token

I'm trying to authenticate with google cloud storage using a credentials token.
Can't find an example anywhere in the node.js GCS api docs on how to do so.
They instruct to generate and download a json file that contains your private key and then link to its path on your file system like so:
const storage = new Storage({keyFilename: "key.json"});
And this works just fine.
However I don't want to save my key as a JSON file, but create the credentials and save them as environment variables something like so:
const gc = new Storage({
credentials: {
client_email: process.env.CLIENT_EMAIL,
private_key: process.env.SECRET_KEY
}
});
I tried getting this token from the settings of the bucket, from the interoperability menu, using service account HMAC access keys.
When I try to upload/delete files from the bucket with the authentication method above I get the following error:
Error: error:0909006C:PEM routines:get_name:no start line
Appreciate any help on the matter
The error
Error: error:0909006C:PEM routines:get_name:no start line
was actually caused because of a dotenv ohmyzsh plugin I downloaded a while back and just forgot about. Was very hard to debug. Turns out the google secret key has \n in it and the ohmyzsh dotenv plugin failed to parse them correctly. So deleting it worked for me.
If you use the JSON api (you most likely are) These are the credentials you need to authenticate, so you can just take the relevant info and put it in a .env file in your project, if you don't like putting the path to the json file:
const gc = new Storage({
projectId: process.env.GOOGLE_STORAGE_PROJECT_ID,
scopes: 'https://www.googleapis.com/auth/cloud-platform',
credentials: {
client_email: process.env.GOOGLE_STORAGE_EMAIL,
private_key: process.env.GOOGLE_STORAGE_PRIVATE_KEY
}
})
Still am not 100% sure on how to authenticate using a token + secret.
I am getting close to the answer and will update this post in the future if I find it. Posting a helpful link: google-auth-library-nodejs hoping someone beats me to it :)
Error: error:0909006C:PEM routines:get_name:no start line can be solved by converting '\n' to the actual char \n using something like:
process.env.ATHENA_PRIVATE_KEY.replace(/\\n/g, '\n')

Firebase Auth Are my variables secure/safe?

I am currently using heroku to store my environmental variables for my firebase authentication initialisation. I am using my server to get the environmental variables and send it to the client using socket.io. Below is what I mean.
1) Example of sending environmental variable to client from server:
socket.emit('value', process.env.apiKey);
2) storing it as data[0] in the client:
socket.on('value', function(data) {
firebase.initializeApp({
apiKey: data[0],
});
})
Is this safe? Can someone from the client retrieve the value of the apiKey if I save it like this on the client?
Thanks
If the data is used from the client, it can be gotten from there by a malicious user. Looking up the data dynamically like you do here, merely adds an extra step.
But the data that you pass to initializeApp is basic configuration data that allows the code to find your Firebase project on the servers. It is not a secret, it's not a security mechanism ,and it can be safely shared with your users. See my answer here, for why you don't have to try and secure this data: Is it safe to expose Firebase apiKey to the public?

How to retrieve firebase api key from my nodejs backend?

I'm building a React app that will use Firebase Auth. I was storing my API key inside the .env file but I've read in the React docs that it is not safe, so I have created a custom and private route on my NodeJS server that is a simple GET request which sends me the data that I need(key, projectid, etc). And I think this is the safest way to do it, right?
Now I'm trying to get this data from my backend and I'm able to do it using console.log for example but I'm unable to integrate it with Firebase function. It keeps saying that it is undefined.
This is what I have tried so far:
import * as firebase from 'firebase/app';
import 'firebase/auth';
const API_URL = process.env.REACT_APP_API_URL;
let res;
export async function getFirebaseKeys() {
const response = await fetch(`${API_URL}/api/get`);
res = response.json();
}
getFirebaseKeys();
//I have also tried to use firebase.OnLog but no success.
//I have also tried to wrap firebase.initializeApp and my fetch in a function but it breaks
const app = firebase.initializeApp({
apiKey: res.apiKey,
authDomain: res.domain,
databaseURL: res.database,
projectId: res.projectId,
storageBucket: res.storageBucket,
messagingSenderId: res.senderId,
});
export default app;
This is my first project with Firebase and React so I'm sorry if i have missed something obvious.
What you're trying to do now is not really any safer than putting the Firebase config in the source (where it's intended to go). Hiding it behind a GET just makes people go through another step to get a hold of it if they want it.
There's nothing unsafe about putting the Firebase config in the hands of the public, as long as you are also signing in users with Firebase Authentication, and using security rules to determine who can access what data in the project.
Just do whatever is most convenient - security is not the issue here.
See also: Is it safe to expose Firebase apiKey to the public?

Questionings about keys and OAuth

I am building a node.js app (script?) that is using google-auth-library and there is something that I don't understand.
I have generated the JSON file containing my OAuth2 client id keys using Google Developers Console, and I am using it in my script the following way :
const keys = require('../client_secret.json');
const oAuth2Client = new OAuth2Client(
keys.web.client_id,
keys.web.client_secret,
keys.web.redirect_uris[0]
);
// Generate the url that will be used for the consent dialog.
const authorizeUrl = oAuth2Client.generateAuthUrl({
access_type: 'offline',
scope: [
'https://mail.google.com',
'https://www.googleapis.com/auth/drive'
]
});
Then, I am opening the consent dialog, and getting my token back, etc. My app has the will to be open source, so my question is: should I let my client_secret.json file in my repository so other users can use it using their Google account?
The client token must be kept secret: extract from Google documentation
After creating your credentials, download the client_secret.json file from the API Console. Securely store the file in a location that only your application can access.
Your application will remain open source as authentication is a service not source code.
To manage your secret, I would suggest you to use environment variables accessible via process.env.YOUR_VARIABLE.
It does exit packages that will make it easy to handle between you different environments, my favorite is dotenv. dotenv loads environment variables from a non required .env file. You would typically use it in your development environment, you must not commit it!
Dotenv do not require the presence of the .env file, and won't override an environment variable that is already set anyway. You will have to define the environment variables in production and test environment the way your prefer.
You can also see this article

Firebase Admin and custom token data pass to server

There is not much information on creating your own customToken on the Firebase Docs. It would be helpful for beginner developers to know how to create them to sort data to be accessible for specific users in an app (ios-swift), e.g. group1 has specific users who can view a specific section of the database.
In my case, I get lost in step 3 from 'Authenticate with Firebase'
When users sign into your app, send their sign-in credentials (for example, their username and password) to your authentication server. Your server checks the credentials and returns a custom token if they are valid.
from: https://firebase.google.com/docs/auth/ios/custom-auth
I am unsure about sending the sign-in credentials back to my server, where I will create a custom token and send it back. But how am I supposed to do this? Firebase Docs doesn't specify how.
I have set a server with node.js with the following code:
var firebase = require('firebase');
var admin = require('firebase-admin');
var FirebaseTokenGenerator = require("firebase-token-generator");
var path = require('path');
var servAcc = '/Users/myUserName/Desktop/nodeClient/service-account.json';
var tokenGenerator = new FirebaseTokenGenerator("firebase-secret");
var token = tokenGenerator.createToken({
uid: "clientId", groupId: "group1", managerId:"MG1"
});
firebase.initializeApp({
serviceAccount: path.resolve(__dirname, '/Users/myUserName/Desktop/nodeClient/service-account.json'),
databaseURL: "https://<app-name>.firebaseio.com/",
databaseAuthVariableOverride: {
uid: "clientId"
}
})
admin.initializeApp({
credential: admin.credential.cert(servAcc),
databaseURL: "https://apos-accentit.firebaseio.com/"
});
Just in admin.credential.cert(servAcc), I get an error in the terminal using nodemon.
Cannot read property of 'cert' undefined
How shall I proceed?
I solved this problem by updating firebase-admin. Apparently, the new admin.credential.cert() method was added on Nov 7th, 2016 with firebase-admin version 4.0.0. I was using a tutorial that referenced an older version of firebase-admin (3.0.0), and that version was what my package.json file referenced. Once I referenced the 4.0.0. I no longer experienced the error.
https://firebase.google.com/support/release-notes/admin/node
If you already have the json downloaded for your service credentials, create a 'js' file named credentials.js in the same folder of your main project. In this file create a variable called credentials and make it equal the json you have downloaded as your 'service-account.json'; also don't forget to export it at the end of your file as:
module.exports = credentials;
Save this file and at the top your main index.js file, import the newly created file named credentials.js as:
var serviceAccount = require('./credentials.js')
Now use this variable as your credentials. Inside of admin.initializedApp should look like this:
admin.initializeApp({
credential: admin.credential.cert(serviceAccount),
databaseURL: "https://<NAME OF YOUR DATABASE>.firebaseio.com"
});
in short. The reason why node can't read your service-account.json is because the file extension is not readable by node. I had the same problem and this fixed it for me

Resources