Setting Heroku Config Var with contents from Google Cloud Service Account Keyfile - node.js

I have a web app that I'm trying to deploy with Heroku. I'm using the Google Cloud Node.js library for accessing google cloud storage. I have the following at the top of my server file:
const gcs = require('#google-cloud/storage')({
projectId: 'my-project-ID',
credentials: process.env.GCS_KEYFILE
});
GCS_KEYFILE is an configuration variable that I've set using the Heroku command line tools using heroku config:set GCS_KEYFILE="$(< /my/file.json)"
Checking the dashboard to make sure that worked confirms the contents of the JSON file have been set to a config var. Screenshot looks like this:
The error I get when I try to do anything with gcs is:
Error: Could not authenticate request
The incoming JSON object does not contain a client_email field
Which makes no sense because it clearly does. The json itself is fine; I took the download directly from Google Cloud without modifying it. I've tried using keyFileName in my const gcs declaration but I get a ENAMETOOLONG error (plus the docs say thats for specifying the path to the JSON file anyway). It works locally when I use keyFileName and specify the path to the JSON file so I'm pretty sure the JSON itself not the issue.
Any ideas as to why I'm getting this error? Or is there a better way to handle JSON Keyfiles from Google on Heroku?

Environment variables on heroku, or anywhere, are strings.
If that google api constructor takes a JSON object you may need to do something like this to convert it back to JSON:
const gcs = require('#google-cloud/storage')({
projectId: 'my-project-ID',
credentials: JSON.parse(process.env.GCS_KEYFILE)
});

There is a helpful heroku buildpack for this
heroku buildpacks:add --index 1 https://github.com/buyersight/heroku-google-application-credentials-buildpack.git
--index 1 is because "The buildpack for the primary language of your app should be the last buildpack added," so if you have other buildpacks be careful of that.

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

JWT token Error preventing backend api connection Node/Express MongoDB

I am creating a blogging application using Node, Express and Next.js. I have successfully deployed the frontend portion of the application however I am running into an error when trying to connect to the backend server:
Error: secret should be set
Here is a link to my Github with all the code https://github.com/DragonKnightLeo/Blog-API
This is my first fullstack application so I am pretty junior at how all this works.
I just went through the code and tried to see if I can run your code in my local.
Also, saw the error that is coming when you run the code in your logs above. Seems like you do not have a .env file or JWT_SECRET mentioned in your env file.
Try adding env file and use dotenv node module to bootstrap it along the server run.

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

Deploying NodeJS project to Web App-config.js breaks due to gitignore (?)

I have a NodeJS server project running fine locally. Connection variables are stored in a config.js file. This file is included in my gitignore. The code is written in such a way as to primarily use system environmental variables, and if those do not appear, pull from config.js.
I have set up an Azure web app with continuous integration through our Git repository. The app is deploying fine, but obviously without config.js.
API calls to the app are returning a 404 error. Looking at the diagnostic logs, I'm seeing the request coming through properly, and errors like this:
Buffer="The resource you are looking for has been removed, had its name changed, or is temporarily unavailable."
I am not seeing any more detail than that, and not sure how I can get details. I assumed that what is causing the error is requiring the config file, which is obviously being ignored by git, and thus not making it to the web app. However, when I remove the import statements and the references to config.js, the error persists.
So, two questions:
1) How do I test this hypothesis in a more systematic way than just trial and error?
2) How do I avoid such errors in the future?
Alright, got a good answer via Stephen Grider's excellent Node+React course on Udemy. Basically, you need three files: a config file which imports one of two other files based on the environment (production or not). Those two files export objects with identical keys, but values which are either pulled from the environment (in production) or hard-wired (in dev.) The dev file is listed in gitignore. Then, in the rest of your app, just import or require config.js and use the keys from there.
It looks like this:
config.js:
if (process.env.node_ENV === 'production')
{
module.exports = require('./prod');
} else {
module.exports = require('./dev');
}
prod.js:
module.exports = {
secret: process.env.JWT_KEY,
conn: process.env.COSMOS_CONN,
sgKey: process.env.SENDGRID_API_KEY,
googleClientID: process.env.GOOGLE_CLIENT_ID,
googleClientSecret: process.env.GOOGLE_CLIENT_SECRET
};
dev.js:
module.exports = {
secret: 'xyz',
conn: '123',
sgKey: 'sdsddf',
googleClientID: 'ddcxcx.apps.googleusercontent.com',
googleClientSecret: 'dcvccfssfdas'
};

How to access local ENV variables in angular js

Is it possible to access ENV variables from angularjs?
I've got an angular 1.2.8 app being compiled via Brunch.io
It's hosted as a basic node app on heroku. (I do compiling locally; then push)
I've read a few things about token replacement during the compile stage for ENV variables.
However I'd much rather be able to push up the code to several different servers and have it resolve the correct local settings using just the ENV variables.
Thanks!
You cannot get ENV variables on browser. You can send request to a rest service on your server, and get env on backend, then response back to client
Quick example: (Express.js)
app.get("/rest/getenv", function(req, res) {
var env = process.env.ENV_VARIABLE;
res.json({result: env});
});
Edit:
You need to protect this rest url with token like strings. Or else, anyone can reach that url and get your environment variable value. This can be security concern
There are multiple ways of doing it and as mentioned above you could simply make and API call, however that creates an unnecessary delay in your app.
I would recommend to inject your env variables into your angular constant service on your server. That probably sounds a bit confusing, but it's simpler than it sounds.
I have written a blog post comparing different ways of injecting env varialbes into angular and explaining why you should take this approach: https://medium.com/#kudresov/a-better-way-to-inject-environmental-variables-in-angular-d3b2d01a3c5e
Also I have created sample Node Angular project to show how it works: https://github.com/kudresov/angular-config-vars

Resources