Firebase Admin and custom token data pass to server - node.js

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

Related

Use google-spreadsheets with nodejs with service account?

How can use a service account with nodejs in order to access at a sheet on google with google api?
I tried with the nodejs (npm) module google-spredsheet but when the loadInfo() it returns always Request failed error 404
edit1:
ii already have a service account and the key file downloaded and in my directory.
Here's the actual script:
const express = require("express"),
{GoogleSpreadsheet} = require('google-spreadsheet'),
creds = require('./client_secret.json'),
app = express();
app.set("view engine", "ejs");
app.use(express.static(__dirname+"/public"));
app.get("/google-spreadsheet", async function(req, res){
// Identifying which document we'll be accessing/reading from
var doc;
doc = new GoogleSpreadsheet('1XKcnyJkTyq15AlvhAs3V_2wnP8vgwY3mWXAijyiINVs/edit#gid=0');
// Authentication
await doc.useServiceAccountAuth(creds);
await doc.loadInfo(); // loads document properties and worksheets
console.log("test: "+doc.title);
});
app.listen(8080, () => {
console.log('[EXPRESS] Web Server active on: ' + 8080);
});
I believe your current situation and goal as follows.
Your client_secret.json is for the service account.
You are using "google-spreadsheet" of the latest version.
You want to access to the Google Spreadsheet using "google-spreadsheet" with the service account.
Modification points:
In your script, '1XKcnyJkTyq15AlvhAs3V_2wnP8vgwY3mWXAijyiINVs/edit#gid=0 is used as the Spreadsheet ID. In this case, such error occurs. I think that this might be the reason of your issue. So please modify it to '1XKcnyJkTyq15AlvhAs3V_2wnP8vgwY3mWXAijyiINVs'.
Modified script:
When your script is modified, it becomes as follows.
From:
doc = new GoogleSpreadsheet('1XKcnyJkTyq15AlvhAs3V_2wnP8vgwY3mWXAijyiINVs/edit#gid=0');
To:
doc = new GoogleSpreadsheet('1XKcnyJkTyq15AlvhAs3V_2wnP8vgwY3mWXAijyiINVs');
Note:
When the Google Spreadsheet of 1XKcnyJkTyq15AlvhAs3V_2wnP8vgwY3mWXAijyiINVs is not shared with the email of the service account, the service account cannot use the Spreadsheet. Please be careful this.
Reference:
google-spreadsheet

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')

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?

Storing the googleapi Secret Keys in Firebase Environmental Variables

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,
...
});

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

Resources