Node.js google cloud storage authentication using credentials token - node.js

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

Related

Docusign run code grant steps programmatically in node?

New to docusign and have been reading the docs, and I can't seem to get around these problems.
Per the docs it says that gets a code getAuthorizationUri() which can then be used by generateAccessToken() to get a token. After that, the token can be used to explore the API with other methods.
I'm stuck in two places: First I configured my get Authorization like this (typescript)
import { ApiClient } from 'docusign-esign'
const apiClient = new ApiClient()
const url: string = apiClient.getAuthorizationUri(
config.docusign.INTERGRATION_KEY,
['signature'],
'http://localhost/',
'code',
'ready'
)
console.log(url)
I get the url fine, but it is always starts with acccounts.docusign as opposed to the required account-d.docusign that's required because my app is in dev.
Secondly, I can't figure out how to transition that URL into a code programtically. Yes, I can copy and paste it from the browser URL (as in the instruction videos), but obviously my next step is to run something like:
async getToken(authCode){
const token = await apiClient.generateAccessToken(
INTERGRATION_KEY,
SECRET_KEY,
authCode
)
console.log(token)
return token
}
To change authentication from prod to demo, you need to make this call:
apiClient.setOAuthBasePath("account-d.docusign.com");
You may also want to change the URL for API calls if you use this later like this:
apiClient.setBasePath("https://demo.docusign.net/restapi");
I suggest using an OAuth npm package and not doing this yourself.
See how it's done in the quickstart (Pick Node.js and Auth Code Grant)

Unable to access BigCommerce WebDAV using node.js

Things actually work if I access the BigCommerce WebDAV via Cyberduck. However, I want to do this programmatically. Therefore, I wrote these code:
const { createClient } = require("webdav");
async function run() {
const client = createClient(
"https://mystore.mybigcommerce.com/dav",
{
username: "myemail#email.com",
password: "mypassword"
}
);
const contents = await client.getDirectoryContents("/");
}
run();
This is my code to get directory contents. I copied it from https://github.com/perry-mitchell/webdav-client#usage. I copied the email and password from the BigCommerce site.
The computer returns (node:32672) UnhandledPromiseRejectionWarning: Error: Request failed with status code 401 after I ran the script.
If I enter the URL in the web browser and enter the correct username and password, it returns this:
<d:error xmlns:d="DAV:" xmlns:s="http://sabredav.org/ns">
<s:exception>Sabre\DAV\Exception\NotImplemented</s:exception>
<s:message>
There was no plugin in the system that was willing to handle this GET method. Enable the Browser plugin to get a better result here.
</s:message>
</d:error>
Hope you guys can find out what is going on, thanks.
You'll need a client that supports Digest Auth, not just Basic. Looks like there was some conversation about adding digest support to the WebDAV client you're using. This PR might be a good starting place:
https://github.com/perry-mitchell/webdav-client/pull/96

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

Setting Heroku Config Var with contents from Google Cloud Service Account Keyfile

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.

Resources